blob: 88704a87dba2d02ad5b8d113ed4d0ccfc1017d70 [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::app::strategies::base::{create_app_strategy, AppStrategyPtr};
use crate::app::strategies::framebuffer::DisplayId;
use crate::drawing::DisplayRotation;
use crate::geometry::Size;
use crate::input::{DeviceId, UserInputMessage};
use crate::message::Message;
use crate::scene::facets::FacetId;
use crate::view::strategies::base::ViewStrategyParams;
use crate::view::{ViewAssistantPtr, ViewController, ViewKey};
use crate::IdGenerator2;
use anyhow::{bail, format_err, Context as _, Error};
use fidl_fuchsia_hardware_display::{CoordinatorEvent, VirtconMode};
use fidl_fuchsia_input_report as hid_input_report;
use fuchsia_async::{self as fasync, DurationExt, Timer};
use fuchsia_component::{self as component};
use fuchsia_trace::duration;
use fuchsia_zircon::{self as zx, DurationNum};
use futures::channel::mpsc::{unbounded, UnboundedSender};
use futures::future::{Either, Future};
use futures::StreamExt;
use once_cell::sync::OnceCell;
use serde::Deserialize;
use std::any::Any;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::fs;
use std::path::PathBuf;
use std::pin::Pin;
pub(crate) mod strategies;
/// Type alias for a non-sync future
pub type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
fn virtcon_mode_from_str(mode_str: &str) -> Result<Option<VirtconMode>, Error> {
match mode_str {
"forced" => Ok(Some(VirtconMode::Forced)),
"fallback" => Ok(Some(VirtconMode::Fallback)),
_ => Err(format_err!("Invalid VirtconMode {}", mode_str)),
}
}
fn deserialize_virtcon_mode<'de, D>(deserializer: D) -> Result<Option<VirtconMode>, D::Error>
where
D: serde::Deserializer<'de>,
{
let str = String::deserialize(deserializer)?;
virtcon_mode_from_str(&str).map_err(serde::de::Error::custom)
}
const fn keyboard_autorepeat_default() -> bool {
true
}
fn duration_from_millis(time_in_millis: u64) -> Result<std::time::Duration, Error> {
Ok(std::time::Duration::from_millis(time_in_millis))
}
fn deserialize_millis<'de, D>(deserializer: D) -> Result<std::time::Duration, D::Error>
where
D: serde::Deserializer<'de>,
{
let ms = u64::deserialize(deserializer)?;
duration_from_millis(ms).map_err(serde::de::Error::custom)
}
const fn keyboard_autorepeat_slow_interval_default() -> std::time::Duration {
const KEYBOARD_AUTOREPEAT_SLOW_INTERVAL: std::time::Duration =
std::time::Duration::from_millis(250);
KEYBOARD_AUTOREPEAT_SLOW_INTERVAL
}
const fn keyboard_autorepeat_fast_interval_default() -> std::time::Duration {
const KEYBOARD_AUTOREPEAT_FAST_INTERVAL: std::time::Duration =
std::time::Duration::from_millis(50);
KEYBOARD_AUTOREPEAT_FAST_INTERVAL
}
const fn display_resource_release_delay_default() -> std::time::Duration {
const DISPLAY_RESOURCE_RELEASE_DELAY_DEFAULT: std::time::Duration =
std::time::Duration::from_secs(5);
DISPLAY_RESOURCE_RELEASE_DELAY_DEFAULT
}
const fn startup_delay_default() -> std::time::Duration {
const STARTUP_DELAY_DEFAULT: std::time::Duration = std::time::Duration::from_secs(0);
STARTUP_DELAY_DEFAULT
}
/// Enum used by Config to control what sort of views
/// will be used.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ViewMode {
/// Choose automatically based on the environment
Auto,
/// Only create views hosted by Scenic.
Hosted,
/// Only create views directly running directly on the display coordinator.
Direct,
}
impl Default for ViewMode {
fn default() -> Self {
Self::Auto
}
}
/// Grab-bag of configuration options for Carnelian apps.
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default = "keyboard_autorepeat_default")]
/// Whether, when running without Scenic, this application should
/// receive keyboard repeat events.
pub keyboard_autorepeat: bool,
#[serde(
default = "keyboard_autorepeat_slow_interval_default",
deserialize_with = "deserialize_millis"
)]
/// The initial and maximum interval between keyboard repeat events, in
/// milliseconds, when running without Scenic.
pub keyboard_autorepeat_slow_interval: std::time::Duration,
#[serde(
default = "keyboard_autorepeat_fast_interval_default",
deserialize_with = "deserialize_millis"
)]
/// The minimum interval between keyboard repeat events, in
/// milliseconds, when running without Scenic.
pub keyboard_autorepeat_fast_interval: std::time::Duration,
#[serde(default)]
/// Whether to try to use hardware rendering (Spinel).
pub use_spinel: bool,
/// What mode to use when acting as a virtual console.
#[serde(default, deserialize_with = "deserialize_virtcon_mode")]
pub virtcon_mode: Option<VirtconMode>,
/// What sort of view system to use.
#[serde(default)]
pub view_mode: ViewMode,
#[serde(default)]
/// Application option to exercise transparent rotation.
pub display_rotation: DisplayRotation,
#[serde(default)]
/// Application option to select keymap. If named keymap is not found
/// the fallback is US QWERTY.
pub keymap_name: Option<String>,
#[serde(
default = "display_resource_release_delay_default",
deserialize_with = "deserialize_millis"
)]
/// How long should carnelian wait before releasing display resources when
/// it loses ownership of the display while running directly on the display. The default
/// value is five seconds, so that the resource will not be rapidly allocated
/// and deallocated when switching quickly between virtcon and the regular display.
pub display_resource_release_delay: std::time::Duration,
#[serde(default)]
/// In a bringup build the display coordinator might not support multiple
/// buffers so Carnelian might have to run with only a
/// single buffer. This configuration option is to allow testing rendering
/// with a single buffer even in build that supports multiple.
pub buffer_count: Option<usize>,
#[serde(default)]
/// Whether input events are needed.
pub input: bool,
#[serde(default)]
/// Whether output can be translucent and needs blending.
pub needs_blending: bool,
#[serde(default = "startup_delay_default", deserialize_with = "deserialize_millis")]
/// How long to wait before entering event loop.
pub startup_delay: std::time::Duration,
}
impl Config {
pub(crate) fn get() -> &'static Config {
// Some input tests access the config. Rather than requiring setup everywhere,
// default the config values for testing purposes.
CONFIG.get_or_init(|| Config::default())
}
}
impl Default for Config {
fn default() -> Self {
Self {
keyboard_autorepeat: keyboard_autorepeat_default(),
keyboard_autorepeat_slow_interval: keyboard_autorepeat_slow_interval_default(),
keyboard_autorepeat_fast_interval: keyboard_autorepeat_fast_interval_default(),
use_spinel: false,
virtcon_mode: None,
view_mode: ViewMode::default(),
display_rotation: DisplayRotation::Deg0,
keymap_name: None,
display_resource_release_delay: display_resource_release_delay_default(),
buffer_count: None,
input: true,
needs_blending: false,
startup_delay: Default::default(),
}
}
}
pub(crate) static CONFIG: OnceCell<Config> = OnceCell::new();
pub(crate) type InternalSender = UnboundedSender<MessageInternal>;
/// Target of a Any-based message
#[derive(Debug, Clone, Copy)]
pub enum MessageTarget {
/// target a facet in a view
Facet(ViewKey, FacetId),
/// target the view assistant in a view.
View(ViewKey),
/// targe the application assistant.
Application,
}
/// Options when creating a view.
pub type CreateViewOptions = Box<dyn Any>;
/// Context struct passed to the application assistant creator
// function.
#[derive(Clone)]
pub struct AppSender {
sender: InternalSender,
}
impl AppSender {
/// Send a message to a view controller.
pub fn queue_message(&self, target: MessageTarget, message: Message) {
self.sender
.unbounded_send(MessageInternal::TargetedMessage(target, message))
.expect("AppSender::queue_message - unbounded_send");
}
/// Request that a frame be rendered at the next appropriate time.
pub fn request_render(&self, target: ViewKey) {
self.sender
.unbounded_send(MessageInternal::RequestRender(target))
.expect("AppSender::request_render - unbounded_send");
}
/// Mode for applications running directly on the display coordinator.
/// Used by Virtcon and the screen saver but not generally useful.
pub fn set_virtcon_mode(&self, virtcon_mode: VirtconMode) {
self.sender
.unbounded_send(MessageInternal::SetVirtconMode(virtcon_mode))
.expect("AppSender::set_virtcon_mode - unbounded_send");
}
/// Request the creation of an additional view.
pub fn create_additional_view(&self, options: Option<CreateViewOptions>) -> ViewKey {
let view_key = IdGenerator2::<ViewKey>::next().expect("view_key");
self.sender
.unbounded_send(MessageInternal::CreateAdditionalView(view_key, options))
.expect("AppSender::create_additional_view - unbounded_send");
view_key
}
/// Close an additional view. It is a fatal error to attempt to close a view that
/// was not created with `create_additional_view()`.
pub fn close_additional_view(&self, view_key: ViewKey) {
self.sender
.unbounded_send(MessageInternal::CloseAdditionalView(view_key))
.expect("AppSender::close_additional_view - unbounded_send");
}
/// Create an futures mpsc sender and a task to poll the receiver and
/// forward the message to the app sender. This setup works around the problem
/// that dyn Any references cannot be `Send` at the cost of an extra trip through
/// the executor.
/// The 'static trait bounds here means that messages send across thread may not
/// contain any non-static references. The data in the messages must be owned, but
/// no not themselves need to be static.
pub fn create_cross_thread_sender<T: 'static + Send>(
&self,
target: MessageTarget,
) -> UnboundedSender<T> {
let (sender, mut receiver) = unbounded::<T>();
let app_sender = self.sender.clone();
let f = async move {
while let Some(message) = receiver.next().await {
app_sender
.unbounded_send(MessageInternal::TargetedMessage(target, Box::new(message)))
.expect("unbounded_send");
}
};
// This task can be detached as it will exit when the unbounded sender
// it provides is dropped.
fasync::Task::local(f).detach();
sender
}
/// Create an context for testing things that need an app context.
pub fn new_for_testing_purposes_only() -> AppSender {
let (internal_sender, _) = unbounded::<MessageInternal>();
AppSender { sender: internal_sender }
}
}
fn make_app_assistant_fut<T: AppAssistant + Default + 'static>(
_: &AppSender,
) -> LocalBoxFuture<'_, Result<AppAssistantPtr, Error>> {
let f = async move {
let assistant = Box::new(T::default());
Ok::<AppAssistantPtr, Error>(assistant)
};
Box::pin(f)
}
/// Convenience function to create an application assistant that implements Default.
pub fn make_app_assistant<T: AppAssistant + Default + 'static>() -> AssistantCreatorFunc {
Box::new(make_app_assistant_fut::<T>)
}
/// Parameter struction for view creation
pub struct ViewCreationParameters {
/// ViewKey for the new view.
pub view_key: ViewKey,
/// App sender that might be of use to the new view assistant.
pub app_sender: AppSender,
/// Display ID of the hosting display for views running directly
/// on the display coordinator.
pub display_id: Option<DisplayId>,
/// Options passed to `create_additional_view()`, if this view is being created
/// by that function and if the caller passed any.
pub options: Option<Box<dyn Any>>,
}
impl Debug for ViewCreationParameters {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ViewCreationParameters")
.field("view_key", &self.view_key)
.field("display_id", &self.display_id)
.field("options", &self.options)
.finish()
}
}
/// 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 {
/// 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, or once at startup
/// when running without Scenic.
fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
todo!("Must implement create_view_assistant_with_parameters or create_view_assistant");
}
/// Called when the Fuchsia view system requests that a view be created. Provides
/// parameters to view creation that include anything provided the the view creation
/// requestor and an AppSender.
fn create_view_assistant_with_parameters(
&mut self,
params: ViewCreationParameters,
) -> Result<ViewAssistantPtr, Error> {
self.create_view_assistant(params.view_key)
}
/// Return the list of names of services this app wants to provide
fn outgoing_services_names(&self) -> Vec<&'static str> {
Vec::new()
}
/// Handle a request to connect to a service provided by this app
fn handle_service_connection_request(
&mut self,
_service_name: &str,
_channel: fasync::Channel,
) -> Result<(), Error> {
return Err(format_err!("handle_service_connection_request not implemented"));
}
/// Filter Carnelian configuration at runtime, if needed.
fn filter_config(&mut self, _config: &mut Config) {}
/// This method is called when `App::queue_message` is called with `Application`
/// as target.
#[allow(unused_variables)]
fn handle_message(&mut self, message: Message) {}
}
/// Reference to an application assistant.
pub type AppAssistantPtr = Box<dyn AppAssistant>;
/// Struct that implements module-wide responsibilities, currently limited
/// to creating views on request.
pub struct App {
strategy: AppStrategyPtr,
view_controllers: BTreeMap<ViewKey, ViewController>,
assistant: AppAssistantPtr,
messages: Vec<(ViewKey, Message)>,
sender: InternalSender,
_inspect_server: Option<fasync::Task<()>>,
}
#[derive(Debug)]
pub(crate) enum MessageInternal {
ServiceConnection(zx::Channel, &'static str),
CreateView(ViewStrategyParams),
CreateAdditionalView(ViewKey, Option<CreateViewOptions>),
CloseAdditionalView(ViewKey),
MetricsChanged(ViewKey, Size),
SizeChanged(ViewKey, Size),
Focus(ViewKey, bool),
CloseViewsOnDisplay(DisplayId),
RequestRender(ViewKey),
Render(ViewKey),
ImageFreed(ViewKey, u64, u32),
TargetedMessage(MessageTarget, Message),
RegisterDevice(DeviceId, hid_input_report::DeviceDescriptor),
InputReport(DeviceId, hid_input_report::InputReport),
KeyboardAutoRepeat(DeviceId),
OwnershipChanged(bool),
DropDisplayResources,
FlatlandOnNextFrameBegin(ViewKey, fidl_fuchsia_ui_composition::OnNextFrameBeginValues),
FlatlandOnFramePresented(ViewKey, fidl_fuchsia_scenic_scheduling::FramePresentedInfo),
FlatlandOnError(ViewKey, fuchsia_scenic::flatland::FlatlandError),
NewDisplayCoordinator(PathBuf),
DisplayCoordinatorEvent(CoordinatorEvent),
SetVirtconMode(VirtconMode),
UserInputMessage(ViewKey, UserInputMessage),
}
/// Future that returns an application assistant.
pub type AssistantCreator<'a> = LocalBoxFuture<'a, Result<AppAssistantPtr, Error>>;
/// Function that creates an AssistantCreator future.
pub type AssistantCreatorFunc = Box<dyn FnOnce(&AppSender) -> AssistantCreator<'_>>;
impl App {
fn new(sender: InternalSender, strategy: AppStrategyPtr, assistant: AppAssistantPtr) -> App {
App {
strategy,
view_controllers: BTreeMap::new(),
assistant,
messages: Vec::new(),
sender,
_inspect_server: inspect_runtime::publish(
fuchsia_inspect::component::inspector(),
inspect_runtime::PublishOptions::default(),
),
}
}
fn load_and_filter_config(assistant: &mut AppAssistantPtr) -> Result<(), Error> {
let mut config = Self::load_config()?;
assistant.filter_config(&mut config);
CONFIG.set(config).expect("config set");
Ok(())
}
/// Starts an application based on Carnelian. The `assistant` parameter will
/// be used to create new views when asked to do so by the Fuchsia view system.
pub fn run(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
let mut executor = fasync::LocalExecutor::new();
let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
let f = async {
let app_sender = AppSender { sender: internal_sender.clone() };
let assistant_creator = assistant_creator_func(&app_sender);
let mut assistant = assistant_creator.await?;
Self::load_and_filter_config(&mut assistant)?;
let strat = create_app_strategy(&internal_sender).await?;
let mut app = App::new(internal_sender, strat, assistant);
app.app_init_common().await?;
let startup_delay = Config::get().startup_delay;
if !startup_delay.is_zero() {
duration!(c"gfx", c"App::run-startup-delay");
std::thread::sleep(Config::get().startup_delay);
}
while let Some(message) = internal_receiver.next().await {
app.handle_message(message).await?;
}
Ok::<(), Error>(())
};
executor.run_singlethreaded(f)?;
Ok(())
}
async fn handle_message(&mut self, message: MessageInternal) -> Result<(), Error> {
match message {
MessageInternal::ServiceConnection(channel, service_name) => {
let channel = fasync::Channel::from_channel(channel);
self.assistant
.handle_service_connection_request(service_name, channel)
.unwrap_or_else(|e| {
eprintln!("error running {} server: {:?}", service_name, e)
});
}
MessageInternal::CreateView(params) => {
self.create_view_with_params(params, None).await?
}
MessageInternal::CreateAdditionalView(view_key, options) => {
self.create_additional_view(view_key, options).await?
}
MessageInternal::CloseAdditionalView(view_key) => {
self.close_additional_view(view_key)?;
}
MessageInternal::MetricsChanged(view_id, metrics) => {
if let Ok(view) = self.get_view(view_id) {
view.handle_metrics_changed(metrics);
}
}
MessageInternal::SizeChanged(view_id, new_size) => {
if let Ok(view) = self.get_view(view_id) {
view.handle_size_changed(new_size);
}
}
MessageInternal::Focus(view_id, focused) => {
if let Ok(view) = self.get_view(view_id) {
view.focus(focused);
}
}
MessageInternal::RequestRender(view_id) => {
if let Ok(view) = self.get_view(view_id) {
view.request_render();
}
}
MessageInternal::Render(view_id) => {
if let Ok(view) = self.get_view(view_id) {
view.render().await;
}
}
MessageInternal::CloseViewsOnDisplay(display_id) => {
let view_keys = self.get_view_keys_for_display(display_id);
for view_key in view_keys.into_iter() {
self.close_view(view_key);
}
}
MessageInternal::ImageFreed(view_id, image_id, collection_id) => {
self.image_freed(view_id, image_id, collection_id)
}
MessageInternal::TargetedMessage(target, message) => match target {
MessageTarget::Facet(view_id, facet_id) => {
let view = self.get_view(view_id).context("TargetedMessage")?;
view.send_facet_message(facet_id, message).context("TargetedMessage")?;
}
MessageTarget::View(view_id) => {
let view = self.get_view(view_id).context("TargetedMessage")?;
view.send_message(message);
}
MessageTarget::Application => {
self.assistant.handle_message(message);
}
},
MessageInternal::RegisterDevice(device_id, device_descriptor) => {
self.strategy.handle_register_input_device(&device_id, &device_descriptor);
}
MessageInternal::InputReport(device_id, input_report) => {
let input_events = self.strategy.handle_input_report(&device_id, &input_report);
if let Some(focused_view_key) = self.get_focused_view_key() {
let view = self.get_view(focused_view_key).context("InputReport")?;
view.handle_input_events(input_events).context("InputReport")?;
} else {
eprintln!("dropping input report due to no focused view");
}
}
MessageInternal::KeyboardAutoRepeat(device_id) => {
let input_events = self.strategy.handle_keyboard_autorepeat(&device_id);
if let Some(focused_view_key) = self.get_focused_view_key() {
let view = self.get_view(focused_view_key).context("KeyboardAutoRepeat")?;
view.handle_input_events(input_events).context("KeyboardAutoRepeat")?;
} else {
eprintln!("dropping keyboard auto repeat due to no focused view");
}
}
MessageInternal::UserInputMessage(view_id, user_input_message) => {
let view = self.get_view(view_id).context("UserInputMessage")?;
view.handle_user_input_message(user_input_message)?;
}
MessageInternal::OwnershipChanged(owned) => {
self.ownership_changed(owned);
}
MessageInternal::DropDisplayResources => {
self.drop_display_resources();
}
MessageInternal::FlatlandOnNextFrameBegin(view_id, info) => {
let view = self.get_view(view_id).context("FlatlandOnNextFrameBegin")?;
view.handle_on_next_frame_begin(&info);
}
MessageInternal::FlatlandOnFramePresented(view_id, info) => {
let view = self.get_view(view_id).context("FlatlandOnFramePresented")?;
view.present_done(info);
}
MessageInternal::FlatlandOnError(view_id, error) => {
eprintln!("flatland error view: {}, error: {:#?}", view_id, error);
}
MessageInternal::NewDisplayCoordinator(display_path) => {
self.strategy.handle_new_display_coordinator(display_path).await;
}
MessageInternal::DisplayCoordinatorEvent(event) => match event {
CoordinatorEvent::OnVsync { display_id, .. } => {
if let Some(view_key) =
self.strategy.get_visible_view_key_for_display(display_id.into())
{
if let Ok(view) = self.get_view(view_key) {
view.handle_display_coordinator_event(event).await;
} else {
// We seem to get two vsyncs after the display is removed.
// Log it to help run down why that is.
eprintln!("vsync for display {:?} with no view", display_id);
}
}
}
_ => self.strategy.handle_display_coordinator_event(event).await,
},
MessageInternal::SetVirtconMode(virtcon_mode) => {
self.strategy.set_virtcon_mode(virtcon_mode);
}
}
Ok(())
}
async fn app_init_common(&mut self) -> Result<(), Error> {
self.assistant.setup().context("app setup")?;
self.start_services()?;
Ok(())
}
/// Tests an application based on Carnelian. The `assistant` parameter will
/// be used to create a single new view for testing. The test will run until the
/// first update call, or until a five second timeout. The Result returned is the
/// result of the test, an Ok(()) result means the test passed.
pub fn test(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
let mut executor = fasync::LocalExecutor::new();
let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
let f = async {
let app_sender = AppSender { sender: internal_sender.clone() };
let assistant_creator = assistant_creator_func(&app_sender);
let mut assistant = assistant_creator.await?;
Self::load_and_filter_config(&mut assistant)?;
let strat = create_app_strategy(&internal_sender).await?;
strat.create_view_for_testing(&internal_sender)?;
let mut app = App::new(internal_sender, strat, assistant);
let mut frame_count = 0;
app.app_init_common().await?;
loop {
let timeout = Timer::new(500_i64.millis().after_now());
let either = futures::future::select(timeout, internal_receiver.next());
let resolved = either.await;
match resolved {
Either::Left(_) => {
return Err(format_err!(
"Carnelian test got timeout before seeing 10 frames"
));
}
Either::Right((right_result, _)) => {
let message = right_result.expect("message");
match message {
MessageInternal::Render(_) => {
frame_count += 1;
}
_ => (),
}
app.handle_message(message).await.expect("handle_message failed");
if frame_count > 10 {
break;
}
}
}
}
Ok::<(), Error>(())
};
executor.run_singlethreaded(f)?;
Ok(())
}
fn get_focused_view_key(&self) -> Option<ViewKey> {
self.strategy.get_focused_view_key()
}
fn get_view(&mut self, view_key: ViewKey) -> Result<&mut ViewController, Error> {
if let Some(view) = self.view_controllers.get_mut(&view_key) {
Ok(view)
} else {
bail!("Could not find view controller for {}", view_key);
}
}
fn get_view_keys_for_display(&mut self, display_id: DisplayId) -> Vec<ViewKey> {
self.view_controllers
.iter()
.filter_map(|(view_key, view_controller)| {
view_controller.is_hosted_on_display(display_id).then(|| *view_key)
})
.collect()
}
fn close_view(&mut self, view_key: ViewKey) {
let view = self.view_controllers.remove(&view_key);
if let Some(mut view) = view {
view.close();
}
self.strategy.handle_view_closed(view_key);
}
fn ownership_changed(&mut self, owned: bool) {
for (_, view_controller) in &mut self.view_controllers {
view_controller.ownership_changed(owned);
}
}
fn drop_display_resources(&mut self) {
for (_, view_controller) in &mut self.view_controllers {
view_controller.drop_display_resources();
}
}
/// Send a message to a specific view controller. Messages not handled by the ViewController
/// will be forwarded to the `ViewControllerAssistant`.
pub fn queue_message(&mut self, target: ViewKey, msg: Message) {
self.messages.push((target, msg));
}
// Creates a view assistant for views that are using the render view mode feature, either
// in hosted or direct mode.
fn create_view_assistant(
&mut self,
view_key: ViewKey,
display_id: Option<DisplayId>,
options: Option<CreateViewOptions>,
) -> Result<ViewAssistantPtr, Error> {
Ok(self.assistant.create_view_assistant_with_parameters(ViewCreationParameters {
view_key,
display_id,
app_sender: AppSender { sender: self.sender.clone() },
options,
})?)
}
async fn create_view_with_params(
&mut self,
params: ViewStrategyParams,
options: Option<CreateViewOptions>,
) -> Result<(), Error> {
let view_key = if let Some(view_key) = params.view_key() {
view_key
} else {
IdGenerator2::<ViewKey>::next().expect("view_key")
};
let view_assistant = self
.create_view_assistant(view_key, params.display_id(), options)
.context("create_view_assistant")?;
let sender = &self.sender;
let view_strat = {
let view_strat = self
.strategy
.create_view_strategy(view_key, sender.clone(), params)
.await
.context("create_view_strategy")?;
self.strategy.post_setup(sender).await.context("post_setup")?;
view_strat
};
let view_controller =
ViewController::new_with_strategy(view_key, view_assistant, view_strat, sender.clone())
.await
.context("new_with_strategy")?;
self.view_controllers.insert(view_key, view_controller);
Ok(())
}
async fn create_additional_view(
&mut self,
view_key: ViewKey,
options: Option<CreateViewOptions>,
) -> Result<(), Error> {
let params = self.strategy.create_view_strategy_params_for_additional_view(view_key);
self.create_view_with_params(params, options).await
}
fn close_additional_view(&mut self, view_key: ViewKey) -> Result<(), Error> {
self.close_view(view_key);
Ok(())
}
fn start_services(self: &mut App) -> Result<(), Error> {
let mut fs = component::server::ServiceFs::new_local();
self.strategy.start_services(self.sender.clone(), &mut fs)?;
let outgoing_services_names = self.assistant.outgoing_services_names();
let mut public = fs.dir("svc");
for name in outgoing_services_names {
let sender = self.sender.clone();
public.add_service_at(name, move |channel| {
sender
.unbounded_send(MessageInternal::ServiceConnection(channel, name))
.expect("unbounded_send");
None
});
}
match fs.take_and_serve_directory_handle() {
Err(e) => eprintln!("Error publishing services: {:#}", e),
Ok(_) => (),
}
fasync::Task::local(fs.collect()).detach();
Ok(())
}
pub(crate) fn image_freed(&mut self, view_id: ViewKey, image_id: u64, collection_id: u32) {
if let Ok(view) = self.get_view(view_id) {
view.image_freed(image_id, collection_id);
}
}
fn load_config() -> Result<Config, Error> {
const CARNELIAN_CONFIG_PATH: &str = "/pkg/data/config/carnelian.toml";
let config_path = PathBuf::from(CARNELIAN_CONFIG_PATH);
if !config_path.exists() {
return Ok(Config::default());
}
let config_contents = fs::read_to_string(config_path)?;
let config = toml::from_str(&config_contents)?;
Ok(config)
}
}