|
172 | 172 | //! Server modules can be remotely loaded and unloaded using [`Introspector::load_module()`] and
|
173 | 173 | //! [`Introspector::unload_module()`].
|
174 | 174 | //!
|
| 175 | +//! # Messages |
| 176 | +//! |
| 177 | +//! Server objects like sinks, sink inputs or modules can register a message handler to communicate |
| 178 | +//! with clients. A message can be sent to a named message handler using |
| 179 | +//! [`Introspector::send_message_to_object()`]. |
| 180 | +//! |
175 | 181 | //! # Clients
|
176 | 182 | //!
|
177 | 183 | //! The only operation supported on clients is the possibility of kicking them off the server using
|
178 | 184 | //! [`Introspector::kill_client()`].
|
179 | 185 |
|
180 | 186 | use std::os::raw::c_void;
|
| 187 | +#[cfg(any(doc, feature = "pa_v15"))] |
| 188 | +use std::os::raw::c_char; |
181 | 189 | use std::ffi::{CStr, CString};
|
182 | 190 | use std::borrow::Cow;
|
183 | 191 | use std::ptr::null_mut;
|
@@ -1278,6 +1286,63 @@ fn context_index_cb_proxy(_: *mut ContextInternal, index: u32, userdata: *mut c_
|
1278 | 1286 | });
|
1279 | 1287 | }
|
1280 | 1288 |
|
| 1289 | +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| 1290 | +// Messages |
| 1291 | +//////////////////////////////////////////////////////////////////////////////////////////////////// |
| 1292 | + |
| 1293 | +impl Introspector { |
| 1294 | + /// Send a message to an object that registered a message handler. |
| 1295 | + /// |
| 1296 | + /// The callback must accept two params, firstly a boolean indicating success if `true`, and |
| 1297 | + /// secondly, the response string. The response string may possibly not be given if |
| 1298 | + /// unsuccessful. |
| 1299 | + /// |
| 1300 | + /// For more information see the [messaging_api.txt] documentation in the PulseAudio repository. |
| 1301 | + /// |
| 1302 | + /// [messaging_api.txt]: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/blob/master/doc/messaging_api.txt |
| 1303 | + #[cfg(any(doc, feature = "pa_v15"))] |
| 1304 | + #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))] |
| 1305 | + pub fn send_message_to_object<F>(&mut self, recipient_name: &str, message: &str, |
| 1306 | + message_parameters: &str, callback: F) -> Operation<dyn FnMut(bool, Option<String>)> |
| 1307 | + where F: FnMut(bool, Option<String>) + 'static |
| 1308 | + { |
| 1309 | + // Warning: New CStrings will be immediately freed if not bound to a variable, leading to |
| 1310 | + // as_ptr() giving dangling pointers! |
| 1311 | + let c_recipient_name = CString::new(recipient_name.clone()).unwrap(); |
| 1312 | + let c_message = CString::new(message.clone()).unwrap(); |
| 1313 | + let c_message_parameters = CString::new(message_parameters.clone()).unwrap(); |
| 1314 | + |
| 1315 | + let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool, Option<String>)>(Box::new(callback)); |
| 1316 | + let ptr = unsafe { capi::pa_context_send_message_to_object(self.context, |
| 1317 | + c_recipient_name.as_ptr(), c_message.as_ptr(), c_message_parameters.as_ptr(), |
| 1318 | + Some(send_message_to_object_cb_proxy), cb_data) }; |
| 1319 | + Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool, Option<String>)>) |
| 1320 | + } |
| 1321 | +} |
| 1322 | + |
| 1323 | +/// Proxy for send message to object callbacks. |
| 1324 | +/// |
| 1325 | +/// Warning: This is for single-use cases only! It destroys the actual closure callback. |
| 1326 | +#[cfg(any(doc, feature = "pa_v15"))] |
| 1327 | +extern "C" |
| 1328 | +fn send_message_to_object_cb_proxy(_: *mut ContextInternal, success: i32, response: *const c_char, |
| 1329 | + userdata: *mut c_void) |
| 1330 | +{ |
| 1331 | + let success_actual = match success { 0 => false, _ => true }; |
| 1332 | + let _ = std::panic::catch_unwind(|| { |
| 1333 | + let r = match response.is_null() { |
| 1334 | + true => None, |
| 1335 | + false => { |
| 1336 | + let tmp = unsafe { CStr::from_ptr(response) }; |
| 1337 | + Some(tmp.to_string_lossy().into_owned()) |
| 1338 | + }, |
| 1339 | + }; |
| 1340 | + // Note, destroys closure callback after use - restoring outer box means it gets dropped |
| 1341 | + let mut callback = get_su_callback::<dyn FnMut(bool, Option<String>)>(userdata); |
| 1342 | + (callback)(success_actual, r); |
| 1343 | + }); |
| 1344 | +} |
| 1345 | + |
1281 | 1346 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
1282 | 1347 | // Client info
|
1283 | 1348 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
0 commit comments