1use 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
38pub 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#[derive(Debug, Deserialize)]
83#[serde(rename_all = "lowercase")]
84pub enum ViewMode {
85 Auto,
87 Hosted,
89 Direct,
91}
92
93impl Default for ViewMode {
94 fn default() -> Self {
95 Self::Auto
96 }
97}
98
99#[derive(Debug, Deserialize)]
101pub struct Config {
102 #[serde(default = "keyboard_autorepeat_default")]
103 pub keyboard_autorepeat: bool,
106 #[serde(
107 default = "keyboard_autorepeat_slow_interval_default",
108 deserialize_with = "deserialize_millis"
109 )]
110 pub keyboard_autorepeat_slow_interval: std::time::Duration,
113 #[serde(
114 default = "keyboard_autorepeat_fast_interval_default",
115 deserialize_with = "deserialize_millis"
116 )]
117 pub keyboard_autorepeat_fast_interval: std::time::Duration,
120 #[serde(default)]
123 pub client_priority: Option<ClientPriorityValue>,
124 #[serde(default)]
126 pub view_mode: ViewMode,
127 #[serde(default)]
128 pub display_rotation: DisplayRotation,
130 #[serde(default)]
131 pub keymap_name: Option<String>,
134 #[serde(
135 default = "display_resource_release_delay_default",
136 deserialize_with = "deserialize_millis"
137 )]
138 pub display_resource_release_delay: std::time::Duration,
143 #[serde(default)]
144 pub buffer_count: Option<usize>,
149 #[serde(default)]
150 pub input: bool,
152 #[serde(default)]
153 pub needs_blending: bool,
155 #[serde(default = "startup_delay_default", deserialize_with = "deserialize_millis")]
156 pub startup_delay: std::time::Duration,
158}
159
160impl Config {
161 pub(crate) fn get() -> &'static Config {
162 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#[derive(Debug, Clone, Copy)]
193pub enum MessageTarget {
194 Facet(ViewKey, FacetId),
196 View(ViewKey),
198 Application,
200}
201
202pub type CreateViewOptions = Box<dyn Any>;
204
205#[derive(Clone)]
208pub struct AppSender {
209 sender: InternalSender,
210}
211
212impl AppSender {
213 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 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 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 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 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 fasync::Task::local(f).detach();
267 sender
268 }
269
270 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
287pub fn make_app_assistant<T: AppAssistant + Default + 'static>() -> AssistantCreatorFunc {
289 Box::new(make_app_assistant_fut::<T>)
290}
291
292pub struct ViewCreationParameters {
294 pub view_key: ViewKey,
296 pub app_sender: AppSender,
298 pub display_id: Option<DisplayId>,
301 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
316pub trait AppAssistant {
320 fn setup(&mut self) -> Result<(), Error>;
325
326 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 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 fn outgoing_services_names(&self) -> Vec<&'static str> {
344 Vec::new()
345 }
346
347 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 fn filter_config(&mut self, _config: &mut Config) {}
358
359 #[allow(unused_variables)]
362 fn handle_message(&mut self, message: Message) {}
363}
364
365pub type AppAssistantPtr = Box<dyn AppAssistant>;
367
368pub 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
407pub type AssistantCreator<'a> = LocalBoxFuture<'a, Result<AppAssistantPtr, Error>>;
409pub 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 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 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 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 pub fn queue_message(&mut self, target: ViewKey, msg: Message) {
694 self.messages.push((target, msg));
695 }
696
697 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}