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}