libmapper_rs/
graph.rs

1//! Graph and Map types for working with libmapper's distributed graph.
2//! 
3//! The [Map] type is used to create a connection between two [Signal] instances.
4//! 
5//! The [Graph] type can be shared between devices to improve performance and memory usage.
6use std::{ffi::c_int, ptr, time::Duration};
7
8use crate::{bindings::*, device::Device, object::MapperObject, signal::Signal, util::read_list};
9
10/// A graph is a lightweight connection to libmapper's distributed graph.
11/// You can use a graph to create maps and query the state of the graph.
12pub struct Graph {
13    pub(crate) handle: mpr_graph,
14    owned: bool
15}
16
17unsafe impl Send for Graph {}
18unsafe impl Sync for Graph {}
19
20impl Graph {
21  pub fn create() -> Graph {
22    Graph {
23      owned: true,
24      handle: unsafe { mpr_graph_new(0) }
25    }
26  }
27}
28
29impl Drop for Graph {
30    fn drop(&mut self) {
31        if self.owned {
32            unsafe {
33                mpr_graph_free(self.handle);
34            }
35        }
36    }
37}
38
39impl Graph {
40  /// Poll the graph without blocking
41  pub fn poll(&self) {
42    unsafe {
43      mpr_graph_poll(self.handle, 0);
44    }
45  }
46  /// Poll the graph and block for the specified amount of time
47  /// 
48  /// Use this instead of sleeping in a loop
49  pub fn poll_and_block(&self, time: Duration) {
50    unsafe {
51      mpr_graph_poll(self.handle, time.as_millis() as c_int);
52    }
53  }
54
55  /// Tells the graph to subscribe to receive updates for the specified device and types.
56  /// If the device is `None`, the graph will automatically subscribe to all devices as they become visible.  
57  /// 
58  /// `types` allows filtering the objects of interest. For example, to only listen for information about signals, use `[mpr_type::MPR_SIG]`.
59  /// 
60  /// This function must be called before functions like [get_devices](Graph::get_devices) will return any results.
61  pub fn subscribe(&self, device: Option<Device>, types: &[mpr_type]) {
62    unsafe {
63      let types_bitflag = types.iter()
64        .map(|t| *t as i32)
65        .fold(0, |acc, t| acc | t);
66
67      mpr_graph_subscribe(self.handle, device.map(|d| d.handle).unwrap_or(ptr::null_mut()), types_bitflag, -1);
68    }
69  }
70
71  /// Get all devices currently visible to the graph.
72  /// 
73  /// If [subscribe](Graph::subscribe) has not been called, this function will not be able to see any devices (except those owned by this graph via `Device::create_from_graph`).
74  pub fn get_devices<'a>(&'a self) -> Vec<Device<'a>> {
75    let ptr = unsafe {
76      mpr_graph_get_list(self.handle, mpr_type::MPR_DEV as i32)
77    };
78    read_list(ptr, |ptr| {
79      Device {
80        handle: ptr,
81        owned: false,
82        graph: Some(&self)
83      }
84    })
85  }
86}
87
88/// A directional connection between multiple signals. Changes to input signals will affect output signals.
89/// 
90/// # Examples
91/// Create a map between two signals:
92/// ```
93/// use std::thread;
94/// use std::time::Duration;
95/// use libmapper_rs::graph::Map;
96/// use libmapper_rs::signal::Signal;
97/// fn create_map(sig_a: &Signal, sig_b: &Signal) -> Map {
98///   let map = Map::create(sig_a, sig_b);
99///   loop {
100///     if map.is_ready() {
101///      break;
102///     }
103///    thread::sleep(Duration::from_millis(10));
104///   }
105///   map
106/// }
107/// ```
108pub struct Map {
109  pub(crate) handle: mpr_map,
110  pub(crate) owned: bool
111}
112
113impl Map {
114  /// Create a new map between two signals.
115  /// This does not actually create the map in the graph, [push](Map::push) must be called to let the rest of the graph know about the map.
116  pub fn create(src: &Signal, dst: &Signal) -> Map {
117    Map {
118      handle: unsafe { mpr_map_new(1, &src.handle, 1, &dst.handle) },
119      owned: true
120    }
121  }
122
123  /// Publish this map to the distributed graph.
124  /// After calling this function and once [is_ready](Map::is_ready) returns `true`, the map is active.
125  pub fn push(&self) {
126    unsafe {
127      mpr_obj_push(self.handle);
128    }
129  }
130
131  /// Returns `true` once the map has been published and is active.
132  /// Otherwise, returns false.
133  pub fn is_ready(&self) -> bool {
134    unsafe {
135      mpr_map_get_is_ready(self.handle) != 0
136    }
137  }
138
139  /// Destroy the map, severing the connection between the signals.
140  pub fn release(self) {
141    if !self.owned {
142      return;
143    }
144
145    unsafe {
146      mpr_map_release(self.handle)
147    }
148  }
149
150  /// Set the expression used to map the values from the source(s) to the destination.
151  ///
152  /// This is a helper function wrapping [`MapperObject::set_property_str`]
153  pub fn set_expr(&self, expression: &str) {
154    self.set_property_str(mpr_prop::MPR_PROP_EXPR, expression);
155  }
156}