// Copyright © 2017 Mozilla Foundation // // This program is made available under an ISC-style license. See the // accompanying file LICENSE for details use cubeb_core::{ffi, DeviceCollectionRef, DeviceRef, DeviceType, StreamParams, StreamParamsRef}; use std::ffi::CStr; use std::mem; use std::os::raw::{c_char, c_int, c_void}; use {ContextOps, StreamOps}; // Helper macro for unwrapping `Result` values from rust-api calls // while returning early with a c-api error code if the value of the // expression is `Err`. macro_rules! _try( ($e:expr) => (match $e { Ok(e) => e, Err(e) => return e.raw_code() }) ); macro_rules! as_opt_ref { ($e:expr) => { if $e.is_null() { None } else { Some(StreamParamsRef::from_ptr($e)) } }; } #[macro_export] macro_rules! capi_new( ($ctx:ident, $stm:ident) => ( Ops { init: Some($crate::capi::capi_init::<$ctx>), get_backend_id: Some($crate::capi::capi_get_backend_id::<$ctx>), get_max_channel_count: Some($crate::capi::capi_get_max_channel_count::<$ctx>), get_min_latency: Some($crate::capi::capi_get_min_latency::<$ctx>), get_preferred_sample_rate: Some($crate::capi::capi_get_preferred_sample_rate::<$ctx>), enumerate_devices: Some($crate::capi::capi_enumerate_devices::<$ctx>), device_collection_destroy: Some($crate::capi::capi_device_collection_destroy::<$ctx>), destroy: Some($crate::capi::capi_destroy::<$ctx>), stream_init: Some($crate::capi::capi_stream_init::<$ctx>), stream_destroy: Some($crate::capi::capi_stream_destroy::<$stm>), stream_start: Some($crate::capi::capi_stream_start::<$stm>), stream_stop: Some($crate::capi::capi_stream_stop::<$stm>), stream_get_position: Some($crate::capi::capi_stream_get_position::<$stm>), stream_get_latency: Some($crate::capi::capi_stream_get_latency::<$stm>), stream_get_input_latency: Some($crate::capi::capi_stream_get_input_latency::<$stm>), stream_set_volume: Some($crate::capi::capi_stream_set_volume::<$stm>), stream_set_name: Some($crate::capi::capi_stream_set_name::<$stm>), stream_get_current_device: Some($crate::capi::capi_stream_get_current_device::<$stm>), stream_device_destroy: Some($crate::capi::capi_stream_device_destroy::<$stm>), stream_register_device_changed_callback: Some($crate::capi::capi_stream_register_device_changed_callback::<$stm>), register_device_collection_changed: Some($crate::capi::capi_register_device_collection_changed::<$ctx>) })); /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `context` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_init( c: *mut *mut ffi::cubeb, context_name: *const c_char, ) -> c_int { let anchor = &(); let context_name = opt_cstr(anchor, context_name); let context = _try!(CTX::init(context_name)); *c = context.as_ptr(); // Leaking pointer across C FFI mem::forget(context); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_get_backend_id(c: *mut ffi::cubeb) -> *const c_char { let ctx = &mut *(c as *mut CTX); ctx.backend_id().as_ptr() } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `max_channels` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_get_max_channel_count( c: *mut ffi::cubeb, max_channels: *mut u32, ) -> c_int { let ctx = &mut *(c as *mut CTX); *max_channels = _try!(ctx.max_channel_count()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `latency_frames` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_get_min_latency( c: *mut ffi::cubeb, param: ffi::cubeb_stream_params, latency_frames: *mut u32, ) -> c_int { let ctx = &mut *(c as *mut CTX); let param = StreamParams::from(param); *latency_frames = _try!(ctx.min_latency(param)); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `rate` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_get_preferred_sample_rate( c: *mut ffi::cubeb, rate: *mut u32, ) -> c_int { let ctx = &mut *(c as *mut CTX); *rate = _try!(ctx.preferred_sample_rate()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `collection` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_enumerate_devices( c: *mut ffi::cubeb, devtype: ffi::cubeb_device_type, collection: *mut ffi::cubeb_device_collection, ) -> c_int { let ctx = &mut *(c as *mut CTX); let devtype = DeviceType::from_bits_truncate(devtype); let collection = DeviceCollectionRef::from_ptr(collection); _try!(ctx.enumerate_devices(devtype, collection)); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` and `collection` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_device_collection_destroy( c: *mut ffi::cubeb, collection: *mut ffi::cubeb_device_collection, ) -> c_int { let ctx = &mut *(c as *mut CTX); let collection = DeviceCollectionRef::from_ptr_mut(collection); _try!(ctx.device_collection_destroy(collection)); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_destroy(c: *mut ffi::cubeb) { let _: Box = Box::from_raw(c as *mut _); } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `c`, `s`, `stream_name`, `input_stream_params`, /// `output_stream_params`, `data_callback`, `state_callback`, and `user_ptr` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_init( c: *mut ffi::cubeb, s: *mut *mut ffi::cubeb_stream, stream_name: *const c_char, input_device: ffi::cubeb_devid, input_stream_params: *mut ffi::cubeb_stream_params, output_device: ffi::cubeb_devid, output_stream_params: *mut ffi::cubeb_stream_params, latency_frames: u32, data_callback: ffi::cubeb_data_callback, state_callback: ffi::cubeb_state_callback, user_ptr: *mut c_void, ) -> c_int { let ctx = &mut *(c as *mut CTX); let anchor = &(); // for lifetime of stream_name as CStr let input_stream_params = as_opt_ref!(input_stream_params); let output_stream_params = as_opt_ref!(output_stream_params); let stream = _try!(ctx.stream_init( opt_cstr(anchor, stream_name), input_device, input_stream_params, output_device, output_stream_params, latency_frames, data_callback, state_callback, user_ptr )); *s = stream.as_ptr(); // Leaking pointer across C FFI mem::forget(stream); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_stream_destroy(s: *mut ffi::cubeb_stream) { let _ = Box::from_raw(s as *mut STM); } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_stream_start(s: *mut ffi::cubeb_stream) -> c_int { let stm = &mut *(s as *mut STM); _try!(stm.start()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_stream_stop(s: *mut ffi::cubeb_stream) -> c_int { let stm = &mut *(s as *mut STM); _try!(stm.stop()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `position` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_get_position( s: *mut ffi::cubeb_stream, position: *mut u64, ) -> c_int { let stm = &mut *(s as *mut STM); *position = _try!(stm.position()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `latency` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_get_latency( s: *mut ffi::cubeb_stream, latency: *mut u32, ) -> c_int { let stm = &mut *(s as *mut STM); *latency = _try!(stm.latency()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `latency` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_get_input_latency( s: *mut ffi::cubeb_stream, latency: *mut u32, ) -> c_int { let stm = &mut *(s as *mut STM); *latency = _try!(stm.input_latency()); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` pointer. /// The caller should ensure that pointer is valid. pub unsafe extern "C" fn capi_stream_set_volume( s: *mut ffi::cubeb_stream, volume: f32, ) -> c_int { let stm = &mut *(s as *mut STM); _try!(stm.set_volume(volume)); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `name` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_set_name( s: *mut ffi::cubeb_stream, name: *const c_char, ) -> c_int { let stm = &mut *(s as *mut STM); let anchor = &(); if let Some(name) = opt_cstr(anchor, name) { _try!(stm.set_name(name)); ffi::CUBEB_OK } else { ffi::CUBEB_ERROR_INVALID_PARAMETER } } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `device` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_get_current_device( s: *mut ffi::cubeb_stream, device: *mut *mut ffi::cubeb_device, ) -> i32 { let stm = &mut *(s as *mut STM); *device = _try!(stm.current_device()).as_ptr(); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `device` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_device_destroy( s: *mut ffi::cubeb_stream, device: *mut ffi::cubeb_device, ) -> c_int { let stm = &mut *(s as *mut STM); let device = DeviceRef::from_ptr(device); let _ = stm.device_destroy(device); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s` and `device_changed_callback` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_stream_register_device_changed_callback( s: *mut ffi::cubeb_stream, device_changed_callback: ffi::cubeb_device_changed_callback, ) -> c_int { let stm = &mut *(s as *mut STM); _try!(stm.register_device_changed_callback(device_changed_callback)); ffi::CUBEB_OK } /// # Safety /// /// Entry point from C code. /// /// This function is unsafe because it dereferences the given `s`, `collection_changed_callback`, and /// `user_ptr` pointers. /// The caller should ensure those pointers are valid. pub unsafe extern "C" fn capi_register_device_collection_changed( c: *mut ffi::cubeb, devtype: ffi::cubeb_device_type, collection_changed_callback: ffi::cubeb_device_collection_changed_callback, user_ptr: *mut c_void, ) -> i32 { let ctx = &mut *(c as *mut CTX); let devtype = DeviceType::from_bits_truncate(devtype); _try!(ctx.register_device_collection_changed(devtype, collection_changed_callback, user_ptr)); ffi::CUBEB_OK } fn opt_cstr(_anchor: &T, ptr: *const c_char) -> Option<&CStr> { if ptr.is_null() { None } else { Some(unsafe { CStr::from_ptr(ptr) }) } }