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 hid_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)]
121 pub use_spinel: bool,
123 #[serde(default)]
126 pub client_priority: Option<ClientPriorityValue>,
127 #[serde(default)]
129 pub view_mode: ViewMode,
130 #[serde(default)]
131 pub display_rotation: DisplayRotation,
133 #[serde(default)]
134 pub keymap_name: Option<String>,
137 #[serde(
138 default = "display_resource_release_delay_default",
139 deserialize_with = "deserialize_millis"
140 )]
141 pub display_resource_release_delay: std::time::Duration,
146 #[serde(default)]
147 pub buffer_count: Option<usize>,
152 #[serde(default)]
153 pub input: bool,
155 #[serde(default)]
156 pub needs_blending: bool,
158 #[serde(default = "startup_delay_default", deserialize_with = "deserialize_millis")]
159 pub startup_delay: std::time::Duration,
161}
162
163impl Config {
164 pub(crate) fn get() -> &'static Config {
165 CONFIG.get_or_init(|| Config::default())
168 }
169}
170
171impl Default for Config {
172 fn default() -> Self {
173 Self {
174 keyboard_autorepeat: keyboard_autorepeat_default(),
175 keyboard_autorepeat_slow_interval: keyboard_autorepeat_slow_interval_default(),
176 keyboard_autorepeat_fast_interval: keyboard_autorepeat_fast_interval_default(),
177 use_spinel: false,
178 client_priority: None,
179 view_mode: ViewMode::default(),
180 display_rotation: DisplayRotation::Deg0,
181 keymap_name: None,
182 display_resource_release_delay: display_resource_release_delay_default(),
183 buffer_count: None,
184 input: true,
185 needs_blending: false,
186 startup_delay: Default::default(),
187 }
188 }
189}
190
191pub(crate) static CONFIG: OnceLock<Config> = OnceLock::new();
192
193pub(crate) type InternalSender = UnboundedSender<MessageInternal>;
194
195#[derive(Debug, Clone, Copy)]
197pub enum MessageTarget {
198 Facet(ViewKey, FacetId),
200 View(ViewKey),
202 Application,
204}
205
206pub type CreateViewOptions = Box<dyn Any>;
208
209#[derive(Clone)]
212pub struct AppSender {
213 sender: InternalSender,
214}
215
216impl AppSender {
217 pub fn queue_message(&self, target: MessageTarget, message: Message) {
219 self.sender
220 .unbounded_send(MessageInternal::TargetedMessage(target, message))
221 .expect("AppSender::queue_message - unbounded_send");
222 }
223
224 pub fn request_render(&self, target: ViewKey) {
226 self.sender
227 .unbounded_send(MessageInternal::RequestRender(target))
228 .expect("AppSender::request_render - unbounded_send");
229 }
230
231 pub fn create_additional_view(&self, options: Option<CreateViewOptions>) -> ViewKey {
233 let view_key = IdGenerator2::<ViewKey>::next().expect("view_key");
234 self.sender
235 .unbounded_send(MessageInternal::CreateAdditionalView(view_key, options))
236 .expect("AppSender::create_additional_view - unbounded_send");
237 view_key
238 }
239
240 pub fn close_additional_view(&self, view_key: ViewKey) {
243 self.sender
244 .unbounded_send(MessageInternal::CloseAdditionalView(view_key))
245 .expect("AppSender::close_additional_view - unbounded_send");
246 }
247
248 pub fn create_cross_thread_sender<T: 'static + Send>(
256 &self,
257 target: MessageTarget,
258 ) -> UnboundedSender<T> {
259 let (sender, mut receiver) = unbounded::<T>();
260 let app_sender = self.sender.clone();
261 let f = async move {
262 while let Some(message) = receiver.next().await {
263 app_sender
264 .unbounded_send(MessageInternal::TargetedMessage(target, Box::new(message)))
265 .expect("unbounded_send");
266 }
267 };
268 fasync::Task::local(f).detach();
271 sender
272 }
273
274 pub fn new_for_testing_purposes_only() -> AppSender {
276 let (internal_sender, _) = unbounded::<MessageInternal>();
277 AppSender { sender: internal_sender }
278 }
279}
280
281fn make_app_assistant_fut<T: AppAssistant + Default + 'static>(
282 _: &AppSender,
283) -> LocalBoxFuture<'_, Result<AppAssistantPtr, Error>> {
284 let f = async move {
285 let assistant = Box::new(T::default());
286 Ok::<AppAssistantPtr, Error>(assistant)
287 };
288 Box::pin(f)
289}
290
291pub fn make_app_assistant<T: AppAssistant + Default + 'static>() -> AssistantCreatorFunc {
293 Box::new(make_app_assistant_fut::<T>)
294}
295
296pub struct ViewCreationParameters {
298 pub view_key: ViewKey,
300 pub app_sender: AppSender,
302 pub display_id: Option<DisplayId>,
305 pub options: Option<Box<dyn Any>>,
308}
309
310impl Debug for ViewCreationParameters {
311 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
312 f.debug_struct("ViewCreationParameters")
313 .field("view_key", &self.view_key)
314 .field("display_id", &self.display_id)
315 .field("options", &self.options)
316 .finish()
317 }
318}
319
320pub trait AppAssistant {
324 fn setup(&mut self) -> Result<(), Error>;
329
330 fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
333 todo!("Must implement create_view_assistant_with_parameters or create_view_assistant");
334 }
335
336 fn create_view_assistant_with_parameters(
340 &mut self,
341 params: ViewCreationParameters,
342 ) -> Result<ViewAssistantPtr, Error> {
343 self.create_view_assistant(params.view_key)
344 }
345
346 fn outgoing_services_names(&self) -> Vec<&'static str> {
348 Vec::new()
349 }
350
351 fn handle_service_connection_request(
353 &mut self,
354 _service_name: &str,
355 _channel: fasync::Channel,
356 ) -> Result<(), Error> {
357 return Err(format_err!("handle_service_connection_request not implemented"));
358 }
359
360 fn filter_config(&mut self, _config: &mut Config) {}
362
363 #[allow(unused_variables)]
366 fn handle_message(&mut self, message: Message) {}
367}
368
369pub type AppAssistantPtr = Box<dyn AppAssistant>;
371
372pub struct App {
375 strategy: AppStrategyPtr,
376 view_controllers: BTreeMap<ViewKey, ViewController>,
377 assistant: AppAssistantPtr,
378 messages: Vec<(ViewKey, Message)>,
379 sender: InternalSender,
380 _inspect_server: Option<inspect_runtime::PublishedInspectController>,
381}
382
383#[derive(Debug)]
384pub(crate) enum MessageInternal {
385 ServiceConnection(zx::Channel, &'static str),
386 CreateView(ViewStrategyParams),
387 CreateAdditionalView(ViewKey, Option<CreateViewOptions>),
388 CloseAdditionalView(ViewKey),
389 MetricsChanged(ViewKey, Size),
390 SizeChanged(ViewKey, Size),
391 Focus(ViewKey, bool),
392 CloseViewsOnDisplay(DisplayId),
393 RequestRender(ViewKey),
394 Render(ViewKey),
395 ImageFreed(ViewKey, u64, u32),
396 TargetedMessage(MessageTarget, Message),
397 RegisterDevice(DeviceId, hid_input_report::DeviceDescriptor),
398 InputReport(DeviceId, hid_input_report::InputReport),
399 KeyboardAutoRepeat(DeviceId),
400 OwnershipChanged(bool),
401 DropDisplayResources,
402 FlatlandOnNextFrameBegin(ViewKey, fidl_fuchsia_ui_composition::OnNextFrameBeginValues),
403 FlatlandOnFramePresented(ViewKey, fidl_fuchsia_scenic_scheduling::FramePresentedInfo),
404 FlatlandOnError(ViewKey, fuchsia_scenic::flatland::FlatlandError),
405 NewDisplayCoordinator(ProviderProxy),
406 DisplayCoordinatorListenerRequest(CoordinatorListenerRequest),
407
408 UserInputMessage(ViewKey, UserInputMessage),
409}
410
411pub type AssistantCreator<'a> = LocalBoxFuture<'a, Result<AppAssistantPtr, Error>>;
413pub type AssistantCreatorFunc = Box<dyn FnOnce(&AppSender) -> AssistantCreator<'_>>;
415
416impl App {
417 fn new(sender: InternalSender, strategy: AppStrategyPtr, assistant: AppAssistantPtr) -> App {
418 App {
419 strategy,
420 view_controllers: BTreeMap::new(),
421 assistant,
422 messages: Vec::new(),
423 sender,
424 _inspect_server: inspect_runtime::publish(
425 fuchsia_inspect::component::inspector(),
426 inspect_runtime::PublishOptions::default(),
427 ),
428 }
429 }
430
431 fn load_and_filter_config(assistant: &mut AppAssistantPtr) -> Result<(), Error> {
432 let mut config = Self::load_config()?;
433 assistant.filter_config(&mut config);
434 CONFIG.set(config).expect("config set");
435 Ok(())
436 }
437
438 pub fn run(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
441 let mut executor = fasync::LocalExecutor::default();
442 let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
443 let f = async {
444 let app_sender = AppSender { sender: internal_sender.clone() };
445 let assistant_creator = assistant_creator_func(&app_sender);
446 let mut assistant = assistant_creator.await?;
447 Self::load_and_filter_config(&mut assistant)?;
448 let strat = create_app_strategy(&internal_sender).await?;
449 let mut app = App::new(internal_sender, strat, assistant);
450 app.app_init_common().await?;
451 let startup_delay = Config::get().startup_delay;
452 if !startup_delay.is_zero() {
453 duration!("gfx", "App::run-startup-delay");
454 std::thread::sleep(Config::get().startup_delay);
455 }
456 while let Some(message) = internal_receiver.next().await {
457 app.handle_message(message).await?;
458 }
459 Ok::<(), Error>(())
460 };
461 executor.run_singlethreaded(f)?;
462 Ok(())
463 }
464
465 async fn handle_message(&mut self, message: MessageInternal) -> Result<(), Error> {
466 match message {
467 MessageInternal::ServiceConnection(channel, service_name) => {
468 let channel = fasync::Channel::from_channel(channel);
469 self.assistant
470 .handle_service_connection_request(service_name, channel)
471 .unwrap_or_else(|e| {
472 eprintln!("error running {} server: {:?}", service_name, e)
473 });
474 }
475 MessageInternal::CreateView(params) => {
476 self.create_view_with_params(params, None).await?
477 }
478 MessageInternal::CreateAdditionalView(view_key, options) => {
479 self.create_additional_view(view_key, options).await?
480 }
481 MessageInternal::CloseAdditionalView(view_key) => {
482 self.close_additional_view(view_key)?;
483 }
484 MessageInternal::MetricsChanged(view_id, metrics) => {
485 if let Ok(view) = self.get_view(view_id) {
486 view.handle_metrics_changed(metrics);
487 }
488 }
489 MessageInternal::SizeChanged(view_id, new_size) => {
490 if let Ok(view) = self.get_view(view_id) {
491 view.handle_size_changed(new_size);
492 }
493 }
494 MessageInternal::Focus(view_id, focused) => {
495 if let Ok(view) = self.get_view(view_id) {
496 view.focus(focused);
497 }
498 }
499 MessageInternal::RequestRender(view_id) => {
500 if let Ok(view) = self.get_view(view_id) {
501 view.request_render();
502 }
503 }
504 MessageInternal::Render(view_id) => {
505 if let Ok(view) = self.get_view(view_id) {
506 view.render().await;
507 }
508 }
509 MessageInternal::CloseViewsOnDisplay(display_id) => {
510 let view_keys = self.get_view_keys_for_display(display_id);
511 for view_key in view_keys.into_iter() {
512 self.close_view(view_key);
513 }
514 }
515 MessageInternal::ImageFreed(view_id, image_id, collection_id) => {
516 self.image_freed(view_id, image_id, collection_id)
517 }
518 MessageInternal::TargetedMessage(target, message) => match target {
519 MessageTarget::Facet(view_id, facet_id) => {
520 let view = self.get_view(view_id).context("TargetedMessage")?;
521 view.send_facet_message(facet_id, message).context("TargetedMessage")?;
522 }
523 MessageTarget::View(view_id) => {
524 let view = self.get_view(view_id).context("TargetedMessage")?;
525 view.send_message(message);
526 }
527 MessageTarget::Application => {
528 self.assistant.handle_message(message);
529 }
530 },
531 MessageInternal::RegisterDevice(device_id, device_descriptor) => {
532 self.strategy.handle_register_input_device(&device_id, &device_descriptor);
533 }
534 MessageInternal::InputReport(device_id, input_report) => {
535 let input_events = self.strategy.handle_input_report(&device_id, &input_report);
536 if let Some(focused_view_key) = self.get_focused_view_key() {
537 let view = self.get_view(focused_view_key).context("InputReport")?;
538 view.handle_input_events(input_events).context("InputReport")?;
539 } else {
540 eprintln!("dropping input report due to no focused view");
541 }
542 }
543 MessageInternal::KeyboardAutoRepeat(device_id) => {
544 let input_events = self.strategy.handle_keyboard_autorepeat(&device_id);
545 if let Some(focused_view_key) = self.get_focused_view_key() {
546 let view = self.get_view(focused_view_key).context("KeyboardAutoRepeat")?;
547 view.handle_input_events(input_events).context("KeyboardAutoRepeat")?;
548 } else {
549 eprintln!("dropping keyboard auto repeat due to no focused view");
550 }
551 }
552 MessageInternal::UserInputMessage(view_id, user_input_message) => {
553 let view = self.get_view(view_id).context("UserInputMessage")?;
554 view.handle_user_input_message(user_input_message)?;
555 }
556 MessageInternal::OwnershipChanged(owned) => {
557 self.ownership_changed(owned);
558 }
559 MessageInternal::DropDisplayResources => {
560 self.drop_display_resources();
561 }
562 MessageInternal::FlatlandOnNextFrameBegin(view_id, info) => {
563 let view = self.get_view(view_id).context("FlatlandOnNextFrameBegin")?;
564 view.handle_on_next_frame_begin(&info);
565 }
566 MessageInternal::FlatlandOnFramePresented(view_id, info) => {
567 let view = self.get_view(view_id).context("FlatlandOnFramePresented")?;
568 view.present_done(info);
569 }
570 MessageInternal::FlatlandOnError(view_id, error) => {
571 eprintln!("flatland error view: {}, error: {:#?}", view_id, error);
572 }
573 MessageInternal::NewDisplayCoordinator(provider) => {
574 self.strategy.handle_new_display_coordinator(provider).await;
575 }
576 MessageInternal::DisplayCoordinatorListenerRequest(request) => match request {
577 CoordinatorListenerRequest::OnVsync { display_id, .. } => {
578 if let Some(view_key) =
579 self.strategy.get_visible_view_key_for_display(display_id.into())
580 {
581 if let Ok(view) = self.get_view(view_key) {
582 view.handle_display_coordinator_listener_request(request).await;
583 } else {
584 eprintln!("vsync for display {:?} with no view", display_id);
587 }
588 }
589 }
590
591 _ => self.strategy.handle_display_coordinator_event(request).await,
592 },
593 }
594 Ok(())
595 }
596
597 async fn app_init_common(&mut self) -> Result<(), Error> {
598 self.assistant.setup().context("app setup")?;
599 self.start_services()?;
600 Ok(())
601 }
602
603 pub fn test(assistant_creator_func: AssistantCreatorFunc) -> Result<(), Error> {
608 let mut executor = fasync::LocalExecutor::default();
609 let (internal_sender, mut internal_receiver) = unbounded::<MessageInternal>();
610 let f = async {
611 let app_sender = AppSender { sender: internal_sender.clone() };
612 let assistant_creator = assistant_creator_func(&app_sender);
613 let mut assistant = assistant_creator.await?;
614 Self::load_and_filter_config(&mut assistant)?;
615 let strat = create_app_strategy(&internal_sender).await?;
616 strat.create_view_for_testing(&internal_sender)?;
617 let mut app = App::new(internal_sender, strat, assistant);
618 let mut frame_count = 0;
619 app.app_init_common().await?;
620 loop {
621 let timeout =
622 pin!(Timer::new(zx::MonotonicDuration::from_millis(500_i64).after_now()));
623 let either = futures::future::select(timeout, internal_receiver.next());
624 let resolved = either.await;
625 match resolved {
626 Either::Left(_) => {
627 return Err(format_err!(
628 "Carnelian test got timeout before seeing 10 frames"
629 ));
630 }
631 Either::Right((right_result, _)) => {
632 let message = right_result.expect("message");
633 match message {
634 MessageInternal::Render(_) => {
635 frame_count += 1;
636 }
637 _ => (),
638 }
639 app.handle_message(message).await.expect("handle_message failed");
640 if frame_count > 10 {
641 break;
642 }
643 }
644 }
645 }
646 Ok::<(), Error>(())
647 };
648
649 executor.run_singlethreaded(f)?;
650
651 Ok(())
652 }
653
654 fn get_focused_view_key(&self) -> Option<ViewKey> {
655 self.strategy.get_focused_view_key()
656 }
657
658 fn get_view(&mut self, view_key: ViewKey) -> Result<&mut ViewController, Error> {
659 if let Some(view) = self.view_controllers.get_mut(&view_key) {
660 Ok(view)
661 } else {
662 bail!("Could not find view controller for {}", view_key);
663 }
664 }
665
666 fn get_view_keys_for_display(&mut self, display_id: DisplayId) -> Vec<ViewKey> {
667 self.view_controllers
668 .iter()
669 .filter_map(|(view_key, view_controller)| {
670 view_controller.is_hosted_on_display(display_id).then_some(*view_key)
671 })
672 .collect()
673 }
674
675 fn close_view(&mut self, view_key: ViewKey) {
676 let view = self.view_controllers.remove(&view_key);
677 if let Some(mut view) = view {
678 view.close();
679 }
680 self.strategy.handle_view_closed(view_key);
681 }
682
683 fn ownership_changed(&mut self, owned: bool) {
684 for (_, view_controller) in &mut self.view_controllers {
685 view_controller.ownership_changed(owned);
686 }
687 }
688
689 fn drop_display_resources(&mut self) {
690 for (_, view_controller) in &mut self.view_controllers {
691 view_controller.drop_display_resources();
692 }
693 }
694
695 pub fn queue_message(&mut self, target: ViewKey, msg: Message) {
698 self.messages.push((target, msg));
699 }
700
701 fn create_view_assistant(
704 &mut self,
705 view_key: ViewKey,
706 display_id: Option<DisplayId>,
707 options: Option<CreateViewOptions>,
708 ) -> Result<ViewAssistantPtr, Error> {
709 Ok(self.assistant.create_view_assistant_with_parameters(ViewCreationParameters {
710 view_key,
711 display_id,
712 app_sender: AppSender { sender: self.sender.clone() },
713 options,
714 })?)
715 }
716
717 async fn create_view_with_params(
718 &mut self,
719 params: ViewStrategyParams,
720 options: Option<CreateViewOptions>,
721 ) -> Result<(), Error> {
722 let view_key = if let Some(view_key) = params.view_key() {
723 view_key
724 } else {
725 IdGenerator2::<ViewKey>::next().expect("view_key")
726 };
727 let view_assistant = self
728 .create_view_assistant(view_key, params.display_id(), options)
729 .context("create_view_assistant")?;
730 let sender = &self.sender;
731 let view_strat = {
732 let view_strat = self
733 .strategy
734 .create_view_strategy(view_key, sender.clone(), params)
735 .await
736 .context("create_view_strategy")?;
737 self.strategy.post_setup(sender).await.context("post_setup")?;
738 view_strat
739 };
740 let view_controller =
741 ViewController::new_with_strategy(view_key, view_assistant, view_strat, sender.clone())
742 .await
743 .context("new_with_strategy")?;
744
745 self.view_controllers.insert(view_key, view_controller);
746 Ok(())
747 }
748
749 async fn create_additional_view(
750 &mut self,
751 view_key: ViewKey,
752 options: Option<CreateViewOptions>,
753 ) -> Result<(), Error> {
754 let params = self.strategy.create_view_strategy_params_for_additional_view(view_key);
755 self.create_view_with_params(params, options).await
756 }
757
758 fn close_additional_view(&mut self, view_key: ViewKey) -> Result<(), Error> {
759 self.close_view(view_key);
760 Ok(())
761 }
762
763 fn start_services(self: &mut App) -> Result<(), Error> {
764 let mut fs = component::server::ServiceFs::new_local();
765
766 self.strategy.start_services(self.sender.clone(), &mut fs)?;
767
768 let outgoing_services_names = self.assistant.outgoing_services_names();
769 let mut public = fs.dir("svc");
770 for name in outgoing_services_names {
771 let sender = self.sender.clone();
772 public.add_service_at(name, move |channel| {
773 sender
774 .unbounded_send(MessageInternal::ServiceConnection(channel, name))
775 .expect("unbounded_send");
776 None
777 });
778 }
779
780 match fs.take_and_serve_directory_handle() {
781 Err(e) => eprintln!("Error publishing services: {:#}", e),
782 Ok(_) => (),
783 }
784
785 fasync::Task::local(fs.collect()).detach();
786 Ok(())
787 }
788
789 pub(crate) fn image_freed(&mut self, view_id: ViewKey, image_id: u64, collection_id: u32) {
790 if let Ok(view) = self.get_view(view_id) {
791 view.image_freed(image_id, collection_id);
792 }
793 }
794
795 fn load_config() -> Result<Config, Error> {
796 const CARNELIAN_CONFIG_PATH: &str = "/pkg/data/config/carnelian.toml";
797 let config_path = PathBuf::from(CARNELIAN_CONFIG_PATH);
798 if !config_path.exists() {
799 return Ok(Config::default());
800 }
801 let config_contents = fs::read_to_string(config_path)?;
802 let config = toml::from_str(&config_contents)?;
803 Ok(config)
804 }
805}