libpulse_binding/context/
introspect.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//! Routines for daemon introspection.
15//!
16//! # Overview
17//!
18//! Sometimes it is necessary to query and modify global settings in the server. For this,
19//! PulseAudio has the introspection API. It can list sinks, sources, samples and other aspects of
20//! the server. It can also modify the attributes of the server that will affect operations on a
21//! global level, and not just the application’s context.
22//!
23//! # Usage
24//!
25//! The introspection routines are exposed as methods on an [`Introspector`] object held by the
26//! [`Context`] object, and can be accessed via the [`Context::introspect()`] method.
27//!
28//! # Querying
29//!
30//! All querying is done through callbacks. This approach is necessary to maintain an asynchronous
31//! design. The client will request the information and some time later, the server will respond
32//! with the desired data.
33//!
34//! Some objects can have multiple instances on the server. When requesting all of these at once,
35//! the callback will be called multiple times, each time with a [`ListResult`] variant. It will be
36//! called once for each item in turn, using the `Item` variant, and then one more time with the
37//! `End` variant to signal that the end of the list has been reached. If an error occurs, then
38//! the `Error` variant will be given.
39//!
40//! Note that even if a single object is requested, and not the entire list, the terminating call
41//! will still be made.
42//!
43//! Data members in the information structures are only valid during the duration of the callback.
44//! If they are required after the callback is finished, a deep copy of the information structure
45//! must be performed.
46//!
47//! # Server Information
48//!
49//! The server can be queried about its name, the environment it’s running on and the currently
50//! active global defaults. Calling [`Introspector::get_server_info()`] provides access to a
51//! [`ServerInfo`] structure containing all of these.
52//!
53//! # Memory Usage
54//!
55//! Statistics about memory usage can be fetched using [`Introspector::stat()`], giving a
56//! [`StatInfo`] structure.
57//!
58//! # Sinks and Sources
59//!
60//! The server can have an arbitrary number of sinks and sources. Each sink and source have both an
61//! index and a name associated with it. As such, there are three ways to get access to them:
62//!
63//! * By index: [`Introspector::get_sink_info_by_index()`],
64//!   [`Introspector::get_source_info_by_index()`]
65//! * By name: [`Introspector::get_sink_info_by_name()`],
66//!   [`Introspector::get_source_info_by_name()`]
67//! * All: [`Introspector::get_sink_info_list()`], [`Introspector::get_source_info_list()`]
68//!
69//! All three methods use the same callback and will provide a [`SinkInfo`] or [`SourceInfo`]
70//! structure.
71//!
72//! # Sink Inputs and Source Outputs
73//!
74//! Sink inputs and source outputs are the representations of the client ends of streams inside the
75//! server. I.e. they connect a client stream to one of the global sinks or sources.
76//!
77//! Sink inputs and source outputs only have an index to identify them. As such, there are only two
78//! ways to get information about them:
79//!
80//! * By index: [`Introspector::get_sink_input_info()`], [`Introspector::get_source_output_info()`]
81//! * All: [`Introspector::get_sink_input_info_list()`],
82//!   [`Introspector::get_source_output_info_list()`]
83//!
84//! The structure returned is the [`SinkInputInfo`] or [`SourceOutputInfo`] structure.
85//!
86//! # Samples
87//!
88//! The list of cached samples can be retrieved from the server. Three methods exist for querying
89//! the sample cache list:
90//!
91//! * By index: [`Introspector::get_sample_info_by_index()`]
92//! * By name: [`Introspector::get_sample_info_by_name()`]
93//! * All: [`Introspector::get_sample_info_list()`]
94//!
95//! Note that this only retrieves information about the sample, not the sample data itself.
96//!
97//! # Driver Modules
98//!
99//! PulseAudio driver modules are identified by index and are retrieved using either
100//! [`Introspector::get_module_info()`] or [`Introspector::get_module_info_list()`]. The information
101//! structure is called [`ModuleInfo`].
102//!
103//! # Clients
104//!
105//! PulseAudio clients are also identified by index and are retrieved using either
106//! [`Introspector::get_client_info()`] or [`Introspector::get_client_info_list()`]. The information
107//! structure is called [`ClientInfo`].
108//!
109//! # Control
110//!
111//! Some parts of the server are only possible to read, but most can also be modified in different
112//! ways. Note that these changes will affect all connected clients and not just the one issuing the
113//! request.
114//!
115//! # Sinks and Sources
116//!
117//! The most common change one would want to apply to sinks and sources is to modify the volume of
118//! the audio. Identically to how sinks and sources can be queried, there are two ways of
119//! identifying them:
120//!
121//! * By index: [`Introspector::set_sink_volume_by_index()`],
122//!   [`Introspector::set_source_volume_by_index()`]
123//! * By name: [`Introspector::set_sink_volume_by_name()`],
124//!   [`Introspector::set_source_volume_by_name()`]
125//!
126//! It is also possible to mute a sink or source:
127//!
128//! * By index: [`Introspector::set_sink_mute_by_index()`],
129//!   [`Introspector::set_source_mute_by_index()`]
130//! * By name: [`Introspector::set_sink_mute_by_name()`],
131//!   [`Introspector::set_source_mute_by_name()`]
132//!
133//! # Sink Inputs and Source Outputs
134//!
135//! If an application desires to modify the volume of just a single stream (commonly one of its own
136//! streams), this can be done by setting the volume of its associated sink input or source output,
137//! using [`Introspector::set_sink_input_volume()`] or [`Introspector::set_source_output_volume()`].
138//!
139//! It is also possible to remove sink inputs and source outputs, terminating the streams associated
140//! with them:
141//!
142//! * Sink input: [`Introspector::kill_sink_input()`]
143//! * Source output: [`Introspector::kill_source_output()`]
144//!
145//! It is strongly recommended that all volume changes are done as a direct result of user input.
146//! With automated requests, such as those resulting from misguided attempts of crossfading,
147//! PulseAudio can store the stream volume at an inappropriate moment and restore it later. Besides,
148//! such attempts lead to OSD popups in some desktop environments.
149//!
150//! As a special case of the general rule above, it is recommended that your application leaves the
151//! task of saving and restoring the volume of its streams to PulseAudio and does not attempt to do
152//! it by itself. PulseAudio really knows better about events such as stream moving or headphone
153//! plugging that would make the volume stored by the application inapplicable to the new
154//! configuration.
155//!
156//! Another important case where setting a sink input volume may be a bad idea is related to
157//! interpreters that interpret potentially untrusted scripts. PulseAudio relies on your application
158//! not making malicious requests (such as repeatedly setting the volume to 100%). Thus, script
159//! interpreters that represent a security boundary must sandbox volume-changing requests coming
160//! from their scripts. In the worst case, it may be necessary to apply the script-requested volume
161//! to the script-produced sounds by altering the samples in the script interpreter and not touching
162//! the sink or sink input volume as seen by PulseAudio.
163//!
164//! If an application changes any volume, it should also listen to changes of the same volume
165//! originating from outside the application (e.g., from the system mixer application) and update
166//! its user interface accordingly. Use [`Context::subscribe()`] to get such notifications.
167//!
168//! # Modules
169//!
170//! Server modules can be remotely loaded and unloaded using [`Introspector::load_module()`] and
171//! [`Introspector::unload_module()`].
172//!
173//! # Messages
174//!
175//! Server objects like sinks, sink inputs or modules can register a message handler to communicate
176//! with clients. A message can be sent to a named message handler using
177//! [`Introspector::send_message_to_object()`].
178//!
179//! # Clients
180//!
181//! The only operation supported on clients is the possibility of kicking them off the server using
182//! [`Introspector::kill_client()`].
183
184use std::os::raw::c_void;
185#[cfg(any(doc, feature = "pa_v15"))]
186use std::os::raw::c_char;
187use std::ffi::{CStr, CString};
188use std::borrow::Cow;
189use std::ptr::null_mut;
190use num_traits::FromPrimitive;
191use capi::pa_sink_port_info as SinkPortInfoInternal;
192use capi::pa_sink_info as SinkInfoInternal;
193use capi::pa_source_port_info as SourcePortInfoInternal;
194use capi::pa_source_info as SourceInfoInternal;
195use capi::pa_server_info as ServerInfoInternal;
196use capi::pa_module_info as ModuleInfoInternal;
197use capi::pa_client_info as ClientInfoInternal;
198use capi::pa_card_profile_info2 as CardProfileInfoInternal;
199use capi::pa_card_port_info as CardPortInfoInternal;
200use capi::pa_card_info as CardInfoInternal;
201use capi::pa_sink_input_info as SinkInputInfoInternal;
202use capi::pa_source_output_info as SourceOutputInfoInternal;
203use capi::pa_sample_info as SampleInfoInternal;
204use super::{Context, ContextInternal};
205use crate::{def, sample, channelmap, format, direction};
206use crate::time::MicroSeconds;
207use crate::callbacks::{
208    ListResult, box_closure_get_capi_ptr, callback_for_list_instance, get_su_capi_params,
209    get_su_callback
210};
211use crate::volume::{ChannelVolumes, Volume};
212use crate::{operation::Operation, proplist::Proplist};
213#[cfg(any(doc, feature = "pa_v14"))]
214use crate::def::DevicePortType;
215
216pub use capi::pa_stat_info as StatInfo;
217
218/// A wrapper object providing introspection routines to a context.
219pub struct Introspector {
220    context: *mut super::ContextInternal,
221}
222
223unsafe impl Send for Introspector {}
224unsafe impl Sync for Introspector {}
225
226impl Context {
227    /// Gets an introspection object linked to the current context, giving access to introspection
228    /// routines.
229    ///
230    /// See [`context::introspect`](mod@crate::context::introspect).
231    #[inline]
232    pub fn introspect(&self) -> Introspector {
233        unsafe { capi::pa_context_ref(self.ptr) };
234        Introspector::from_raw(self.ptr)
235    }
236}
237
238impl Introspector {
239    /// Creates a new `Introspector` from an existing [`ContextInternal`] pointer.
240    #[inline(always)]
241    fn from_raw(context: *mut ContextInternal) -> Self {
242        Self { context: context }
243    }
244}
245
246impl Drop for Introspector {
247    fn drop(&mut self) {
248        unsafe { capi::pa_context_unref(self.context) };
249        self.context = null_mut::<super::ContextInternal>();
250    }
251}
252
253////////////////////////////////////////////////////////////////////////////////////////////////////
254// Sink info
255////////////////////////////////////////////////////////////////////////////////////////////////////
256
257/// Stores information about a specific port of a sink.
258///
259/// Please note that this structure can be extended as part of evolutionary API updates at any time
260/// in any new release.
261#[derive(Debug)]
262pub struct SinkPortInfo<'a> {
263    /// Name of this port.
264    pub name: Option<Cow<'a, str>>,
265    /// Description of this port.
266    pub description: Option<Cow<'a, str>>,
267    /// The higher this value is, the more useful this port is as a default.
268    pub priority: u32,
269    /// A flag indicating availability status of this port.
270    pub available: def::PortAvailable,
271    /// An indentifier for the group of ports that share their availability status with each other.
272    ///
273    /// This is meant especially for handling cases where one 3.5 mm connector is used for
274    /// headphones, headsets and microphones, and the hardware can only tell that something was
275    /// plugged in but not what exactly. In this situation the ports for all those devices share
276    /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and
277    /// some application may ask the user what was plugged in. Such applications should get a list
278    /// of all card ports and compare their `availability_group` fields. Ports that have the same
279    /// group are those that need input from the user to determine which device was plugged in. The
280    /// application should then activate the user-chosen port.
281    ///
282    /// May be `None`, in which case the port is not part of any availability group (which is the
283    /// same as having a group with only one member).
284    ///
285    /// The group identifier must be treated as an opaque identifier. The string may look like an
286    /// ALSA control name, but applications must not assume any such relationship. The group naming
287    /// scheme can change without a warning.
288    ///
289    /// Since one group can include both input and output ports, the grouping should be done using
290    /// `CardPortInfo` instead of `SinkPortInfo`, but this field is duplicated also in
291    /// `SinkPortInfo` (and `SourcePortInfo`) in case someone finds that convenient.
292    #[cfg(any(doc, feature = "pa_v14"))]
293    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
294    pub availability_group: Option<Cow<'a, str>>,
295    /// Port device type.
296    #[cfg(any(doc, feature = "pa_v14"))]
297    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
298    pub r#type: DevicePortType,
299}
300
301impl<'a> SinkPortInfo<'a> {
302    fn new_from_raw(p: *const SinkPortInfoInternal) -> Self {
303        assert!(!p.is_null());
304        let src = unsafe { &*p };
305        unsafe {
306            SinkPortInfo {
307                name: match src.name.is_null() {
308                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
309                    true => None,
310                },
311                description: match src.description.is_null() {
312                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
313                    true => None,
314                },
315                priority: src.priority,
316                available: def::PortAvailable::from_i32(src.available).unwrap(),
317                #[cfg(any(doc, feature = "pa_v14"))]
318                availability_group: match src.availability_group.is_null() {
319                    false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()),
320                    true => None,
321                },
322                #[cfg(any(doc, feature = "pa_v14"))]
323                r#type: DevicePortType::from_u32(src.r#type).unwrap(),
324            }
325        }
326    }
327}
328
329/// Stores information about sinks.
330///
331/// Please note that this structure can be extended as part of evolutionary API updates at any time
332/// in any new release.
333#[derive(Debug)]
334pub struct SinkInfo<'a> {
335    /// Name of the sink.
336    pub name: Option<Cow<'a, str>>,
337    /// Index of the sink.
338    pub index: u32,
339    /// Description of this sink.
340    pub description: Option<Cow<'a, str>>,
341    /// Sample spec of this sink.
342    pub sample_spec: sample::Spec,
343    /// Channel map.
344    pub channel_map: channelmap::Map,
345    /// Index of the owning module of this sink, or `None` if is invalid.
346    pub owner_module: Option<u32>,
347    /// Volume of the sink.
348    pub volume: ChannelVolumes,
349    /// Mute switch of the sink.
350    pub mute: bool,
351    /// Index of the monitor source connected to this sink.
352    pub monitor_source: u32,
353    /// The name of the monitor source.
354    pub monitor_source_name: Option<Cow<'a, str>>,
355    /// Length of queued audio in the output buffer.
356    pub latency: MicroSeconds,
357    /// Driver name.
358    pub driver: Option<Cow<'a, str>>,
359    /// Flags.
360    pub flags: def::SinkFlagSet,
361    /// Property list.
362    pub proplist: Proplist,
363    /// The latency this device has been configured to.
364    pub configured_latency: MicroSeconds,
365    /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of
366    /// the output device.
367    pub base_volume: Volume,
368    /// State.
369    pub state: def::SinkState,
370    /// Number of volume steps for sinks which do not support arbitrary volumes.
371    pub n_volume_steps: u32,
372    /// Card index, or `None` if invalid.
373    pub card: Option<u32>,
374    /// Set of available ports.
375    pub ports: Vec<SinkPortInfo<'a>>,
376    /// Pointer to active port in the set, or `None`.
377    pub active_port: Option<Box<SinkPortInfo<'a>>>,
378    /// Set of formats supported by the sink.
379    pub formats: Vec<format::Info>,
380}
381
382impl<'a> SinkInfo<'a> {
383    fn new_from_raw(p: *const SinkInfoInternal) -> Self {
384        assert!(!p.is_null());
385        let src = unsafe { &*p };
386
387        let mut port_vec = Vec::with_capacity(src.n_ports as usize);
388        assert!(src.n_ports == 0 || !src.ports.is_null());
389        for i in 0..src.n_ports as isize {
390            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SinkPortInfoInternal };
391            if !indexed_ptr.is_null() {
392                port_vec.push(SinkPortInfo::new_from_raw(indexed_ptr));
393            }
394        }
395        let mut formats_vec = Vec::with_capacity(src.n_formats as usize);
396        assert!(src.n_formats == 0 || !src.formats.is_null());
397        for i in 0..src.n_formats as isize {
398            let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal };
399            if !indexed_ptr.is_null() {
400                formats_vec.push(format::Info::from_raw_weak(indexed_ptr));
401            }
402        }
403
404        unsafe {
405            SinkInfo {
406                name: match src.name.is_null() {
407                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
408                    true => None,
409                },
410                index: src.index,
411                description: match src.description.is_null() {
412                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
413                    true => None,
414                },
415                sample_spec: src.sample_spec.into(),
416                channel_map: src.channel_map.into(),
417                owner_module: match src.owner_module {
418                    def::INVALID_INDEX => None,
419                    i => Some(i),
420                },
421                volume: src.volume.into(),
422                mute: match src.mute {
423                    0 => false,
424                    _ => true,
425                },
426                monitor_source: src.monitor_source,
427                monitor_source_name: match src.monitor_source_name.is_null() {
428                    false => Some(CStr::from_ptr(src.monitor_source_name).to_string_lossy()),
429                    true => None,
430                },
431                latency: MicroSeconds(src.latency),
432                driver: match src.driver.is_null() {
433                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
434                    true => None,
435                },
436                flags: def::SinkFlagSet::from_bits_truncate(src.flags),
437                proplist: Proplist::from_raw_weak(src.proplist),
438                configured_latency: MicroSeconds(src.configured_latency),
439                base_volume: Volume(src.base_volume),
440                state: src.state.into(),
441                n_volume_steps: src.n_volume_steps,
442                card: match src.card {
443                    def::INVALID_INDEX => None,
444                    i => Some(i),
445                },
446                ports: port_vec,
447                active_port: match src.active_port.is_null() {
448                    true => None,
449                    false => Some(Box::new(SinkPortInfo::new_from_raw(src.active_port))),
450                },
451                formats: formats_vec,
452            }
453        }
454    }
455}
456
457impl Introspector {
458    /// Gets information about a sink by its name.
459    ///
460    /// Panics on error, i.e. invalid arguments or state.
461    pub fn get_sink_info_by_name<F>(&self, name: &str, callback: F)
462        -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
463        where F: FnMut(ListResult<&SinkInfo>) + 'static
464    {
465        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
466        // as_ptr() giving dangling pointers!
467        let c_name = CString::new(name).unwrap();
468
469        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
470        let ptr = unsafe { capi::pa_context_get_sink_info_by_name(self.context, c_name.as_ptr(),
471            Some(get_sink_info_list_cb_proxy), cb_data) };
472        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
473    }
474
475    /// Gets information about a sink by its index.
476    ///
477    /// Panics on error, i.e. invalid arguments or state.
478    pub fn get_sink_info_by_index<F>(&self, index: u32, callback: F)
479        -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
480        where F: FnMut(ListResult<&SinkInfo>) + 'static
481    {
482        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
483        let ptr = unsafe { capi::pa_context_get_sink_info_by_index(self.context, index,
484            Some(get_sink_info_list_cb_proxy), cb_data) };
485        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
486    }
487
488    /// Gets the complete sink list.
489    ///
490    /// Panics on error, i.e. invalid arguments or state.
491    pub fn get_sink_info_list<F>(&self, callback: F) -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
492        where F: FnMut(ListResult<&SinkInfo>) + 'static
493    {
494        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
495        let ptr = unsafe { capi::pa_context_get_sink_info_list(self.context,
496            Some(get_sink_info_list_cb_proxy), cb_data) };
497        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
498    }
499
500    /// Sets the volume of a sink device specified by its index.
501    ///
502    /// Panics on error, i.e. invalid arguments or state.
503    ///
504    /// The optional callback must accept a `bool`, which indicates success.
505    pub fn set_sink_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes,
506        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
507    {
508        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
509            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
510        let ptr = unsafe { capi::pa_context_set_sink_volume_by_index(self.context, index,
511            volume.as_ref(), cb_fn, cb_data) };
512        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
513    }
514
515    /// Sets the volume of a sink device specified by its name.
516    ///
517    /// Panics on error, i.e. invalid arguments or state.
518    ///
519    /// The optional callback must accept a `bool`, which indicates success.
520    pub fn set_sink_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes,
521        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
522    {
523        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
524        // as_ptr() giving dangling pointers!
525        let c_name = CString::new(name).unwrap();
526
527        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
528            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
529        let ptr = unsafe { capi::pa_context_set_sink_volume_by_name(self.context, c_name.as_ptr(),
530            volume.as_ref(), cb_fn, cb_data) };
531        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
532    }
533
534    /// Sets the mute switch of a sink device specified by its index.
535    ///
536    /// Panics on error, i.e. invalid arguments or state.
537    ///
538    /// The optional callback must accept a `bool`, which indicates success.
539    pub fn set_sink_mute_by_index(&mut self, index: u32, mute: bool,
540        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
541    {
542        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
543            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
544        let ptr = unsafe { capi::pa_context_set_sink_mute_by_index(self.context, index, mute as i32,
545            cb_fn, cb_data) };
546        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
547    }
548
549    /// Sets the mute switch of a sink device specified by its name.
550    ///
551    /// Panics on error, i.e. invalid arguments or state.
552    ///
553    /// The optional callback must accept a `bool`, which indicates success.
554    pub fn set_sink_mute_by_name(&mut self, name: &str, mute: bool,
555        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
556    {
557        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
558        // as_ptr() giving dangling pointers!
559        let c_name = CString::new(name).unwrap();
560
561        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
562            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
563        let ptr = unsafe { capi::pa_context_set_sink_mute_by_name(self.context, c_name.as_ptr(),
564            mute as i32, cb_fn, cb_data) };
565        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
566    }
567
568    /// Suspends/Resumes a sink.
569    ///
570    /// Panics on error, i.e. invalid arguments or state.
571    ///
572    /// The optional callback must accept a `bool`, which indicates success.
573    pub fn suspend_sink_by_name(&mut self, sink_name: &str, suspend: bool,
574        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
575    {
576        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
577        // as_ptr() giving dangling pointers!
578        let c_name = CString::new(sink_name).unwrap();
579
580        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
581            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
582        let ptr = unsafe { capi::pa_context_suspend_sink_by_name(self.context, c_name.as_ptr(),
583            suspend as i32, cb_fn, cb_data) };
584        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
585    }
586
587    /// Suspends/Resumes a sink.
588    ///
589    /// If `index` is [`def::INVALID_INDEX`] all sinks will be suspended.
590    /// Panics on error, i.e. invalid arguments or state.
591    ///
592    /// The optional callback must accept a `bool`, which indicates success.
593    pub fn suspend_sink_by_index(&mut self, index: u32, suspend: bool,
594        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
595    {
596        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
597            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
598        let ptr = unsafe { capi::pa_context_suspend_sink_by_index(self.context, index,
599            suspend as i32, cb_fn, cb_data) };
600        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
601    }
602
603    /// Changes the profile of a sink.
604    ///
605    /// Panics on error, i.e. invalid arguments or state.
606    ///
607    /// The optional callback must accept a `bool`, which indicates success.
608    pub fn set_sink_port_by_index(&mut self, index: u32, port: &str,
609        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
610    {
611        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
612        // as_ptr() giving dangling pointers!
613        let c_port = CString::new(port).unwrap();
614
615        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
616            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
617        let ptr = unsafe { capi::pa_context_set_sink_port_by_index(self.context, index,
618            c_port.as_ptr(), cb_fn, cb_data) };
619        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
620    }
621
622    /// Changes the profile of a sink.
623    ///
624    /// Panics on error, i.e. invalid arguments or state.
625    ///
626    /// The optional callback must accept a `bool`, which indicates success.
627    pub fn set_sink_port_by_name(&mut self, name: &str, port: &str,
628        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
629    {
630        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
631        // as_ptr() giving dangling pointers!
632        let c_name = CString::new(name).unwrap();
633        let c_port = CString::new(port).unwrap();
634
635        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
636            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
637        let ptr = unsafe { capi::pa_context_set_sink_port_by_name(self.context, c_name.as_ptr(),
638            c_port.as_ptr(), cb_fn, cb_data) };
639        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
640    }
641}
642
643/// Proxy for get sink info list callbacks.
644/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
645extern "C"
646fn get_sink_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInfoInternal, eol: i32,
647    userdata: *mut c_void)
648{
649    let _ = std::panic::catch_unwind(|| {
650        callback_for_list_instance(i, eol, userdata, SinkInfo::new_from_raw);
651    });
652}
653
654////////////////////////////////////////////////////////////////////////////////////////////////////
655// Source info
656////////////////////////////////////////////////////////////////////////////////////////////////////
657
658/// Stores information about a specific port of a source.
659///
660/// Please note that this structure can be extended as part of evolutionary API updates at any time
661/// in any new release.
662#[derive(Debug)]
663pub struct SourcePortInfo<'a> {
664    /// Name of this port.
665    pub name: Option<Cow<'a, str>>,
666    /// Description of this port.
667    pub description: Option<Cow<'a, str>>,
668    /// The higher this value is, the more useful this port is as a default.
669    pub priority: u32,
670    /// A flag indicating availability status of this port.
671    pub available: def::PortAvailable,
672    /// An indentifier for the group of ports that share their availability status with each other.
673    ///
674    /// This is meant especially for handling cases where one 3.5 mm connector is used for
675    /// headphones, headsets and microphones, and the hardware can only tell that something was
676    /// plugged in but not what exactly. In this situation the ports for all those devices share
677    /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and
678    /// some application may ask the user what was plugged in. Such applications should get a list
679    /// of all card ports and compare their `availability_group` fields. Ports that have the same
680    /// group are those that need input from the user to determine which device was plugged in. The
681    /// application should then activate the user-chosen port.
682    ///
683    /// May be `None`, in which case the port is not part of any availability group (which is the
684    /// same as having a group with only one member).
685    ///
686    /// The group identifier must be treated as an opaque identifier. The string may look like an
687    /// ALSA control name, but applications must not assume any such relationship. The group naming
688    /// scheme can change without a warning.
689    ///
690    /// Since one group can include both input and output ports, the grouping should be done using
691    /// `CardPortInfo` instead of `SourcePortInfo`, but this field is duplicated also in
692    /// `SourcePortInfo` (and `SinkPortInfo`) in case someone finds that convenient.
693    #[cfg(any(doc, feature = "pa_v14"))]
694    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
695    pub availability_group: Option<Cow<'a, str>>,
696    /// Port device type.
697    #[cfg(any(doc, feature = "pa_v14"))]
698    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
699    pub r#type: DevicePortType,
700}
701
702impl<'a> SourcePortInfo<'a> {
703    fn new_from_raw(p: *const SourcePortInfoInternal) -> Self {
704        assert!(!p.is_null());
705        let src = unsafe { &*p };
706        unsafe {
707            SourcePortInfo {
708                name: match src.name.is_null() {
709                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
710                    true => None,
711                },
712                description: match src.description.is_null() {
713                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
714                    true => None,
715                },
716                priority: src.priority,
717                available: def::PortAvailable::from_i32(src.available).unwrap(),
718                #[cfg(any(doc, feature = "pa_v14"))]
719                availability_group: match src.availability_group.is_null() {
720                    false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()),
721                    true => None,
722                },
723                #[cfg(any(doc, feature = "pa_v14"))]
724                r#type: DevicePortType::from_u32(src.r#type).unwrap(),
725            }
726        }
727    }
728}
729
730/// Stores information about sources.
731///
732/// Please note that this structure can be extended as part of evolutionary API updates at any time
733/// in any new release.
734#[derive(Debug)]
735pub struct SourceInfo<'a> {
736    /// Name of the source.
737    pub name: Option<Cow<'a, str>>,
738    /// Index of the source.
739    pub index: u32,
740    /// Description of this source.
741    pub description: Option<Cow<'a, str>>,
742    /// Sample spec of this source.
743    pub sample_spec: sample::Spec,
744    /// Channel map.
745    pub channel_map: channelmap::Map,
746    /// Owning module index, or `None`.
747    pub owner_module: Option<u32>,
748    /// Volume of the source.
749    pub volume: ChannelVolumes,
750    /// Mute switch of the sink.
751    pub mute: bool,
752    /// If this is a monitor source, the index of the owning sink, otherwise `None`.
753    pub monitor_of_sink: Option<u32>,
754    /// Name of the owning sink, or `None`.
755    pub monitor_of_sink_name: Option<Cow<'a, str>>,
756    /// Length of filled record buffer of this source.
757    pub latency: MicroSeconds,
758    /// Driver name.
759    pub driver: Option<Cow<'a, str>>,
760    /// Flags.
761    pub flags: def::SourceFlagSet,
762    /// Property list.
763    pub proplist: Proplist,
764    /// The latency this device has been configured to.
765    pub configured_latency: MicroSeconds,
766    /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of
767    /// the input device.
768    pub base_volume: Volume,
769    /// State.
770    pub state: def::SourceState,
771    /// Number of volume steps for sources which do not support arbitrary volumes.
772    pub n_volume_steps: u32,
773    /// Card index, or `None`.
774    pub card: Option<u32>,
775    /// Set of available ports.
776    pub ports: Vec<SourcePortInfo<'a>>,
777    /// Pointer to active port in the set, or `None`.
778    pub active_port: Option<Box<SourcePortInfo<'a>>>,
779    /// Set of formats supported by the sink.
780    pub formats: Vec<format::Info>,
781}
782
783impl<'a> SourceInfo<'a> {
784    fn new_from_raw(p: *const SourceInfoInternal) -> Self {
785        assert!(!p.is_null());
786        let src = unsafe { &*p };
787
788        let mut port_vec = Vec::with_capacity(src.n_ports as usize);
789        assert!(src.n_ports == 0 || !src.ports.is_null());
790        for i in 0..src.n_ports as isize {
791            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SourcePortInfoInternal };
792            if !indexed_ptr.is_null() {
793                port_vec.push(SourcePortInfo::new_from_raw(indexed_ptr));
794            }
795        }
796        let mut formats_vec = Vec::with_capacity(src.n_formats as usize);
797        assert!(src.n_formats == 0 || !src.formats.is_null());
798        for i in 0..src.n_formats as isize {
799            let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal };
800            if !indexed_ptr.is_null() {
801                formats_vec.push(format::Info::from_raw_weak(indexed_ptr));
802            }
803        }
804
805        unsafe {
806            SourceInfo {
807                name: match src.name.is_null() {
808                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
809                    true => None,
810                },
811                index: src.index,
812                description: match src.description.is_null() {
813                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
814                    true => None,
815                },
816                sample_spec: src.sample_spec.into(),
817                channel_map: src.channel_map.into(),
818                owner_module: match src.owner_module {
819                    def::INVALID_INDEX => None,
820                    i => Some(i),
821                },
822                volume: src.volume.into(),
823                mute: match src.mute {
824                    0 => false,
825                    _ => true,
826                },
827                monitor_of_sink: match src.monitor_of_sink {
828                    def::INVALID_INDEX => None,
829                    i => Some(i),
830                },
831                monitor_of_sink_name: match src.monitor_of_sink_name.is_null() {
832                    false => Some(CStr::from_ptr(src.monitor_of_sink_name).to_string_lossy()),
833                    true => None,
834                },
835                latency: MicroSeconds(src.latency),
836                driver: match src.driver.is_null() {
837                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
838                    true => None,
839                },
840                flags: def::SourceFlagSet::from_bits_truncate(src.flags),
841                proplist: Proplist::from_raw_weak(src.proplist),
842                configured_latency: MicroSeconds(src.configured_latency),
843                base_volume: Volume(src.base_volume),
844                state: src.state.into(),
845                n_volume_steps: src.n_volume_steps,
846                card: match src.card {
847                    def::INVALID_INDEX => None,
848                    i => Some(i),
849                },
850                ports: port_vec,
851                active_port: match src.active_port.is_null() {
852                    true => None,
853                    false => Some(Box::new(SourcePortInfo::new_from_raw(src.active_port))),
854                },
855                formats: formats_vec,
856            }
857        }
858    }
859}
860
861impl Introspector {
862    /// Gets information about a source by its name.
863    ///
864    /// Panics on error, i.e. invalid arguments or state.
865    pub fn get_source_info_by_name<F>(&self, name: &str, callback: F)
866        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
867        where F: FnMut(ListResult<&SourceInfo>) + 'static
868    {
869        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
870        // as_ptr() giving dangling pointers!
871        let c_name = CString::new(name).unwrap();
872
873        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
874        let ptr = unsafe { capi::pa_context_get_source_info_by_name(self.context, c_name.as_ptr(),
875            Some(get_source_info_list_cb_proxy), cb_data) };
876        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
877    }
878
879    /// Gets information about a source by its index.
880    ///
881    /// Panics on error, i.e. invalid arguments or state.
882    pub fn get_source_info_by_index<F>(&self, index: u32, callback: F)
883        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
884        where F: FnMut(ListResult<&SourceInfo>) + 'static
885    {
886        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
887        let ptr = unsafe { capi::pa_context_get_source_info_by_index(self.context, index,
888            Some(get_source_info_list_cb_proxy), cb_data) };
889        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
890    }
891
892    /// Gets the complete source list.
893    ///
894    /// Panics on error, i.e. invalid arguments or state.
895    pub fn get_source_info_list<F>(&self, callback: F)
896        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
897        where F: FnMut(ListResult<&SourceInfo>) + 'static
898    {
899        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
900        let ptr = unsafe { capi::pa_context_get_source_info_list(self.context,
901            Some(get_source_info_list_cb_proxy), cb_data) };
902        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
903    }
904
905    /// Sets the volume of a source device specified by its index.
906    ///
907    /// Panics on error, i.e. invalid arguments or state.
908    ///
909    /// The optional callback must accept a `bool`, which indicates success.
910    pub fn set_source_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes,
911        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
912    {
913        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
914            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
915        let ptr = unsafe { capi::pa_context_set_source_volume_by_index(self.context, index,
916            volume.as_ref(), cb_fn, cb_data) };
917        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
918    }
919
920    /// Sets the volume of a source device specified by its name.
921    ///
922    /// Panics on error, i.e. invalid arguments or state.
923    ///
924    /// The optional callback must accept a `bool`, which indicates success.
925    pub fn set_source_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes,
926        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
927    {
928        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
929        // as_ptr() giving dangling pointers!
930        let c_name = CString::new(name).unwrap();
931
932        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
933            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
934        let ptr = unsafe { capi::pa_context_set_source_volume_by_name(self.context,
935            c_name.as_ptr(), volume.as_ref(), cb_fn, cb_data) };
936        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
937    }
938
939    /// Sets the mute switch of a source device specified by its index.
940    ///
941    /// Panics on error, i.e. invalid arguments or state.
942    ///
943    /// The optional callback must accept a `bool`, which indicates success.
944    pub fn set_source_mute_by_index(&mut self, index: u32, mute: bool,
945        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
946    {
947        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
948            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
949        let ptr = unsafe { capi::pa_context_set_source_mute_by_index(self.context, index,
950            mute as i32, cb_fn, cb_data) };
951        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
952    }
953
954    /// Sets the mute switch of a source device specified by its name.
955    ///
956    /// Panics on error, i.e. invalid arguments or state.
957    ///
958    /// The optional callback must accept a `bool`, which indicates success.
959    pub fn set_source_mute_by_name(&mut self, name: &str, mute: bool,
960        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
961    {
962        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
963        // as_ptr() giving dangling pointers!
964        let c_name = CString::new(name).unwrap();
965
966        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
967            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
968        let ptr = unsafe { capi::pa_context_set_source_mute_by_name(self.context, c_name.as_ptr(),
969            mute as i32, cb_fn, cb_data) };
970        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
971    }
972
973    /// Suspends/Resumes a source.
974    ///
975    /// Panics on error, i.e. invalid arguments or state.
976    ///
977    /// The optional callback must accept a `bool`, which indicates success.
978    pub fn suspend_source_by_name(&mut self, name: &str, suspend: bool,
979        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
980    {
981        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
982        // as_ptr() giving dangling pointers!
983        let c_name = CString::new(name).unwrap();
984
985        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
986            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
987        let ptr = unsafe { capi::pa_context_suspend_source_by_name(self.context, c_name.as_ptr(),
988            suspend as i32, cb_fn, cb_data) };
989        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
990    }
991
992    /// Suspends/Resumes a source.
993    ///
994    /// If `index` is [`def::INVALID_INDEX`], all sources will be suspended.
995    /// Panics on error, i.e. invalid arguments or state.
996    ///
997    /// The optional callback must accept a `bool`, which indicates success.
998    pub fn suspend_source_by_index(&mut self, index: u32, suspend: bool,
999        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1000    {
1001        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1002            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1003        let ptr = unsafe { capi::pa_context_suspend_source_by_index(self.context, index,
1004            suspend as i32, cb_fn, cb_data) };
1005        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1006    }
1007
1008    /// Changes the profile of a source.
1009    ///
1010    /// Panics on error, i.e. invalid arguments or state.
1011    ///
1012    /// The optional callback must accept a `bool`, which indicates success.
1013    pub fn set_source_port_by_index(&mut self, index: u32, port: &str,
1014        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1015    {
1016        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1017        // as_ptr() giving dangling pointers!
1018        let c_port = CString::new(port).unwrap();
1019
1020        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1021            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1022        let ptr = unsafe { capi::pa_context_set_source_port_by_index(self.context, index,
1023            c_port.as_ptr(), cb_fn, cb_data) };
1024        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1025    }
1026
1027    /// Changes the profile of a source.
1028    ///
1029    /// Panics on error, i.e. invalid arguments or state.
1030    ///
1031    /// The optional callback must accept a `bool`, which indicates success.
1032    pub fn set_source_port_by_name(&mut self, name: &str, port: &str,
1033        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1034    {
1035        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1036        // as_ptr() giving dangling pointers!
1037        let c_name = CString::new(name).unwrap();
1038        let c_port = CString::new(port).unwrap();
1039
1040        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1041            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1042        let ptr = unsafe { capi::pa_context_set_source_port_by_name(self.context, c_name.as_ptr(),
1043            c_port.as_ptr(), cb_fn, cb_data) };
1044        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1045    }
1046}
1047
1048/// Proxy for get source info list callbacks.
1049///
1050/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1051extern "C"
1052fn get_source_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceInfoInternal, eol: i32,
1053    userdata: *mut c_void)
1054{
1055    let _ = std::panic::catch_unwind(|| {
1056        callback_for_list_instance(i, eol, userdata, SourceInfo::new_from_raw);
1057    });
1058}
1059
1060////////////////////////////////////////////////////////////////////////////////////////////////////
1061// Server info
1062////////////////////////////////////////////////////////////////////////////////////////////////////
1063
1064/// Server information.
1065///
1066/// Please note that this structure can be extended as part of evolutionary API updates at any time
1067/// in any new release.
1068#[derive(Debug)]
1069pub struct ServerInfo<'a> {
1070    /// User name of the daemon process.
1071    pub user_name: Option<Cow<'a, str>>,
1072    /// Host name the daemon is running on.
1073    pub host_name: Option<Cow<'a, str>>,
1074    /// Version string of the daemon.
1075    pub server_version: Option<Cow<'a, str>>,
1076    /// Server package name (usually “pulseaudio”).
1077    pub server_name: Option<Cow<'a, str>>,
1078    /// Default sample specification.
1079    pub sample_spec: sample::Spec,
1080    /// Name of default sink.
1081    pub default_sink_name: Option<Cow<'a, str>>,
1082    /// Name of default source.
1083    pub default_source_name: Option<Cow<'a, str>>,
1084    /// A random cookie for identifying this instance of PulseAudio.
1085    pub cookie: u32,
1086    /// Default channel map.
1087    pub channel_map: channelmap::Map,
1088}
1089
1090impl<'a> ServerInfo<'a> {
1091    fn new_from_raw(p: *const ServerInfoInternal) -> Self {
1092        assert!(!p.is_null());
1093        let src = unsafe { &*p };
1094        unsafe {
1095            ServerInfo {
1096                user_name: match src.user_name.is_null() {
1097                    false => Some(CStr::from_ptr(src.user_name).to_string_lossy()),
1098                    true => None,
1099                },
1100                host_name: match src.host_name.is_null() {
1101                    false => Some(CStr::from_ptr(src.host_name).to_string_lossy()),
1102                    true => None,
1103                },
1104                server_version: match src.server_version.is_null() {
1105                    false => Some(CStr::from_ptr(src.server_version).to_string_lossy()),
1106                    true => None,
1107                },
1108                server_name: match src.server_name.is_null() {
1109                    false => Some(CStr::from_ptr(src.server_name).to_string_lossy()),
1110                    true => None,
1111                },
1112                sample_spec: src.sample_spec.into(),
1113                default_sink_name: match src.default_sink_name.is_null() {
1114                    false => Some(CStr::from_ptr(src.default_sink_name).to_string_lossy()),
1115                    true => None,
1116                },
1117                default_source_name: match src.default_source_name.is_null() {
1118                    false => Some(CStr::from_ptr(src.default_source_name).to_string_lossy()),
1119                    true => None,
1120                },
1121                cookie: src.cookie,
1122                channel_map: src.channel_map.into(),
1123            }
1124        }
1125    }
1126}
1127
1128impl Introspector {
1129    /// Gets some information about the server.
1130    ///
1131    /// Panics on error, i.e. invalid arguments or state.
1132    pub fn get_server_info<F>(&self, callback: F) -> Operation<dyn FnMut(&ServerInfo)>
1133        where F: FnMut(&ServerInfo) + 'static
1134    {
1135        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(&ServerInfo)>(Box::new(callback));
1136        let ptr = unsafe { capi::pa_context_get_server_info(self.context,
1137            Some(get_server_info_cb_proxy), cb_data) };
1138        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(&ServerInfo)>)
1139    }
1140}
1141
1142/// Proxy for get server info callbacks.
1143/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1144extern "C"
1145fn get_server_info_cb_proxy(_: *mut ContextInternal, i: *const ServerInfoInternal,
1146    userdata: *mut c_void)
1147{
1148    let _ = std::panic::catch_unwind(|| {
1149        assert!(!i.is_null());
1150        let obj = ServerInfo::new_from_raw(i);
1151
1152        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1153        let mut callback = get_su_callback::<dyn FnMut(&ServerInfo)>(userdata);
1154        (callback)(&obj);
1155    });
1156}
1157
1158////////////////////////////////////////////////////////////////////////////////////////////////////
1159// Module info
1160////////////////////////////////////////////////////////////////////////////////////////////////////
1161
1162/// Stores information about modules.
1163///
1164/// Please note that this structure can be extended as part of evolutionary API updates at any time
1165/// in any new release.
1166#[derive(Debug)]
1167pub struct ModuleInfo<'a> {
1168    /// Index of the module.
1169    pub index: u32,
1170    /// Name of the module.
1171    pub name: Option<Cow<'a, str>>,
1172    /// Argument string of the module.
1173    pub argument: Option<Cow<'a, str>>,
1174    /// Usage counter or `None` if invalid.
1175    pub n_used: Option<u32>,
1176    /// Property list.
1177    pub proplist: Proplist,
1178}
1179
1180impl<'a> ModuleInfo<'a> {
1181    fn new_from_raw(p: *const ModuleInfoInternal) -> Self {
1182        assert!(!p.is_null());
1183        let src = unsafe { &*p };
1184        unsafe {
1185            ModuleInfo {
1186                index: src.index,
1187                name: match src.name.is_null() {
1188                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1189                    true => None,
1190                },
1191                argument: match src.argument.is_null() {
1192                    false => Some(CStr::from_ptr(src.argument).to_string_lossy()),
1193                    true => None,
1194                },
1195                n_used: match src.n_used {
1196                    def::INVALID_INDEX => None,
1197                    i => Some(i),
1198                },
1199                proplist: Proplist::from_raw_weak(src.proplist),
1200            }
1201        }
1202    }
1203}
1204
1205impl Introspector {
1206    /// Gets some information about a module by its index.
1207    ///
1208    /// Panics on error, i.e. invalid arguments or state.
1209    pub fn get_module_info<F>(&self, index: u32, callback: F)
1210        -> Operation<dyn FnMut(ListResult<&ModuleInfo>)>
1211        where F: FnMut(ListResult<&ModuleInfo>) + 'static
1212    {
1213        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ModuleInfo>)>(Box::new(callback));
1214        let ptr = unsafe { capi::pa_context_get_module_info(self.context, index,
1215            Some(mod_info_list_cb_proxy), cb_data) };
1216        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ModuleInfo>)>)
1217    }
1218
1219    /// Gets the complete list of currently loaded modules.
1220    ///
1221    /// Panics on error, i.e. invalid arguments or state.
1222    pub fn get_module_info_list<F>(&self, callback: F)
1223        -> Operation<dyn FnMut(ListResult<&ModuleInfo>)>
1224        where F: FnMut(ListResult<&ModuleInfo>) + 'static
1225    {
1226        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ModuleInfo>)>(Box::new(callback));
1227        let ptr = unsafe { capi::pa_context_get_module_info_list(self.context,
1228            Some(mod_info_list_cb_proxy), cb_data) };
1229        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ModuleInfo>)>)
1230    }
1231
1232    /// Loads a module.
1233    ///
1234    /// Panics on error, i.e. invalid arguments or state. The callback is provided with the
1235    /// index.
1236    pub fn load_module<F>(&mut self, name: &str, argument: &str, callback: F)
1237        -> Operation<dyn FnMut(u32)>
1238        where F: FnMut(u32) + 'static
1239    {
1240        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1241        // as_ptr() giving dangling pointers!
1242        let c_name = CString::new(name).unwrap();
1243        let c_arg = CString::new(argument).unwrap();
1244
1245        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(u32)>(Box::new(callback));
1246        let ptr = unsafe { capi::pa_context_load_module(self.context, c_name.as_ptr(),
1247            c_arg.as_ptr(), Some(context_index_cb_proxy), cb_data) };
1248        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(u32)>)
1249    }
1250
1251    /// Unloads a module.
1252    ///
1253    /// Panics on error, i.e. invalid arguments or state.
1254    ///
1255    /// The callback must accept a `bool`, which indicates success.
1256    pub fn unload_module<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
1257        where F: FnMut(bool) + 'static
1258    {
1259        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
1260        let ptr = unsafe { capi::pa_context_unload_module(self.context, index,
1261            Some(super::success_cb_proxy), cb_data) };
1262        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1263    }
1264}
1265
1266/// Proxy for get module info list callbacks.
1267///
1268/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1269extern "C"
1270fn mod_info_list_cb_proxy(_: *mut ContextInternal, i: *const ModuleInfoInternal, eol: i32,
1271    userdata: *mut c_void)
1272{
1273    let _ = std::panic::catch_unwind(|| {
1274        callback_for_list_instance(i, eol, userdata, ModuleInfo::new_from_raw);
1275    });
1276}
1277
1278/// Proxy for context index callbacks.
1279///
1280/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1281extern "C"
1282fn context_index_cb_proxy(_: *mut ContextInternal, index: u32, userdata: *mut c_void) {
1283    let _ = std::panic::catch_unwind(|| {
1284        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1285        let mut callback = get_su_callback::<dyn FnMut(u32)>(userdata);
1286        (callback)(index);
1287    });
1288}
1289
1290////////////////////////////////////////////////////////////////////////////////////////////////////
1291// Messages
1292////////////////////////////////////////////////////////////////////////////////////////////////////
1293
1294impl Introspector {
1295    /// Send a message to an object that registered a message handler.
1296    ///
1297    /// The callback must accept two params, firstly a boolean indicating success if `true`, and
1298    /// secondly, the response string. The response string may possibly not be given if
1299    /// unsuccessful.
1300    ///
1301    /// For more information see the [messaging_api.txt] documentation in the PulseAudio repository.
1302    ///
1303    /// [messaging_api.txt]: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/blob/master/doc/messaging_api.txt
1304    #[cfg(any(doc, feature = "pa_v15"))]
1305    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
1306    pub fn send_message_to_object<F>(&mut self, recipient_name: &str, message: &str,
1307        message_parameters: &str, callback: F) -> Operation<dyn FnMut(bool, Option<String>)>
1308        where F: FnMut(bool, Option<String>) + 'static
1309    {
1310        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1311        // as_ptr() giving dangling pointers!
1312        let c_recipient_name = CString::new(recipient_name.clone()).unwrap();
1313        let c_message = CString::new(message.clone()).unwrap();
1314        let c_message_parameters = CString::new(message_parameters.clone()).unwrap();
1315
1316        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool, Option<String>)>(Box::new(callback));
1317        let ptr = unsafe { capi::pa_context_send_message_to_object(self.context,
1318            c_recipient_name.as_ptr(), c_message.as_ptr(), c_message_parameters.as_ptr(),
1319            Some(send_message_to_object_cb_proxy), cb_data) };
1320        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool, Option<String>)>)
1321    }
1322}
1323
1324/// Proxy for send message to object callbacks.
1325///
1326/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1327#[cfg(any(doc, feature = "pa_v15"))]
1328extern "C"
1329fn send_message_to_object_cb_proxy(_: *mut ContextInternal, success: i32, response: *const c_char,
1330    userdata: *mut c_void)
1331{
1332    let success_actual = match success {
1333        0 => false,
1334        _ => true,
1335    };
1336    let _ = std::panic::catch_unwind(|| {
1337        let r = match response.is_null() {
1338            true => None,
1339            false => {
1340                let tmp = unsafe { CStr::from_ptr(response) };
1341                Some(tmp.to_string_lossy().into_owned())
1342            },
1343        };
1344        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1345        let mut callback = get_su_callback::<dyn FnMut(bool, Option<String>)>(userdata);
1346        (callback)(success_actual, r);
1347    });
1348}
1349
1350////////////////////////////////////////////////////////////////////////////////////////////////////
1351// Client info
1352////////////////////////////////////////////////////////////////////////////////////////////////////
1353
1354/// Stores information about clients.
1355///
1356/// Please note that this structure can be extended as part of evolutionary API updates at any time
1357/// in any new release.
1358#[derive(Debug)]
1359pub struct ClientInfo<'a> {
1360    /// Index of this client.
1361    pub index: u32,
1362    /// Name of this client.
1363    pub name: Option<Cow<'a, str>>,
1364    /// Index of the owning module, or `None`.
1365    pub owner_module: Option<u32>,
1366    /// Driver name.
1367    pub driver: Option<Cow<'a, str>>,
1368    /// Property list.
1369    pub proplist: Proplist,
1370}
1371
1372impl<'a> ClientInfo<'a> {
1373    fn new_from_raw(p: *const ClientInfoInternal) -> Self {
1374        assert!(!p.is_null());
1375        let src = unsafe { &*p };
1376        unsafe {
1377            ClientInfo {
1378                index: src.index,
1379                name: match src.name.is_null() {
1380                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1381                    true => None,
1382                },
1383                owner_module: match src.owner_module {
1384                    def::INVALID_INDEX => None,
1385                    i => Some(i),
1386                },
1387                driver: match src.driver.is_null() {
1388                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1389                    true => None,
1390                },
1391                proplist: Proplist::from_raw_weak(src.proplist),
1392            }
1393        }
1394    }
1395}
1396
1397impl Introspector {
1398    /// Gets information about a client by its index.
1399    ///
1400    /// Panics on error, i.e. invalid arguments or state.
1401    pub fn get_client_info<F>(&self, index: u32, callback: F)
1402        -> Operation<dyn FnMut(ListResult<&ClientInfo>)>
1403        where F: FnMut(ListResult<&ClientInfo>) + 'static
1404    {
1405        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ClientInfo>)>(Box::new(callback));
1406        let ptr = unsafe { capi::pa_context_get_client_info(self.context, index,
1407            Some(get_client_info_list_cb_proxy), cb_data) };
1408        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ClientInfo>)>)
1409    }
1410
1411    /// Gets the complete client list.
1412    ///
1413    /// Panics on error, i.e. invalid arguments or state.
1414    pub fn get_client_info_list<F>(&self, callback: F)
1415        -> Operation<dyn FnMut(ListResult<&ClientInfo>)>
1416        where F: FnMut(ListResult<&ClientInfo>) + 'static
1417    {
1418        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ClientInfo>)>(Box::new(callback));
1419        let ptr = unsafe { capi::pa_context_get_client_info_list(self.context,
1420            Some(get_client_info_list_cb_proxy), cb_data) };
1421        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ClientInfo>)>)
1422    }
1423
1424    /// Kills a client.
1425    ///
1426    /// Panics on error, i.e. invalid arguments or state.
1427    ///
1428    /// The callback must accept a `bool`, which indicates success.
1429    pub fn kill_client<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
1430        where F: FnMut(bool) + 'static
1431    {
1432        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
1433        let ptr = unsafe { capi::pa_context_kill_client(self.context, index,
1434            Some(super::success_cb_proxy), cb_data) };
1435        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1436    }
1437}
1438
1439/// Proxy for get sink info list callbacks.
1440///
1441/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1442extern "C"
1443fn get_client_info_list_cb_proxy(_: *mut ContextInternal, i: *const ClientInfoInternal, eol: i32,
1444    userdata: *mut c_void)
1445{
1446    let _ = std::panic::catch_unwind(|| {
1447        callback_for_list_instance(i, eol, userdata, ClientInfo::new_from_raw);
1448    });
1449}
1450
1451////////////////////////////////////////////////////////////////////////////////////////////////////
1452// Card info
1453////////////////////////////////////////////////////////////////////////////////////////////////////
1454
1455/// Backwards compatable alias
1456#[deprecated(since = "2.28.0", note = "Use the name CardProfileInfo instead")]
1457pub type CardProfileInfo2<'a> = CardProfileInfo<'a>;
1458
1459/// Stores information about a specific profile of a card.
1460///
1461/// Please note that this structure can be extended as part of evolutionary API updates at any time
1462/// in any new release.
1463#[derive(Debug)]
1464pub struct CardProfileInfo<'a> {
1465    /// Name of this profile.
1466    pub name: Option<Cow<'a, str>>,
1467    /// Description of this profile.
1468    pub description: Option<Cow<'a, str>>,
1469    /// Number of sinks this profile would create.
1470    pub n_sinks: u32,
1471    /// Number of sources this profile would create.
1472    pub n_sources: u32,
1473    /// The higher this value is, the more useful this profile is as a default.
1474    pub priority: u32,
1475    /// Is this profile available? If this is `false`, meaning “unavailable”, then it makes no sense
1476    /// to try to activate this profile. If this is `true`, it’s still not a guarantee that
1477    /// activating the profile will result in anything useful, it just means that the server isn’t
1478    /// aware of any reason why the profile would definitely be useless.
1479    pub available: bool,
1480}
1481
1482impl<'a> CardProfileInfo<'a> {
1483    fn new_from_raw(p: *const CardProfileInfoInternal) -> Self {
1484        assert!(!p.is_null());
1485        let src = unsafe { &*p };
1486        unsafe {
1487            CardProfileInfo {
1488                name: match src.name.is_null() {
1489                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1490                    true => None,
1491                },
1492                description: match src.description.is_null() {
1493                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
1494                    true => None,
1495                },
1496                n_sinks: src.n_sinks,
1497                n_sources: src.n_sources,
1498                priority: src.priority,
1499                available: match src.available {
1500                    0 => false,
1501                    _ => true,
1502                },
1503            }
1504        }
1505    }
1506}
1507
1508/// Stores information about a specific port of a card.
1509///
1510/// Please note that this structure can be extended as part of evolutionary API updates at any time
1511/// in any new release.
1512#[derive(Debug)]
1513pub struct CardPortInfo<'a> {
1514    /// Name of this port.
1515    pub name: Option<Cow<'a, str>>,
1516    /// Description of this port.
1517    pub description: Option<Cow<'a, str>>,
1518    /// The higher this value is, the more useful this port is as a default.
1519    pub priority: u32,
1520    /// Availability status of this port.
1521    pub available: def::PortAvailable,
1522    /// The direction of this port.
1523    pub direction: direction::FlagSet,
1524    /// Property list.
1525    pub proplist: Proplist,
1526    /// Latency offset of the port that gets added to the sink/source latency when the port is
1527    /// active.
1528    pub latency_offset: i64,
1529    /// Set of available profiles.
1530    pub profiles: Vec<CardProfileInfo<'a>>,
1531    /// An indentifier for the group of ports that share their availability status with each other.
1532    ///
1533    /// This is meant especially for handling cases where one 3.5 mm connector is used for
1534    /// headphones, headsets and microphones, and the hardware can only tell that something was
1535    /// plugged in but not what exactly. In this situation the ports for all those devices share
1536    /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and
1537    /// some application may ask the user what was plugged in. Such applications should get a list
1538    /// of all card ports and compare their `availability_group` fields. Ports that have the same
1539    /// group are those that need input from the user to determine which device was plugged in. The
1540    /// application should then activate the user-chosen port.
1541    ///
1542    /// May be `None`, in which case the port is not part of any availability group (which is the
1543    /// same as having a group with only one member).
1544    ///
1545    /// The group identifier must be treated as an opaque identifier. The string may look like an
1546    /// ALSA control name, but applications must not assume any such relationship. The group naming
1547    /// scheme can change without a warning.
1548    #[cfg(any(doc, feature = "pa_v14"))]
1549    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
1550    pub availability_group: Option<Cow<'a, str>>,
1551    /// Port device type.
1552    #[cfg(any(doc, feature = "pa_v14"))]
1553    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
1554    pub r#type: DevicePortType,
1555}
1556
1557impl<'a> CardPortInfo<'a> {
1558    fn new_from_raw(p: *const CardPortInfoInternal) -> Self {
1559        assert!(!p.is_null());
1560        let src = unsafe { &*p };
1561
1562        let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
1563
1564        assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1565        for i in 0..src.n_profiles as isize {
1566            let indexed_ptr =
1567                unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
1568            if !indexed_ptr.is_null() {
1569                profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1570            }
1571        }
1572
1573        unsafe {
1574            CardPortInfo {
1575                name: match src.name.is_null() {
1576                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1577                    true => None,
1578                },
1579                description: match src.description.is_null() {
1580                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
1581                    true => None,
1582                },
1583                priority: src.priority,
1584                available: def::PortAvailable::from_i32(src.available).unwrap(),
1585                direction: direction::FlagSet::from_bits_truncate(src.direction),
1586                proplist: Proplist::from_raw_weak(src.proplist),
1587                latency_offset: src.latency_offset,
1588                profiles: profiles_vec,
1589                #[cfg(any(doc, feature = "pa_v14"))]
1590                availability_group: match src.availability_group.is_null() {
1591                    false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()),
1592                    true => None,
1593                },
1594                #[cfg(any(doc, feature = "pa_v14"))]
1595                r#type: DevicePortType::from_u32(src.r#type).unwrap(),
1596            }
1597        }
1598    }
1599}
1600
1601/// Stores information about cards.
1602///
1603/// Please note that this structure can be extended as part of evolutionary API updates at any time
1604/// in any new release.
1605#[derive(Debug)]
1606pub struct CardInfo<'a> {
1607    /// Index of this card.
1608    pub index: u32,
1609    /// Name of this card.
1610    pub name: Option<Cow<'a, str>>,
1611    /// Index of the owning module, or `None`.
1612    pub owner_module: Option<u32>,
1613    /// Driver name.
1614    pub driver: Option<Cow<'a, str>>,
1615    /// Property list.
1616    pub proplist: Proplist,
1617    /// Set of ports.
1618    pub ports: Vec<CardPortInfo<'a>>,
1619    /// Set of available profiles.
1620    pub profiles: Vec<CardProfileInfo<'a>>,
1621    /// Pointer to active profile in the set, or `None`.
1622    pub active_profile: Option<Box<CardProfileInfo<'a>>>,
1623}
1624
1625impl<'a> CardInfo<'a> {
1626    fn new_from_raw(p: *const CardInfoInternal) -> Self {
1627        assert!(!p.is_null());
1628        let src = unsafe { &*p };
1629
1630        let mut ports_vec = Vec::with_capacity(src.n_ports as usize);
1631        assert!(src.n_ports == 0 || !src.ports.is_null());
1632        for i in 0..src.n_ports as isize {
1633            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut CardPortInfoInternal };
1634            if !indexed_ptr.is_null() {
1635                ports_vec.push(CardPortInfo::new_from_raw(indexed_ptr));
1636            }
1637        }
1638        let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
1639
1640        assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1641        for i in 0..src.n_profiles as isize {
1642            let indexed_ptr =
1643                unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
1644            if !indexed_ptr.is_null() {
1645                profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1646            }
1647        }
1648
1649        unsafe {
1650            CardInfo {
1651                index: src.index,
1652                name: match src.name.is_null() {
1653                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1654                    true => None,
1655                },
1656                owner_module: match src.owner_module {
1657                    def::INVALID_INDEX => None,
1658                    i => Some(i),
1659                },
1660                driver: match src.driver.is_null() {
1661                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1662                    true => None,
1663                },
1664                proplist: Proplist::from_raw_weak(src.proplist),
1665                ports: ports_vec,
1666                profiles: profiles_vec,
1667                active_profile: match src.active_profile2.is_null() {
1668                    true => None,
1669                    false => Some(Box::new(CardProfileInfo::new_from_raw(src.active_profile2))),
1670                },
1671            }
1672        }
1673    }
1674}
1675
1676impl Introspector {
1677    /// Gets information about a card by its index.
1678    ///
1679    /// Panics on error, i.e. invalid arguments or state.
1680    pub fn get_card_info_by_index<F>(&self, index: u32, callback: F)
1681        -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1682        where F: FnMut(ListResult<&CardInfo>) + 'static
1683    {
1684        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1685        let ptr = unsafe { capi::pa_context_get_card_info_by_index(self.context, index,
1686            Some(get_card_info_list_cb_proxy), cb_data) };
1687        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1688    }
1689
1690    /// Gets information about a card by its name.
1691    ///
1692    /// Panics on error, i.e. invalid arguments or state.
1693    pub fn get_card_info_by_name<F>(&self, name: &str, callback: F)
1694        -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1695        where F: FnMut(ListResult<&CardInfo>) + 'static
1696    {
1697        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1698        // as_ptr() giving dangling pointers!
1699        let c_name = CString::new(name).unwrap();
1700
1701        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1702        let ptr = unsafe { capi::pa_context_get_card_info_by_name(self.context, c_name.as_ptr(),
1703            Some(get_card_info_list_cb_proxy), cb_data) };
1704        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1705    }
1706
1707    /// Gets the complete card list.
1708    ///
1709    /// Panics on error, i.e. invalid arguments or state.
1710    pub fn get_card_info_list<F>(&self, callback: F) -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1711        where F: FnMut(ListResult<&CardInfo>) + 'static
1712    {
1713        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1714        let ptr = unsafe { capi::pa_context_get_card_info_list(self.context,
1715            Some(get_card_info_list_cb_proxy), cb_data) };
1716        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1717    }
1718
1719    /// Changes the profile of a card.
1720    ///
1721    /// Panics on error, i.e. invalid arguments or state.
1722    ///
1723    /// The optional callback must accept a `bool`, which indicates success.
1724    pub fn set_card_profile_by_index(&mut self, index: u32, profile: &str,
1725        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1726    {
1727        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1728        // as_ptr() giving dangling pointers!
1729        let c_profile = CString::new(profile).unwrap();
1730
1731        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1732            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1733        let ptr = unsafe { capi::pa_context_set_card_profile_by_index(self.context, index,
1734            c_profile.as_ptr(), cb_fn, cb_data) };
1735        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1736    }
1737
1738    /// Changes the profile of a card.
1739    ///
1740    /// Panics on error, i.e. invalid arguments or state.
1741    ///
1742    /// The optional callback must accept a `bool`, which indicates success.
1743    pub fn set_card_profile_by_name(&mut self, name: &str, profile: &str,
1744        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1745    {
1746        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1747        // as_ptr() giving dangling pointers!
1748        let c_name = CString::new(name).unwrap();
1749        let c_profile = CString::new(profile).unwrap();
1750
1751        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1752            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1753        let ptr = unsafe { capi::pa_context_set_card_profile_by_name(self.context, c_name.as_ptr(),
1754            c_profile.as_ptr(), cb_fn, cb_data) };
1755        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1756    }
1757
1758    /// Sets the latency offset of a port.
1759    ///
1760    /// Panics on error, i.e. invalid arguments or state.
1761    ///
1762    /// The optional callback must accept a `bool`, which indicates success.
1763    pub fn set_port_latency_offset(&mut self, card_name: &str, port_name: &str, offset: i64,
1764        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1765    {
1766        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1767        // as_ptr() giving dangling pointers!
1768        let c_name = CString::new(card_name).unwrap();
1769        let c_port = CString::new(port_name).unwrap();
1770
1771        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1772            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1773        let ptr = unsafe { capi::pa_context_set_port_latency_offset(self.context, c_name.as_ptr(),
1774            c_port.as_ptr(), offset, cb_fn, cb_data) };
1775        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1776    }
1777}
1778
1779/// Proxy for get card info list callbacks.
1780///
1781/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1782extern "C"
1783fn get_card_info_list_cb_proxy(_: *mut ContextInternal, i: *const CardInfoInternal, eol: i32,
1784    userdata: *mut c_void)
1785{
1786    let _ = std::panic::catch_unwind(|| {
1787        callback_for_list_instance(i, eol, userdata, CardInfo::new_from_raw);
1788    });
1789}
1790
1791////////////////////////////////////////////////////////////////////////////////////////////////////
1792// Sink input info
1793////////////////////////////////////////////////////////////////////////////////////////////////////
1794
1795/// Stores information about sink inputs.
1796///
1797/// Please note that this structure can be extended as part of evolutionary API updates at any time
1798/// in any new release.
1799#[derive(Debug)]
1800pub struct SinkInputInfo<'a> {
1801    /// Index of the sink input.
1802    pub index: u32,
1803    /// Name of the sink input.
1804    pub name: Option<Cow<'a, str>>,
1805    /// Index of the module this sink input belongs to, or `None` when it does not belong to any
1806    /// module.
1807    pub owner_module: Option<u32>,
1808    /// Index of the client this sink input belongs to, or invalid when it does not belong to any
1809    /// client.
1810    pub client: Option<u32>,
1811    /// Index of the connected sink.
1812    pub sink: u32,
1813    /// The sample specification of the sink input.
1814    pub sample_spec: sample::Spec,
1815    /// Channel map.
1816    pub channel_map: channelmap::Map,
1817    /// The volume of this sink input.
1818    pub volume: ChannelVolumes,
1819    /// Latency due to buffering in sink input, see [`TimingInfo`](crate::def::TimingInfo) for
1820    /// details.
1821    pub buffer_usec: MicroSeconds,
1822    /// Latency of the sink device, see [`TimingInfo`](crate::def::TimingInfo) for details.
1823    pub sink_usec: MicroSeconds,
1824    /// The resampling method used by this sink input.
1825    pub resample_method: Option<Cow<'a, str>>,
1826    /// Driver name.
1827    pub driver: Option<Cow<'a, str>>,
1828    /// Stream muted.
1829    pub mute: bool,
1830    /// Property list.
1831    pub proplist: Proplist,
1832    /// Stream corked.
1833    pub corked: bool,
1834    /// Stream has volume. If not set, then the meaning of this struct’s volume member is
1835    /// unspecified.
1836    pub has_volume: bool,
1837    /// The volume can be set. If not set, the volume can still change even though clients can’t
1838    /// control the volume.
1839    pub volume_writable: bool,
1840    /// Stream format information.
1841    pub format: format::Info,
1842}
1843
1844impl<'a> SinkInputInfo<'a> {
1845    fn new_from_raw(p: *const SinkInputInfoInternal) -> Self {
1846        assert!(!p.is_null());
1847        let src = unsafe { &*p };
1848        unsafe {
1849            SinkInputInfo {
1850                index: src.index,
1851                name: match src.name.is_null() {
1852                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1853                    true => None,
1854                },
1855                owner_module: match src.owner_module {
1856                    def::INVALID_INDEX => None,
1857                    i => Some(i),
1858                },
1859                client: match src.client {
1860                    def::INVALID_INDEX => None,
1861                    i => Some(i),
1862                },
1863                sink: src.sink,
1864                sample_spec: src.sample_spec.into(),
1865                channel_map: src.channel_map.into(),
1866                volume: src.volume.into(),
1867                buffer_usec: MicroSeconds(src.buffer_usec),
1868                sink_usec: MicroSeconds(src.sink_usec),
1869                resample_method: match src.resample_method.is_null() {
1870                    false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()),
1871                    true => None,
1872                },
1873                driver: match src.driver.is_null() {
1874                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1875                    true => None,
1876                },
1877                mute: match src.mute {
1878                    0 => false,
1879                    _ => true,
1880                },
1881                proplist: Proplist::from_raw_weak(src.proplist),
1882                corked: match src.corked {
1883                    0 => false,
1884                    _ => true,
1885                },
1886                has_volume: match src.has_volume {
1887                    0 => false,
1888                    _ => true,
1889                },
1890                volume_writable: match src.volume_writable {
1891                    0 => false,
1892                    _ => true,
1893                },
1894                format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal),
1895            }
1896        }
1897    }
1898}
1899
1900impl Introspector {
1901    /// Gets some information about a sink input by its index.
1902    ///
1903    /// Panics on error, i.e. invalid arguments or state.
1904    pub fn get_sink_input_info<F>(&self, index: u32, callback: F)
1905        -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
1906        where F: FnMut(ListResult<&SinkInputInfo>) + 'static
1907    {
1908        let cb_data =
1909            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInputInfo>)>(Box::new(callback));
1910        let ptr = unsafe { capi::pa_context_get_sink_input_info(self.context, index,
1911            Some(get_sink_input_info_list_cb_proxy), cb_data) };
1912        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInputInfo>)>)
1913    }
1914
1915    /// Gets the complete sink input list.
1916    ///
1917    /// Panics on error, i.e. invalid arguments or state.
1918    pub fn get_sink_input_info_list<F>(&self, callback: F)
1919        -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
1920        where F: FnMut(ListResult<&SinkInputInfo>) + 'static
1921    {
1922        let cb_data =
1923            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInputInfo>)>(Box::new(callback));
1924        let ptr = unsafe { capi::pa_context_get_sink_input_info_list(self.context,
1925            Some(get_sink_input_info_list_cb_proxy), cb_data) };
1926        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInputInfo>)>)
1927    }
1928
1929    /// Moves the specified sink input to a different sink.
1930    ///
1931    /// Panics on error, i.e. invalid arguments or state.
1932    ///
1933    /// The optional callback must accept a `bool`, which indicates success.
1934    pub fn move_sink_input_by_name(&mut self, index: u32, sink_name: &str,
1935        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1936    {
1937        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1938        // as_ptr() giving dangling pointers!
1939        let c_name = CString::new(sink_name).unwrap();
1940
1941        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1942            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1943        let ptr = unsafe { capi::pa_context_move_sink_input_by_name(self.context, index,
1944            c_name.as_ptr(), cb_fn, cb_data) };
1945        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1946    }
1947
1948    /// Moves the specified sink input to a different sink.
1949    ///
1950    /// Panics on error, i.e. invalid arguments or state.
1951    ///
1952    /// The optional callback must accept a `bool`, which indicates success.
1953    pub fn move_sink_input_by_index(&mut self, index: u32, sink_index: u32,
1954        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1955    {
1956        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1957            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1958        let ptr = unsafe { capi::pa_context_move_sink_input_by_index(self.context, index,
1959            sink_index, cb_fn, cb_data) };
1960        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1961    }
1962
1963    /// Sets the volume of a sink input stream.
1964    ///
1965    /// Panics on error, i.e. invalid arguments or state.
1966    ///
1967    /// The optional callback must accept a `bool`, which indicates success.
1968    pub fn set_sink_input_volume(&mut self, index: u32, volume: &ChannelVolumes,
1969        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1970    {
1971        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1972            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1973        let ptr = unsafe { capi::pa_context_set_sink_input_volume(self.context, index,
1974            volume.as_ref(), cb_fn, cb_data) };
1975        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1976    }
1977
1978    /// Sets the mute switch of a sink input stream.
1979    ///
1980    /// Panics on error, i.e. invalid arguments or state.
1981    ///
1982    /// The optional callback must accept a `bool`, which indicates success.
1983    pub fn set_sink_input_mute(&mut self, index: u32, mute: bool,
1984        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1985    {
1986        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1987            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1988        let ptr = unsafe { capi::pa_context_set_sink_input_mute(self.context, index, mute as i32,
1989            cb_fn, cb_data) };
1990        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1991    }
1992
1993    /// Kills a sink input.
1994    ///
1995    /// Panics on error, i.e. invalid arguments or state.
1996    ///
1997    /// The callback must accept a `bool`, which indicates success.
1998    pub fn kill_sink_input<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
1999        where F: FnMut(bool) + 'static
2000    {
2001        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
2002        let ptr = unsafe { capi::pa_context_kill_sink_input(self.context, index,
2003            Some(super::success_cb_proxy), cb_data) };
2004        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2005    }
2006}
2007
2008/// Proxy for get sink input info list callbacks.
2009///
2010/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2011extern "C"
2012fn get_sink_input_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInputInfoInternal,
2013    eol: i32, userdata: *mut c_void)
2014{
2015    let _ = std::panic::catch_unwind(|| {
2016        callback_for_list_instance(i, eol, userdata, SinkInputInfo::new_from_raw);
2017    });
2018}
2019
2020////////////////////////////////////////////////////////////////////////////////////////////////////
2021// Source output info
2022////////////////////////////////////////////////////////////////////////////////////////////////////
2023
2024/// Stores information about source outputs.
2025///
2026/// Please note that this structure can be extended as part of evolutionary API updates at any time
2027/// in any new release.
2028#[derive(Debug)]
2029pub struct SourceOutputInfo<'a> {
2030    /// Index of the source output.
2031    pub index: u32,
2032    /// Name of the source output.
2033    pub name: Option<Cow<'a, str>>,
2034    /// Index of the module this source output belongs to, or `None` when it does not belong to any
2035    /// module.
2036    pub owner_module: Option<u32>,
2037    /// Index of the client this source output belongs to, or `None` when it does not belong to any
2038    /// client.
2039    pub client: Option<u32>,
2040    /// Index of the connected source.
2041    pub source: u32,
2042    /// The sample specification of the source output.
2043    pub sample_spec: sample::Spec,
2044    /// Channel map.
2045    pub channel_map: channelmap::Map,
2046    /// Latency due to buffering in the source output, see [`TimingInfo`](crate::def::TimingInfo)
2047    /// for details.
2048    pub buffer_usec: MicroSeconds,
2049    /// Latency of the source device, see [`TimingInfo`](crate::def::TimingInfo) for details.
2050    pub source_usec: MicroSeconds,
2051    /// The resampling method used by this source output.
2052    pub resample_method: Option<Cow<'a, str>>,
2053    /// Driver name.
2054    pub driver: Option<Cow<'a, str>>,
2055    /// Property list.
2056    pub proplist: Proplist,
2057    /// Stream corked.
2058    pub corked: bool,
2059    /// The volume of this source output.
2060    pub volume: ChannelVolumes,
2061    /// Stream muted.
2062    pub mute: bool,
2063    /// Stream has volume. If not set, then the meaning of this struct’s volume member is
2064    /// unspecified.
2065    pub has_volume: bool,
2066    /// The volume can be set. If not set, the volume can still change even though clients can’t
2067    /// control the volume.
2068    pub volume_writable: bool,
2069    /// Stream format information.
2070    pub format: format::Info,
2071}
2072
2073impl<'a> SourceOutputInfo<'a> {
2074    fn new_from_raw(p: *const SourceOutputInfoInternal) -> Self {
2075        assert!(!p.is_null());
2076        let src = unsafe { &*p };
2077        unsafe {
2078            SourceOutputInfo {
2079                index: src.index,
2080                name: match src.name.is_null() {
2081                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
2082                    true => None,
2083                },
2084                owner_module: match src.owner_module {
2085                    def::INVALID_INDEX => None,
2086                    i => Some(i),
2087                },
2088                client: match src.client {
2089                    def::INVALID_INDEX => None,
2090                    i => Some(i),
2091                },
2092                source: src.source,
2093                sample_spec: src.sample_spec.into(),
2094                channel_map: src.channel_map.into(),
2095                buffer_usec: MicroSeconds(src.buffer_usec),
2096                source_usec: MicroSeconds(src.source_usec),
2097                resample_method: match src.resample_method.is_null() {
2098                    false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()),
2099                    true => None,
2100                },
2101                driver: match src.driver.is_null() {
2102                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
2103                    true => None,
2104                },
2105                proplist: Proplist::from_raw_weak(src.proplist),
2106                corked: match src.corked {
2107                    0 => false,
2108                    _ => true,
2109                },
2110                volume: src.volume.into(),
2111                mute: match src.mute {
2112                    0 => false,
2113                    _ => true,
2114                },
2115                has_volume: match src.has_volume {
2116                    0 => false,
2117                    _ => true,
2118                },
2119                volume_writable: match src.volume_writable {
2120                    0 => false,
2121                    _ => true,
2122                },
2123                format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal),
2124            }
2125        }
2126    }
2127}
2128
2129impl Introspector {
2130    /// Gets information about a source output by its index.
2131    ///
2132    /// Panics on error, i.e. invalid arguments or state.
2133    pub fn get_source_output_info<F>(&self, index: u32, callback: F)
2134        -> Operation<dyn FnMut(ListResult<&SourceOutputInfo>)>
2135        where F: FnMut(ListResult<&SourceOutputInfo>) + 'static
2136    {
2137        let cb_data =
2138            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceOutputInfo>)>(Box::new(callback));
2139        let ptr = unsafe { capi::pa_context_get_source_output_info(self.context, index,
2140            Some(get_source_output_info_list_cb_proxy), cb_data) };
2141        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceOutputInfo>)>)
2142    }
2143
2144    /// Gets the complete list of source outputs.
2145    ///
2146    /// Panics on error, i.e. invalid arguments or state.
2147    pub fn get_source_output_info_list<F>(&self, callback: F)
2148        -> Operation<dyn FnMut(ListResult<&SourceOutputInfo>)>
2149        where F: FnMut(ListResult<&SourceOutputInfo>) + 'static
2150    {
2151        let cb_data =
2152            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceOutputInfo>)>(Box::new(callback));
2153        let ptr = unsafe { capi::pa_context_get_source_output_info_list(self.context,
2154            Some(get_source_output_info_list_cb_proxy), cb_data) };
2155        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceOutputInfo>)>)
2156    }
2157
2158    /// Moves the specified source output to a different source.
2159    ///
2160    /// Panics on error, i.e. invalid arguments or state.
2161    ///
2162    /// The optional callback must accept a `bool`, which indicates success.
2163    pub fn move_source_output_by_name(&mut self, index: u32, source_name: &str,
2164        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2165    {
2166        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
2167        // as_ptr() giving dangling pointers!
2168        let c_name = CString::new(source_name).unwrap();
2169
2170        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2171            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2172        let ptr = unsafe { capi::pa_context_move_source_output_by_name(self.context, index,
2173            c_name.as_ptr(), cb_fn, cb_data) };
2174        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2175    }
2176
2177    /// Moves the specified source output to a different source.
2178    ///
2179    /// Panics on error, i.e. invalid arguments or state.
2180    ///
2181    /// The optional callback must accept a `bool`, which indicates success.
2182    pub fn move_source_output_by_index(&mut self, index: u32, source_index: u32,
2183        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2184    {
2185        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2186            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2187        let ptr = unsafe { capi::pa_context_move_source_output_by_index(self.context, index,
2188            source_index, cb_fn, cb_data) };
2189        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2190    }
2191
2192    /// Sets the volume of a source output stream.
2193    ///
2194    /// Panics on error, i.e. invalid arguments or state.
2195    ///
2196    /// The optional callback must accept a `bool`, which indicates success.
2197    pub fn set_source_output_volume(&mut self, index: u32, volume: &ChannelVolumes,
2198        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2199    {
2200        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2201            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2202        let ptr = unsafe { capi::pa_context_set_source_output_volume(self.context, index,
2203            volume.as_ref(), cb_fn, cb_data) };
2204        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2205    }
2206
2207    /// Sets the mute switch of a source output stream.
2208    ///
2209    /// Panics on error, i.e. invalid arguments or state.
2210    ///
2211    /// The optional callback must accept a `bool`, which indicates success.
2212    pub fn set_source_output_mute(&mut self, index: u32, mute: bool,
2213        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2214    {
2215        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2216            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2217        let ptr = unsafe { capi::pa_context_set_source_output_mute(self.context, index, mute as i32,
2218            cb_fn, cb_data) };
2219        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2220    }
2221
2222    /// Kills a source output.
2223    ///
2224    /// Panics on error, i.e. invalid arguments or state.
2225    ///
2226    /// The callback must accept a `bool`, which indicates success.
2227    pub fn kill_source_output<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
2228        where F: FnMut(bool) + 'static
2229    {
2230        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
2231        let ptr = unsafe { capi::pa_context_kill_source_output(self.context, index,
2232            Some(super::success_cb_proxy), cb_data) };
2233        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2234    }
2235}
2236
2237/// Proxy for get source output info list callbacks.
2238///
2239/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2240extern "C"
2241fn get_source_output_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceOutputInfoInternal,
2242    eol: i32, userdata: *mut c_void)
2243{
2244    let _ = std::panic::catch_unwind(|| {
2245        callback_for_list_instance(i, eol, userdata, SourceOutputInfo::new_from_raw);
2246    });
2247}
2248
2249////////////////////////////////////////////////////////////////////////////////////////////////////
2250// Stat info
2251////////////////////////////////////////////////////////////////////////////////////////////////////
2252
2253impl Introspector {
2254    /// Gets daemon memory block statistics.
2255    ///
2256    /// Panics on error, i.e. invalid arguments or state.
2257    pub fn stat<F>(&self, callback: F) -> Operation<dyn FnMut(&StatInfo)>
2258        where F: FnMut(&StatInfo) + 'static
2259    {
2260        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(&StatInfo)>(Box::new(callback));
2261        let ptr =
2262            unsafe { capi::pa_context_stat(self.context, Some(get_stat_info_cb_proxy), cb_data) };
2263        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(&StatInfo)>)
2264    }
2265}
2266
2267/// Proxy for get stat info callbacks.
2268///
2269/// Warning: This is for single-use cases only! It destroys the actual closure callback.
2270extern "C"
2271fn get_stat_info_cb_proxy(_: *mut ContextInternal, i: *const StatInfo, userdata: *mut c_void) {
2272    let _ = std::panic::catch_unwind(|| {
2273        assert!(!i.is_null());
2274        // Note, destroys closure callback after use - restoring outer box means it gets dropped
2275        let mut callback = get_su_callback::<dyn FnMut(&StatInfo)>(userdata);
2276        (callback)(unsafe { &*i });
2277    });
2278}
2279
2280////////////////////////////////////////////////////////////////////////////////////////////////////
2281// Sample info
2282////////////////////////////////////////////////////////////////////////////////////////////////////
2283
2284/// Stores information about sample cache entries.
2285///
2286/// Please note that this structure can be extended as part of evolutionary API updates at any time
2287/// in any new release.
2288#[derive(Debug)]
2289pub struct SampleInfo<'a> {
2290    /// Index of this entry.
2291    pub index: u32,
2292    /// Name of this entry.
2293    pub name: Option<Cow<'a, str>>,
2294    /// Default volume of this entry.
2295    pub volume: ChannelVolumes,
2296    /// Sample specification of the sample.
2297    pub sample_spec: sample::Spec,
2298    /// The channel map.
2299    pub channel_map: channelmap::Map,
2300    /// Duration of this entry.
2301    pub duration: MicroSeconds,
2302    /// Length of this sample in bytes.
2303    pub bytes: u32,
2304    /// Non-zero when this is a lazy cache entry.
2305    pub lazy: bool,
2306    /// In case this is a lazy cache entry, the filename for the sound file to be loaded on demand.
2307    pub filename: Option<Cow<'a, str>>,
2308    /// Property list for this sample.
2309    pub proplist: Proplist,
2310}
2311
2312impl<'a> SampleInfo<'a> {
2313    fn new_from_raw(p: *const SampleInfoInternal) -> Self {
2314        assert!(!p.is_null());
2315        let src = unsafe { &*p };
2316        unsafe {
2317            SampleInfo {
2318                index: src.index,
2319                name: match src.name.is_null() {
2320                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
2321                    true => None,
2322                },
2323                volume: src.volume.into(),
2324                sample_spec: src.sample_spec.into(),
2325                channel_map: src.channel_map.into(),
2326                duration: MicroSeconds(src.duration),
2327                bytes: src.bytes,
2328                lazy: match src.lazy {
2329                    0 => false,
2330                    _ => true,
2331                },
2332                filename: match src.filename.is_null() {
2333                    false => Some(CStr::from_ptr(src.filename).to_string_lossy()),
2334                    true => None,
2335                },
2336                proplist: Proplist::from_raw_weak(src.proplist),
2337            }
2338        }
2339    }
2340}
2341
2342impl Introspector {
2343    /// Gets information about a sample by its name.
2344    ///
2345    /// Panics on error, i.e. invalid arguments or state.
2346    pub fn get_sample_info_by_name<F>(&self, name: &str, callback: F)
2347        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2348        where F: FnMut(ListResult<&SampleInfo>) + 'static
2349    {
2350        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
2351        // as_ptr() giving dangling pointers!
2352        let c_name = CString::new(name).unwrap();
2353
2354        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2355        let ptr = unsafe { capi::pa_context_get_sample_info_by_name(self.context, c_name.as_ptr(),
2356            Some(get_sample_info_list_cb_proxy), cb_data) };
2357        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2358    }
2359
2360    /// Gets information about a sample by its index.
2361    ///
2362    /// Panics on error, i.e. invalid arguments or state.
2363    pub fn get_sample_info_by_index<F>(&self, index: u32, callback: F)
2364        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2365        where F: FnMut(ListResult<&SampleInfo>) + 'static
2366    {
2367        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2368        let ptr = unsafe { capi::pa_context_get_sample_info_by_index(self.context, index,
2369            Some(get_sample_info_list_cb_proxy), cb_data) };
2370        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2371    }
2372
2373    /// Gets the complete list of samples stored in the daemon.
2374    ///
2375    /// Panics on error, i.e. invalid arguments or state.
2376    pub fn get_sample_info_list<F>(&self, callback: F)
2377        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2378        where F: FnMut(ListResult<&SampleInfo>) + 'static
2379    {
2380        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2381        let ptr = unsafe { capi::pa_context_get_sample_info_list(self.context,
2382            Some(get_sample_info_list_cb_proxy), cb_data) };
2383        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2384    }
2385}
2386
2387/// Proxy for get sample info list callbacks.
2388///
2389/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2390extern "C"
2391fn get_sample_info_list_cb_proxy(_: *mut ContextInternal, i: *const SampleInfoInternal, eol: i32,
2392    userdata: *mut c_void)
2393{
2394    let _ = std::panic::catch_unwind(|| {
2395        callback_for_list_instance(i, eol, userdata, SampleInfo::new_from_raw);
2396    });
2397}