gooey/presentation/fmod/
remote.rs

1//! Remote FMOD backend
2
3use std::sync;
4use unbounded_spsc;
5
6use crate::interface::{self, view, View};
7use crate::tree::{NodeId, Tree};
8use super::{Audio, Presentation};
9
10/// Used to transfer view display events to the FMOD backend
11#[expect(clippy::type_complexity)]
12static DISPLAY_RECEIVER : sync::LazyLock <
13  sync::Mutex <Option <unbounded_spsc::Receiver <Vec <(NodeId, view::Display)>>>>
14> = sync::LazyLock::new (|| sync::Mutex::new (None));
15
16/// Remote FMOD handle
17#[derive(Debug)]
18pub struct Remote {
19  display : unbounded_spsc::Sender <Vec <(NodeId, view::Display)>>,
20}
21
22/// Audition backend that can receive and handle display updates
23pub struct Fmod {
24  pub inner : super::Fmod,
25  updates   : unbounded_spsc::Receiver <Vec <(NodeId, view::Display)>>,
26  /// Counts number of batches of display events have been processed
27  display_counter : u64,
28  /// Counts number of frames that have been rendered
29  frame           : u64
30}
31
32impl Default for Remote {
33  fn default() -> Self {
34    let display = {
35      let (sender, receiver) =
36        unbounded_spsc::channel::<Vec <(NodeId, view::Display)>>();
37      {
38        let mut display_receiver_lock = DISPLAY_RECEIVER.lock().unwrap();
39        debug_assert!(display_receiver_lock.is_none());
40        *display_receiver_lock = Some (receiver);
41      }
42      sender
43    };
44    Remote {
45      display
46    }
47  }
48}
49
50impl Audio        for Remote { }
51impl Presentation for Remote {
52  fn with_root (root : View, root_id : NodeId) -> Self {
53    let remote = Self::default();
54    log::debug!("fmod remote with root: {remote:?}");
55    remote.display.send (vec![(
56      root_id.clone(),
57      view::Display::Create (root, root_id, interface::CreateOrder::Append)
58    )]).unwrap();
59    remote
60  }
61
62  fn get_input (&mut self, _ : &mut Vec <view::Input>) { /* no-op */ }
63
64  fn display_view <V : AsRef <View>> (&mut self,
65    _view_tree      : &Tree <V>,
66    display_values : std::vec::Drain <(NodeId, view::Display)>
67  ) {
68    self.display.send (display_values.collect()).unwrap();
69  }
70}
71
72impl Fmod {
73  pub fn init() -> Self {
74    let inner = super::Fmod::default();
75    let updates = loop {
76      let maybe_receiver = (*DISPLAY_RECEIVER).lock().unwrap().take();
77      if let Some (receiver) = maybe_receiver {
78        break receiver
79      }
80      std::thread::sleep (std::time::Duration::from_millis (100));
81    };
82    Fmod {
83      inner,
84      updates,
85      display_counter: 0,
86      frame:           0
87    }
88  }
89
90  #[inline]
91  pub const fn display_counter (&self) -> u64 {
92    self.display_counter
93  }
94
95  #[inline]
96  pub const fn reset_display_counter (&mut self) {
97    self.display_counter = 0;
98  }
99
100  pub fn process_display_events (&mut self) {
101    // process updates
102    while let Ok (updates) = self.updates.try_recv() {
103      for (node_id, display) in updates {
104        self.inner.display_value (node_id, display);
105      }
106      self.display_counter += 1;
107    }
108  }
109
110  pub fn update_audition (&mut self) {
111    self.inner.audition.update();
112    self.frame += 1;
113  }
114
115  /// Pump display events and update
116  pub fn display (&mut self) {
117    log::trace!("fmod remote display...");
118    self.process_display_events();
119    self.update_audition();
120    log::trace!("...fmod remote display");
121  }
122}