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}