libpulse_binding/context/mod.rs
1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! Connection contexts for asynchronous communication with a server.
15//!
16//! A `Context` object wraps a connection to a PulseAudio server using its native protocol.
17//!
18//! # Overview
19//!
20//! A context is the basic object for a connection to a PulseAudio server. It multiplexes commands,
21//! data streams and events through a single channel.
22//!
23//! There is no need for more than one context per application, unless connections to multiple
24//! servers are needed.
25//!
26//! # Operations
27//!
28//! All operations on the context are performed asynchronously. I.e. the client will not wait for
29//! the server to complete the request. To keep track of all these in-flight operations, the
30//! application is given an [`Operation`] object for each asynchronous operation.
31//!
32//! There are only two actions (besides reference counting) that can be performed on an
33//! [`Operation`]: querying its state with [`Operation::get_state()`] and aborting it with
34//! [`Operation::cancel()`].
35//!
36//! An [`Operation`] object is reference counted, so an application must make sure to unreference
37//! it, even if it has no intention of using it. This however is taken care of automatically in this
38//! Rust binding via the implementation of the `Drop` trait on the object.
39//!
40//! # Connecting
41//!
42//! A context must be connected to a server before any operation can be issued. Calling
43//! [`Context::connect()`] will initiate the connection procedure. Unlike most asynchronous
44//! operations, connecting does not result in an [`Operation`] object. Instead, the application
45//! should register a callback using [`Context::set_state_callback()`].
46//!
47//! # Disconnecting
48//!
49//! When the sound support is no longer needed, the connection needs to be closed using
50//! [`Context::disconnect()`]. This is an immediate function that works synchronously.
51//!
52//! Since the context object has references to other objects it must be disconnected after use or
53//! there is a high risk of memory leaks. If the connection has terminated by itself, then there is
54//! no need to explicitly disconnect the context using [`Context::disconnect()`].
55//!
56//! # Functions
57//!
58//! The sound server’s functionality can be divided into a number of subsections:
59//!
60//! * [`stream`](mod@crate::stream)
61//! * [`context::scache`](mod@crate::context::scache)
62//! * [`context::introspect`](mod@crate::context::introspect)
63//! * [`context::subscribe`](mod@crate::context::subscribe)
64
65pub mod ext_device_manager;
66pub mod ext_device_restore;
67pub mod ext_stream_restore;
68pub mod introspect;
69pub mod scache;
70pub mod subscribe;
71
72use std::os::raw::{c_char, c_void};
73use std::ffi::{CStr, CString};
74use std::ptr::{null, null_mut};
75use std::rc::Rc;
76use bitflags::bitflags;
77use num_derive::{FromPrimitive, ToPrimitive};
78use crate::{def, sample};
79use crate::mainloop::api::{Mainloop, MainloopInnerType};
80use crate::mainloop::events;
81use crate::mainloop::events::timer::{TimeEvent, TimeEventRef};
82use crate::operation::Operation;
83use crate::error::PAErr;
84use crate::time::MonotonicTs;
85use crate::proplist::{self, Proplist, ProplistInternal};
86use crate::callbacks::{box_closure_get_capi_ptr, get_su_callback, MultiUseCallback};
87use crate::capi::pa_context as ContextInternal;
88
89/// An opaque connection context to a daemon.
90///
91/// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop.
92pub struct Context {
93 /// The actual C object.
94 pub(crate) ptr: *mut ContextInternal,
95 /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks.
96 weak: bool,
97 /// Multi-use callback closure pointers.
98 cb_ptrs: CallbackPointers,
99}
100
101unsafe impl Send for Context {}
102unsafe impl Sync for Context {}
103
104/// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple
105/// times), for freeing at the appropriate time.
106#[derive(Default)]
107struct CallbackPointers {
108 set_state: NotifyCb,
109 subscribe: self::subscribe::Callback,
110 event: EventCb,
111}
112
113type NotifyCb = MultiUseCallback<dyn FnMut(), extern "C" fn(*mut ContextInternal, *mut c_void)>;
114
115type EventCb = MultiUseCallback<dyn FnMut(String, Proplist),
116 extern "C" fn(*mut ContextInternal, name: *const c_char, pl: *mut ProplistInternal, *mut c_void)>;
117
118type ExtSubscribeCb = MultiUseCallback<dyn FnMut(), extern "C" fn(*mut ContextInternal, *mut c_void)>;
119
120/// The state of a connection context.
121#[repr(C)]
122#[derive(Debug, Copy, Clone, PartialEq, Eq)]
123#[derive(FromPrimitive, ToPrimitive)]
124pub enum State {
125 /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
126 (C API) equivalent */
127 /// The context hasn’t been connected yet.
128 Unconnected,
129 /// A connection is being established.
130 Connecting,
131 /// The client is authorizing itself to the daemon.
132 Authorizing,
133 /// The client is passing its application name to the daemon.
134 SettingName,
135 /// The connection is established, the context is ready to execute operations.
136 Ready,
137 /// The connection failed or was disconnected.
138 Failed,
139 /// The connection was terminated cleanly.
140 Terminated,
141}
142
143/// Test size is equal to `sys` equivalent
144#[test]
145fn state_compare_capi() {
146 assert_eq!(std::mem::size_of::<State>(), std::mem::size_of::<capi::pa_context_state_t>());
147 assert_eq!(std::mem::align_of::<State>(), std::mem::align_of::<capi::pa_context_state_t>());
148
149 // Check order and value of variants match
150 // No point checking conversions in both directions since both are a transmute
151 assert_eq!(State::Unconnected, State::from(capi::pa_context_state_t::Unconnected));
152 assert_eq!(State::Connecting, State::from(capi::pa_context_state_t::Connecting));
153 assert_eq!(State::Authorizing, State::from(capi::pa_context_state_t::Authorizing));
154 assert_eq!(State::SettingName, State::from(capi::pa_context_state_t::SettingName));
155 assert_eq!(State::Ready, State::from(capi::pa_context_state_t::Ready));
156 assert_eq!(State::Failed, State::from(capi::pa_context_state_t::Failed));
157 assert_eq!(State::Terminated, State::from(capi::pa_context_state_t::Terminated));
158}
159
160impl From<State> for capi::pa_context_state_t {
161 #[inline]
162 fn from(s: State) -> Self {
163 unsafe { std::mem::transmute(s) }
164 }
165}
166impl From<capi::pa_context_state_t> for State {
167 #[inline]
168 fn from(s: capi::pa_context_state_t) -> Self {
169 unsafe { std::mem::transmute(s) }
170 }
171}
172
173impl State {
174 /// Checks if the passed state is one of the connected states (returns `true` if so).
175 pub fn is_good(self) -> bool {
176 self == State::Connecting
177 || self == State::Authorizing
178 || self == State::SettingName
179 || self == State::Ready
180 }
181}
182
183bitflags! {
184 /// Context flag set.
185 #[repr(transparent)]
186 pub struct FlagSet: u32 {
187 /// No flags set.
188 const NOFLAGS = capi::PA_CONTEXT_NOFLAGS;
189 /// Disable autospawning of the PulseAudio daemon if required.
190 const NOAUTOSPAWN = capi::PA_CONTEXT_NOAUTOSPAWN;
191 /// Don’t fail if the daemon is not available when [`Context::connect()`] is called, instead
192 /// enter [`State::Connecting`] state and wait for the daemon to appear.
193 const NOFAIL = capi::PA_CONTEXT_NOFAIL;
194 }
195}
196
197impl Context {
198 /// Instantiates a new connection context with an abstract mainloop API and an application name.
199 ///
200 /// It is recommended to use [`new_with_proplist()`](Self::new_with_proplist) instead and
201 /// specify some initial properties.
202 ///
203 /// Note, this will fail either should the underlying C API call return a null pointer for some
204 /// reason, or if the version of the PulseAudio client system library at runtime is found to be
205 /// older than the minimum version set via this crate’s feature flags (as a means to help
206 /// prevent “forward” compatibility problems, as discussed in the project `COMPATIBILITY.md`
207 /// documentation).
208 pub fn new(mainloop: &impl Mainloop, name: &str) -> Option<Self> {
209 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
210 // as_ptr() giving dangling pointers!
211 let c_name = CString::new(name).unwrap();
212 let ptr =
213 unsafe { capi::pa_context_new(mainloop.inner().get_api().as_ref(), c_name.as_ptr()) };
214 Self::create(ptr)
215 }
216
217 /// Instantiates a new connection context with an abstract mainloop API and an application name,
218 /// and specify the initial client property list.
219 ///
220 /// Note, this will fail either should the underlying C API call return a null pointer for some
221 /// reason, or if the version of the PulseAudio client system library at runtime is found to be
222 /// older than the minimum version set via this crate’s feature flags (as a means to help
223 /// prevent “forward” compatibility problems, as discussed in the project `COMPATIBILITY.md`
224 /// documentation).
225 pub fn new_with_proplist(mainloop: &impl Mainloop, name: &str, proplist: &Proplist)
226 -> Option<Self>
227 {
228 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
229 // as_ptr() giving dangling pointers!
230 let c_name = CString::new(name).unwrap();
231 let ptr = unsafe { capi::pa_context_new_with_proplist(mainloop.inner().get_api().as_ref(),
232 c_name.as_ptr(), proplist.0.ptr) };
233 Self::create(ptr)
234 }
235
236 /// Internal common creation function
237 fn create(ptr: *mut ContextInternal) -> Option<Self> {
238 // Block creation if runtime client system library is too old, to block the potential
239 // “forward” compatibility problems discussed in the project `COMPATIBILITY.md`
240 // documentation.
241 if crate::version::library_version_is_too_old() != Ok(false) {
242 return None;
243 }
244
245 match ptr.is_null() {
246 false => Some(Self::from_raw(ptr)),
247 true => None,
248 }
249 }
250
251 /// Creates a new `Context` from an existing [`ContextInternal`] pointer.
252 #[inline]
253 pub(crate) fn from_raw(ptr: *mut ContextInternal) -> Self {
254 assert_eq!(false, ptr.is_null());
255 Self { ptr: ptr, weak: false, cb_ptrs: Default::default() }
256 }
257
258 /// Sets a callback function that is called whenever the context status changes.
259 pub fn set_state_callback(&mut self, callback: Option<Box<dyn FnMut() + 'static>>) {
260 let saved = &mut self.cb_ptrs.set_state;
261 *saved = NotifyCb::new(callback);
262 let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy_multi);
263 unsafe { capi::pa_context_set_state_callback(self.ptr, cb_fn, cb_data); }
264 }
265
266 /// Sets a callback function that is called whenever a meta/policy control event is received.
267 ///
268 /// The callback is given a name which represents what event occurred. The set of defined events
269 /// can be extended at any time. Also, server modules may introduce additional message types so
270 /// make sure that your callback function ignores messages it doesn’t know. It is also given an
271 /// (owned) property list.
272 pub fn set_event_callback(&mut self,
273 callback: Option<Box<dyn FnMut(String, Proplist) + 'static>>)
274 {
275 let saved = &mut self.cb_ptrs.event;
276 *saved = EventCb::new(callback);
277 let (cb_fn, cb_data) = saved.get_capi_params(event_cb_proxy);
278 unsafe { capi::pa_context_set_event_callback(self.ptr, cb_fn, cb_data); }
279 }
280
281 /// Gets the error number of the last failed operation.
282 #[inline]
283 pub fn errno(&self) -> PAErr {
284 PAErr(unsafe { capi::pa_context_errno(self.ptr) })
285 }
286
287 /// Checks if some data is pending to be written to the connection (returns `true` if so).
288 #[inline]
289 pub fn is_pending(&self) -> bool {
290 unsafe { capi::pa_context_is_pending(self.ptr) != 0 }
291 }
292
293 /// Gets the current context status.
294 #[inline]
295 pub fn get_state(&self) -> State {
296 unsafe { capi::pa_context_get_state(self.ptr).into() }
297 }
298
299 /// Connects the context to the specified server.
300 ///
301 /// If server is `None`, connect to the default server. This routine may but will not always
302 /// return synchronously on error. Use [`set_state_callback()`] to be notified when the
303 /// connection is established. If `flags` doesn’t have [`FlagSet::NOAUTOSPAWN`] set and no
304 /// specific server is specified or accessible, a new daemon is spawned. If `api` is not `None`,
305 /// the functions specified in the structure are used when forking a new child process.
306 ///
307 /// [`set_state_callback()`]: Self::set_state_callback
308 pub fn connect(&mut self, server: Option<&str>, flags: FlagSet, api: Option<&def::SpawnApi>)
309 -> Result<(), PAErr>
310 {
311 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
312 // as_ptr() giving dangling pointers!
313 let c_server = match server {
314 Some(server) => CString::new(server).unwrap(),
315 None => CString::new("").unwrap(),
316 };
317
318 let p_api = api.map_or(null::<capi::pa_spawn_api>(), |a| a.as_ref());
319 let p_server = server.map_or(null::<c_char>(), |_| c_server.as_ptr() as *const c_char);
320
321 match unsafe { capi::pa_context_connect(self.ptr, p_server, flags.bits(), p_api) } {
322 0 => Ok(()),
323 e => Err(PAErr(e)),
324 }
325 }
326
327 /// Terminates the context connection immediately.
328 #[inline]
329 pub fn disconnect(&mut self) {
330 unsafe { capi::pa_context_disconnect(self.ptr); }
331 }
332
333 /// Drains the context.
334 ///
335 /// If there is nothing to drain, the function returns `None`.
336 ///
337 /// Note that it can also return `None` under other conditions. Many functions in the C API
338 /// perform internal state validation checks and return a null pointer if they detect a problem,
339 /// just as they return a null pointer on invalid input. Other functions panic on getting a null
340 /// pointer return, however this function is unique in a null pointer also signalling something
341 /// useful, and it is not possible to tell the difference. However, while I feel the need to be
342 /// clear about the possibility, I believe that such invalid state conditions should only occur
343 /// if there were a serious bug within PA, thus you are probably safe to just ignore this and
344 /// always take a `None` return to indicate only that there is nothing to drain.
345 pub fn drain<F>(&mut self, callback: F) -> Option<Operation<dyn FnMut()>>
346 where F: FnMut() + 'static
347 {
348 let cb_data = box_closure_get_capi_ptr::<dyn FnMut()>(Box::new(callback));
349 let ptr =
350 unsafe { capi::pa_context_drain(self.ptr, Some(notify_cb_proxy_single), cb_data) };
351 // NOTE: this function is unique in NEEDING the `Option` wrapper on the return value, since
352 // a null pointer may be returned if there is nothing to drain! Do not remove it!
353 match ptr.is_null() {
354 false => Some(Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut()>)),
355 true => None,
356 }
357 }
358
359 /// Tells the daemon to exit.
360 ///
361 /// The returned operation is unlikely to complete successfully, since the daemon probably died
362 /// before returning a success notification.
363 ///
364 /// The callback must accept a `bool`, which indicates success.
365 ///
366 /// Panics if the underlying C function returns a null pointer.
367 pub fn exit_daemon<F>(&mut self, callback: F) -> Operation<dyn FnMut(bool)>
368 where F: FnMut(bool) + 'static
369 {
370 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
371 let ptr =
372 unsafe { capi::pa_context_exit_daemon(self.ptr, Some(success_cb_proxy), cb_data) };
373 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
374 }
375
376 /// Sets the name of the default sink.
377 ///
378 /// The callback must accept a `bool`, which indicates success.
379 ///
380 /// Panics if the underlying C function returns a null pointer.
381 pub fn set_default_sink<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
382 where F: FnMut(bool) + 'static
383 {
384 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
385 // as_ptr() giving dangling pointers!
386 let c_name = CString::new(name).unwrap();
387
388 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
389 let ptr = unsafe { capi::pa_context_set_default_sink(self.ptr, c_name.as_ptr(),
390 Some(success_cb_proxy), cb_data) };
391 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
392 }
393
394 /// Sets the name of the default source.
395 ///
396 /// The callback must accept a `bool`, which indicates success.
397 ///
398 /// Panics if the underlying C function returns a null pointer.
399 pub fn set_default_source<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
400 where F: FnMut(bool) + 'static
401 {
402 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
403 // as_ptr() giving dangling pointers!
404 let c_name = CString::new(name).unwrap();
405
406 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
407 let ptr = unsafe { capi::pa_context_set_default_source(self.ptr, c_name.as_ptr(),
408 Some(success_cb_proxy), cb_data) };
409 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
410 }
411
412 /// Checks if this is a connection to a local daemon.
413 ///
414 /// Returns `true` when the connection is to a local daemon. Returns `None` on error, for
415 /// instance when no connection has been made yet.
416 pub fn is_local(&self) -> Option<bool> {
417 match unsafe { capi::pa_context_is_local(self.ptr) } {
418 1 => Some(true),
419 0 => Some(false),
420 _ => None,
421 }
422 }
423
424 /// Sets a different application name for context on the server.
425 ///
426 /// Panics if the underlying C function returns a null pointer.
427 pub fn set_name<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
428 where F: FnMut(bool) + 'static
429 {
430 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
431 // as_ptr() giving dangling pointers!
432 let c_name = CString::new(name).unwrap();
433
434 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
435 let ptr = unsafe { capi::pa_context_set_name(self.ptr, c_name.as_ptr(),
436 Some(success_cb_proxy), cb_data) };
437 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
438 }
439
440 /// Gets the server name this context is connected to.
441 pub fn get_server(&self) -> Option<String> {
442 let ptr = unsafe { capi::pa_context_get_server(self.ptr) };
443 match ptr.is_null() {
444 false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
445 true => None,
446 }
447 }
448
449 /// Gets the protocol version of the library.
450 #[inline]
451 pub fn get_protocol_version(&self) -> u32 {
452 unsafe { capi::pa_context_get_protocol_version(self.ptr) }
453 }
454
455 /// Gets the protocol version of the connected server.
456 ///
457 /// Returns `None` on error.
458 pub fn get_server_protocol_version(&self) -> Option<u32> {
459 match unsafe { capi::pa_context_get_server_protocol_version(self.ptr) } {
460 def::INVALID_INDEX => None,
461 r => Some(r),
462 }
463 }
464
465 /// Updates the property list of the client, adding new entries.
466 ///
467 /// Please note that it is highly recommended to set as many properties initially via
468 /// [`new_with_proplist()`](Self::new_with_proplist) as possible instead a posteriori with this
469 /// function, since that information may then be used to route streams of the client to the
470 /// right device.
471 ///
472 /// Panics if the underlying C function returns a null pointer.
473 pub fn proplist_update<F>(&mut self, mode: proplist::UpdateMode, pl: &Proplist, callback: F)
474 -> Operation<dyn FnMut(bool)>
475 where F: FnMut(bool) + 'static
476 {
477 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
478 let ptr = unsafe { capi::pa_context_proplist_update(self.ptr, mode, pl.0.ptr,
479 Some(success_cb_proxy), cb_data) };
480 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
481 }
482
483 /// Updates the property list of the client, remove entries.
484 ///
485 /// Panics if the underlying C function returns a null pointer.
486 pub fn proplist_remove<F>(&mut self, keys: &[&str], callback: F) -> Operation<dyn FnMut(bool)>
487 where F: FnMut(bool) + 'static
488 {
489 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
490 // as_ptr() giving dangling pointers!
491 let mut c_keys: Vec<CString> = Vec::with_capacity(keys.len());
492 for key in keys {
493 c_keys.push(CString::new(*key).unwrap());
494 }
495
496 // Capture array of pointers to the above CString values.
497 // We also add a NULL pointer entry on the end, as expected by the C function called here.
498 let mut c_key_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1);
499 for c_key in c_keys {
500 c_key_ptrs.push(c_key.as_ptr());
501 }
502 c_key_ptrs.push(null());
503
504 let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
505 let ptr = unsafe { capi::pa_context_proplist_remove(self.ptr, c_key_ptrs.as_ptr(),
506 Some(success_cb_proxy), cb_data) };
507 Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
508 }
509
510 /// Gets the client index this context is identified in the server with.
511 ///
512 /// This is useful for usage with the introspection functions, such as
513 /// [`Introspector::get_client_info()`](self::introspect::Introspector::get_client_info).
514 ///
515 /// Returns `None` on error.
516 pub fn get_index(&self) -> Option<u32> {
517 match unsafe { capi::pa_context_get_index(self.ptr) } {
518 def::INVALID_INDEX => None,
519 r => Some(r),
520 }
521 }
522
523 /// Creates a new timer event source for the specified time.
524 ///
525 /// This is an alternative to the mainloop `new_timer_event_rt` method.
526 ///
527 /// A reference to the mainloop object is needed, in order to associate the event object with
528 /// it. The association is done to ensure the event does not outlive the mainloop.
529 ///
530 /// If pointer returned by underlying C function is `NULL`, `None` will be returned, otherwise a
531 /// [`TimeEvent`](crate::mainloop::events::timer::TimeEvent) object will be returned.
532 ///
533 /// Example event set to fire in five seconds time:
534 ///
535 /// ```rust,ignore
536 /// use libpulse_binding::time::{MonotonicTs, MicroSeconds};
537 /// let _t_event = context.rttime_new::<Mainloop, _>(&mainloop,
538 /// MonotonicTs::now() + MicroSeconds::from_secs(5).unwrap(),
539 /// |_| { println!("Timer event fired!"); });
540 /// ```
541 ///
542 /// **Note**: You must ensure that the returned event object lives for as long as you want its
543 /// event(s) to fire, as its `Drop` implementation destroys the event source. I.e. if you create
544 /// a new event, but then immediately drop the object returned here, no event will fire!
545 pub fn rttime_new<T, F>(&self, mainloop: &dyn Mainloop<MI=T::MI>, time: MonotonicTs,
546 mut callback: F) -> Option<TimeEvent<T::MI>>
547 where T: Mainloop + 'static,
548 F: FnMut(TimeEventRef<T::MI>) + 'static
549 {
550 let inner_for_wrapper = mainloop.inner();
551 let wrapper_cb = Box::new(move |ptr| {
552 let ref_obj = TimeEventRef::<T::MI>::from_raw(ptr, Rc::clone(&inner_for_wrapper));
553 callback(ref_obj);
554 });
555
556 let to_save = events::timer::EventCb::new(Some(wrapper_cb));
557 let (cb_fn, cb_data) = to_save.get_capi_params(events::timer::event_cb_proxy);
558
559 let ptr = unsafe {
560 capi::pa_context_rttime_new(self.ptr, (time.0).0, std::mem::transmute(cb_fn), cb_data)
561 };
562 match ptr.is_null() {
563 false => Some(TimeEvent::<T::MI>::from_raw(ptr, mainloop.inner(), to_save)),
564 true => None,
565 }
566 }
567
568 /// Gets the optimal block size for passing around audio buffers.
569 ///
570 /// It is recommended to allocate buffers of the size returned here when writing audio data to
571 /// playback streams, if the latency constraints permit this. It is not recommended writing
572 /// larger blocks than this because usually they will then be split up internally into chunks of
573 /// this size. It is not recommended writing smaller blocks than this (unless required due to
574 /// latency demands) because this increases CPU usage.
575 ///
576 /// If `ss` is `None` you will be returned the byte-exact tile size.
577 ///
578 /// If `ss` is invalid, returns `None`, else returns tile size rounded down to multiple of the
579 /// frame size.
580 ///
581 /// This is supposed to be used in a construct such as:
582 ///
583 /// ```rust,ignore
584 /// let ss = stream.get_sample_spec().unwrap();
585 /// let size = context.get_tile_size(Some(ss)).unwrap();
586 /// ```
587 pub fn get_tile_size(&self, ss: Option<&sample::Spec>) -> Option<usize> {
588 let p_ss = ss.map_or(null::<capi::pa_sample_spec>(), |s| s.as_ref());
589 match unsafe { capi::pa_context_get_tile_size(self.ptr, p_ss) } {
590 std::usize::MAX => None,
591 r => Some(r),
592 }
593 }
594
595 /// Loads the authentication cookie from a file.
596 ///
597 /// This function is primarily meant for PulseAudio’s own tunnel modules, which need to load the
598 /// cookie from a custom location. Applications don’t usually need to care about the cookie at
599 /// all, but if it happens that you know what the authentication cookie is and your application
600 /// needs to load it from a non-standard location, feel free to use this function.
601 pub fn load_cookie_from_file(&mut self, cookie_file_path: &str) -> Result<(), PAErr> {
602 // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
603 // as_ptr() giving dangling pointers!
604 let c_path = CString::new(cookie_file_path).unwrap();
605 match unsafe { capi::pa_context_load_cookie_from_file(self.ptr, c_path.as_ptr()) } {
606 0 => Ok(()),
607 e => Err(PAErr(e)),
608 }
609 }
610}
611
612impl Drop for Context {
613 fn drop(&mut self) {
614 if !self.weak {
615 unsafe { capi::pa_context_unref(self.ptr) };
616 }
617 self.ptr = null_mut::<ContextInternal>();
618 }
619}
620
621/// Proxy for completion success callbacks.
622///
623/// Warning: This is for single-use cases only! It destroys the actual closure callback.
624extern "C"
625fn success_cb_proxy(_: *mut ContextInternal, success: i32, userdata: *mut c_void) {
626 let success_actual = match success { 0 => false, _ => true };
627 let _ = std::panic::catch_unwind(|| {
628 assert!(!userdata.is_null());
629 // Note, destroys closure callback after use - restoring outer box means it gets dropped
630 let mut callback = unsafe { Box::from_raw(userdata as *mut Box<dyn FnMut(bool)>) };
631 (callback)(success_actual);
632 });
633}
634
635/// Proxy for notification callbacks (single use).
636///
637/// Warning: This is for single-use cases only! It destroys the actual closure callback.
638extern "C"
639fn notify_cb_proxy_single(_: *mut ContextInternal, userdata: *mut c_void) {
640 let _ = std::panic::catch_unwind(|| {
641 assert!(!userdata.is_null());
642 // Note, destroys closure callback after use - restoring outer box means it gets dropped
643 let mut callback = unsafe { Box::from_raw(userdata as *mut Box<dyn FnMut()>) };
644 (callback)();
645 });
646}
647
648/// Proxy for notification callbacks (multi use).
649///
650/// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which
651/// must be accomplished separately to avoid a memory leak.
652extern "C"
653fn notify_cb_proxy_multi(_: *mut ContextInternal, userdata: *mut c_void) {
654 let _ = std::panic::catch_unwind(|| {
655 let callback = NotifyCb::get_callback(userdata);
656 (callback)();
657 });
658}
659
660/// Proxy for event callbacks.
661///
662/// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which
663/// must be accomplished separately to avoid a memory leak.
664extern "C"
665fn event_cb_proxy(_: *mut ContextInternal, name: *const c_char, proplist: *mut ProplistInternal,
666 userdata: *mut c_void)
667{
668 let _ = std::panic::catch_unwind(|| {
669 assert!(!name.is_null());
670 let n = {
671 let tmp = unsafe { CStr::from_ptr(name) };
672 tmp.to_string_lossy().into_owned()
673 };
674 let pl = Proplist::from_raw_weak(proplist);
675
676 let callback = EventCb::get_callback(userdata);
677 (callback)(n, pl);
678 });
679}
680
681/// Proxy for extension test callbacks.
682///
683/// Warning: This is for single-use cases only! It destroys the actual closure callback.
684extern "C"
685fn ext_test_cb_proxy(_: *mut ContextInternal, version: u32, userdata: *mut c_void) {
686 let _ = std::panic::catch_unwind(|| {
687 // Note, destroys closure callback after use - restoring outer box means it gets dropped
688 let mut callback = get_su_callback::<dyn FnMut(u32)>(userdata);
689 (callback)(version);
690 });
691}
692
693/// Proxy for extension subscribe callbacks.
694///
695/// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which
696/// must be accomplished separately to avoid a memory leak.
697extern "C"
698fn ext_subscribe_cb_proxy(_: *mut ContextInternal, userdata: *mut c_void) {
699 let _ = std::panic::catch_unwind(|| {
700 let callback = ExtSubscribeCb::get_callback(userdata);
701 (callback)();
702 });
703}