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