libmapper_rs/device.rs
1use std::ffi::CString;
2use std::os::raw::c_int;
3use std::ptr;
4use std::time::Duration;
5use crate::bindings::{mpr_dev, mpr_dev_free, mpr_dev_get_is_ready, mpr_dev_get_sigs, mpr_dev_new, mpr_dev_poll, mpr_dir, mpr_obj, mpr_prop, mpr_sig_new, mpr_type};
6use crate::graph::Graph;
7use crate::object::MapperObject;
8use crate::signal::Signal;
9
10/// A device is libmapper's connection to the distributed graph.
11/// Each device is a collection of signal instances and their metadata.
12///
13/// # Examples
14/// ```
15/// use libmapper_rs::device::Device;
16/// use std::time::Duration;
17/// // you can create a device with Device::create
18/// let dev = Device::create("rust");
19/// // you have to poll a device occasionally to make things happen
20/// loop {
21/// dev.poll_and_block(Duration::from_millis(10)); // poll in 10ms intervals
22/// if dev.is_ready() {
23/// break;
24/// }
25/// }
26/// // create signals, etc...
27/// ```
28pub struct Device<'a> {
29 pub(crate) handle: mpr_dev,
30 pub(crate) owned: bool,
31 pub(crate) graph: Option<&'a Graph>
32}
33
34unsafe impl Send for Device<'_> {}
35unsafe impl Sync for Device<'_> {}
36
37impl Drop for Device<'_> {
38 fn drop(&mut self) {
39 if self.owned {
40 unsafe {
41 mpr_dev_free(self.handle);
42 }
43 }
44 }
45}
46
47impl Device<'_> {
48 /// Create a new device with the given name.
49 /// The device will use it's own connection to the graph.
50 ///
51 /// Before calling any other methods on the device, you should poll it until it is ready.
52 ///
53 /// # Notes
54 /// If you plan on creating multiple devices, consider using (Device::create_from_graph)[Device::create_from_graph] instead to pool resources.
55 pub fn create(name: &str) -> Device {
56 let name_ptr = CString::new(name).expect("CString::new failed");
57 unsafe {
58 Device {
59 owned: true,
60 handle: mpr_dev_new(name_ptr.as_ptr(), ptr::null_mut()),
61 graph: None
62 }
63 }
64 }
65 /// Create a new device with a shared graph.
66 /// Sharing a graph between devices allows them to pool some resources and networking, potentially improving performance.
67 pub fn create_from_graph<'a>(name: &str, graph: &'a Graph) -> Device<'a> {
68 let name_ptr = CString::new(name).expect("CString::new failed");
69 unsafe {
70 Device {
71 owned: true,
72 handle: mpr_dev_new(name_ptr.as_ptr(), graph.handle),
73 graph: Some(graph)
74 }
75 }
76 }
77}
78
79impl Device<'_> {
80 /// Poll the device without blocking
81 ///
82 /// # Notes
83 /// You may want to use [poll_all](Device::poll_all) in a multithreaded enviroment,
84 /// when using non-blocking polling libmapper will use a heuristic to determine how many messages
85 /// to parse at once for performance. If you don't care how long this function will take to run,
86 /// call Device::poll_all.
87 pub fn poll(&self) {
88 unsafe {
89 mpr_dev_poll(self.handle, 0);
90 }
91 }
92 /// Processes all messages in the device's queue, no matter how long it takes.
93 /// If using dedicated threads to poll devices this is probably what you want to use instead of [poll](Device::poll)
94 pub fn poll_all(&self) {
95 unsafe {
96 mpr_dev_poll(self.handle, -1);
97 }
98 }
99 /// Blocks the current thread for a specified amount of time.
100 /// Use this function instead of sleeping in a loop.
101 pub fn poll_and_block(&self, time: Duration) {
102 unsafe {
103 mpr_dev_poll(self.handle, time.as_millis() as c_int);
104 }
105 }
106}
107
108impl Device<'_> {
109 /// Tests if the device is ready to use.
110 /// Do not try to call any other methods until this returns `true`.
111 pub fn is_ready(&self) -> bool {
112 unsafe {
113 mpr_dev_get_is_ready(self.handle) > 0
114 }
115 }
116}
117
118/// Marker trait for types that are bit-compatible with the libmapper C library.
119/// If this trait is implemented on a type, that type can be passed to libmapper functions safely.
120/// Use the `get_mpr_type` function to pass a type parameter to libmapper.
121pub trait MappableType {
122 /// Get the `mpr_type` representing this rust type.
123 fn get_mpr_type() -> mpr_type;
124}
125
126impl MappableType for f64 {
127 fn get_mpr_type() -> mpr_type {
128 mpr_type::MPR_DBL
129 }
130}
131
132impl MappableType for mpr_type {
133 fn get_mpr_type() -> mpr_type {
134 mpr_type::MPR_TYPE
135 }
136}
137
138impl MappableType for f32 {
139 fn get_mpr_type() -> mpr_type {
140 mpr_type::MPR_FLT
141 }
142}
143
144impl MappableType for i32 {
145 fn get_mpr_type() -> mpr_type {
146 mpr_type::MPR_INT32
147 }
148}
149
150impl MappableType for i64 {
151 fn get_mpr_type() -> mpr_type {
152 mpr_type::MPR_INT64
153 }
154}
155
156impl<'a> Device<'a> {
157 /// Get the shared graph used by this device.
158 /// If the device was created with [Device::create](Device::create) this will return None.
159 pub fn get_graph(&self) -> Option<&'a Graph> {
160 self.graph
161 }
162}
163
164impl Device<'_> {
165 /// Check if the device was created with a shared graph.
166 pub fn has_shared_graph(&self) -> bool {
167 self.graph.is_some()
168 }
169 /// Create a signal with the given name and direction.
170 ///
171 /// # Notes
172 /// - The signal will have a vector length of 1 (i.e. single value).
173 /// - The passed generic parameter controls what type of data the signal will hold.
174 ///
175 /// # Examples
176 /// ```
177 /// use libmapper_rs::device::Device;
178 /// use libmapper_rs::constants::mpr_dir;
179 /// fn setup_signals(dev: &Device) {
180 /// // create an outgoing signal that outputs a single f64 value
181 /// let sig = dev.create_signal::<f64>("test_signal", mpr_dir::MPR_DIR_OUT);
182 /// }
183 /// ```
184 pub fn create_signal<T: MappableType + Copy>(&self, name: &str, direction: mpr_dir) -> Signal {
185 self.create_vector_signal::<T>(name, direction, 1)
186 }
187 /// Create a signal with the given name, direction, and vector length.
188 ///
189 /// # Notes
190 /// - The passed generic parameter controls what type of data the signal will hold.
191 ///
192 pub fn create_vector_signal<T: MappableType + Copy>(&self, name: &str, direction: mpr_dir, vector_length: u32) -> Signal {
193 let data_type: mpr_type = T::get_mpr_type();
194
195 let name_ptr = CString::new(name).expect("CString::new failed");
196 unsafe {
197 Signal {
198 handle: mpr_sig_new(self.handle, direction, name_ptr.as_ptr(), vector_length as i32,
199 data_type, ptr::null(), ptr::null(), ptr::null(), ptr::null_mut(), None, 0),
200 data_type,
201 owned: true,
202 vector_length
203 }
204 }
205 }
206 /// Get a list of all signals owned by this device.
207 pub fn get_signals(&self, direction: mpr_dir) -> Vec<Signal> {
208 let list = unsafe {mpr_dev_get_sigs(self.handle, direction)};
209 crate::util::read_list(list, |ptr| {
210 let data_type = (ptr as mpr_obj).get_property::<mpr_type>(mpr_prop::MPR_PROP_TYPE).unwrap();
211 let vector_length = (ptr as mpr_obj).get_property::<i32>(mpr_prop::MPR_PROP_LEN).unwrap() as u32;
212 Signal {
213 handle: ptr,
214 data_type,
215 owned: false,
216 vector_length
217 }
218 })
219 }
220}