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 SinkPortInfo<'_> {
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    /// Creates a copy with owned data.
329    pub fn to_owned(&self) -> SinkPortInfo<'static> {
330        SinkPortInfo {
331            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
332            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
333            #[cfg(any(doc, feature = "pa_v14"))]
334            availability_group: self.availability_group.clone().map(|o| Cow::Owned(o.into_owned())),
335            ..*self
336        }
337    }
338}
339
340/// Stores information about sinks.
341///
342/// Please note that this structure can be extended as part of evolutionary API updates at any time
343/// in any new release.
344#[derive(Debug)]
345pub struct SinkInfo<'a> {
346    /// Name of the sink.
347    pub name: Option<Cow<'a, str>>,
348    /// Index of the sink.
349    pub index: u32,
350    /// Description of this sink.
351    pub description: Option<Cow<'a, str>>,
352    /// Sample spec of this sink.
353    pub sample_spec: sample::Spec,
354    /// Channel map.
355    pub channel_map: channelmap::Map,
356    /// Index of the owning module of this sink, or `None` if is invalid.
357    pub owner_module: Option<u32>,
358    /// Volume of the sink.
359    pub volume: ChannelVolumes,
360    /// Mute switch of the sink.
361    pub mute: bool,
362    /// Index of the monitor source connected to this sink.
363    pub monitor_source: u32,
364    /// The name of the monitor source.
365    pub monitor_source_name: Option<Cow<'a, str>>,
366    /// Length of queued audio in the output buffer.
367    pub latency: MicroSeconds,
368    /// Driver name.
369    pub driver: Option<Cow<'a, str>>,
370    /// Flags.
371    pub flags: def::SinkFlagSet,
372    /// Property list.
373    pub proplist: Proplist,
374    /// The latency this device has been configured to.
375    pub configured_latency: MicroSeconds,
376    /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of
377    /// the output device.
378    pub base_volume: Volume,
379    /// State.
380    pub state: def::SinkState,
381    /// Number of volume steps for sinks which do not support arbitrary volumes.
382    pub n_volume_steps: u32,
383    /// Card index, or `None` if invalid.
384    pub card: Option<u32>,
385    /// Set of available ports.
386    pub ports: Vec<SinkPortInfo<'a>>,
387    /// Pointer to active port in the set, or `None`.
388    pub active_port: Option<Box<SinkPortInfo<'a>>>,
389    /// Set of formats supported by the sink.
390    pub formats: Vec<format::Info>,
391}
392
393impl SinkInfo<'_> {
394    fn new_from_raw(p: *const SinkInfoInternal) -> Self {
395        assert!(!p.is_null());
396        let src = unsafe { &*p };
397
398        let mut port_vec = Vec::with_capacity(src.n_ports as usize);
399        assert!(src.n_ports == 0 || !src.ports.is_null());
400        for i in 0..src.n_ports as isize {
401            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SinkPortInfoInternal };
402            if !indexed_ptr.is_null() {
403                port_vec.push(SinkPortInfo::new_from_raw(indexed_ptr));
404            }
405        }
406        let mut formats_vec = Vec::with_capacity(src.n_formats as usize);
407        assert!(src.n_formats == 0 || !src.formats.is_null());
408        for i in 0..src.n_formats as isize {
409            let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal };
410            if !indexed_ptr.is_null() {
411                formats_vec.push(format::Info::from_raw_weak(indexed_ptr));
412            }
413        }
414
415        unsafe {
416            SinkInfo {
417                name: match src.name.is_null() {
418                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
419                    true => None,
420                },
421                index: src.index,
422                description: match src.description.is_null() {
423                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
424                    true => None,
425                },
426                sample_spec: src.sample_spec.into(),
427                channel_map: src.channel_map.into(),
428                owner_module: match src.owner_module {
429                    def::INVALID_INDEX => None,
430                    i => Some(i),
431                },
432                volume: src.volume.into(),
433                mute: match src.mute {
434                    0 => false,
435                    _ => true,
436                },
437                monitor_source: src.monitor_source,
438                monitor_source_name: match src.monitor_source_name.is_null() {
439                    false => Some(CStr::from_ptr(src.monitor_source_name).to_string_lossy()),
440                    true => None,
441                },
442                latency: MicroSeconds(src.latency),
443                driver: match src.driver.is_null() {
444                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
445                    true => None,
446                },
447                flags: def::SinkFlagSet::from_bits_truncate(src.flags),
448                proplist: Proplist::from_raw_weak(src.proplist),
449                configured_latency: MicroSeconds(src.configured_latency),
450                base_volume: Volume(src.base_volume),
451                state: src.state.into(),
452                n_volume_steps: src.n_volume_steps,
453                card: match src.card {
454                    def::INVALID_INDEX => None,
455                    i => Some(i),
456                },
457                ports: port_vec,
458                active_port: match src.active_port.is_null() {
459                    true => None,
460                    false => Some(Box::new(SinkPortInfo::new_from_raw(src.active_port))),
461                },
462                formats: formats_vec,
463            }
464        }
465    }
466
467    /// Creates a copy with owned data.
468    pub fn to_owned(&self) -> SinkInfo<'static> {
469        SinkInfo {
470            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
471            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
472            monitor_source_name: self.monitor_source_name.clone().map(|o| Cow::Owned(o.into_owned())),
473            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
474            proplist: self.proplist.to_owned(),
475            ports: self.ports.iter().map(SinkPortInfo::to_owned).collect(),
476            active_port: self.active_port.as_ref().map(|ap| Box::new(ap.as_ref().to_owned())),
477            formats: self.formats.iter().map(format::Info::to_owned).collect(),
478            ..*self
479        }
480    }
481}
482
483impl Introspector {
484    /// Gets information about a sink by its name.
485    ///
486    /// Panics on error, i.e. invalid arguments or state.
487    pub fn get_sink_info_by_name<F>(&self, name: &str, callback: F)
488        -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
489        where F: FnMut(ListResult<&SinkInfo>) + 'static
490    {
491        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
492        // as_ptr() giving dangling pointers!
493        let c_name = CString::new(name).unwrap();
494
495        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
496        let ptr = unsafe { capi::pa_context_get_sink_info_by_name(self.context, c_name.as_ptr(),
497            Some(get_sink_info_list_cb_proxy), cb_data) };
498        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
499    }
500
501    /// Gets information about a sink by its index.
502    ///
503    /// Panics on error, i.e. invalid arguments or state.
504    pub fn get_sink_info_by_index<F>(&self, index: u32, callback: F)
505        -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
506        where F: FnMut(ListResult<&SinkInfo>) + 'static
507    {
508        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
509        let ptr = unsafe { capi::pa_context_get_sink_info_by_index(self.context, index,
510            Some(get_sink_info_list_cb_proxy), cb_data) };
511        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
512    }
513
514    /// Gets the complete sink list.
515    ///
516    /// Panics on error, i.e. invalid arguments or state.
517    pub fn get_sink_info_list<F>(&self, callback: F) -> Operation<dyn FnMut(ListResult<&SinkInfo>)>
518        where F: FnMut(ListResult<&SinkInfo>) + 'static
519    {
520        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInfo>)>(Box::new(callback));
521        let ptr = unsafe { capi::pa_context_get_sink_info_list(self.context,
522            Some(get_sink_info_list_cb_proxy), cb_data) };
523        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInfo>)>)
524    }
525
526    /// Sets the volume of a sink device specified by its index.
527    ///
528    /// Panics on error, i.e. invalid arguments or state.
529    ///
530    /// The optional callback must accept a `bool`, which indicates success.
531    pub fn set_sink_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes,
532        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
533    {
534        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
535            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
536        let ptr = unsafe { capi::pa_context_set_sink_volume_by_index(self.context, index,
537            volume.as_ref(), cb_fn, cb_data) };
538        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
539    }
540
541    /// Sets the volume of a sink device specified by its name.
542    ///
543    /// Panics on error, i.e. invalid arguments or state.
544    ///
545    /// The optional callback must accept a `bool`, which indicates success.
546    pub fn set_sink_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes,
547        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
548    {
549        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
550        // as_ptr() giving dangling pointers!
551        let c_name = CString::new(name).unwrap();
552
553        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
554            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
555        let ptr = unsafe { capi::pa_context_set_sink_volume_by_name(self.context, c_name.as_ptr(),
556            volume.as_ref(), cb_fn, cb_data) };
557        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
558    }
559
560    /// Sets the mute switch of a sink device specified by its index.
561    ///
562    /// Panics on error, i.e. invalid arguments or state.
563    ///
564    /// The optional callback must accept a `bool`, which indicates success.
565    pub fn set_sink_mute_by_index(&mut self, index: u32, mute: bool,
566        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
567    {
568        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
569            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
570        let ptr = unsafe { capi::pa_context_set_sink_mute_by_index(self.context, index, mute as i32,
571            cb_fn, cb_data) };
572        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
573    }
574
575    /// Sets the mute switch of a sink device specified by its name.
576    ///
577    /// Panics on error, i.e. invalid arguments or state.
578    ///
579    /// The optional callback must accept a `bool`, which indicates success.
580    pub fn set_sink_mute_by_name(&mut self, name: &str, mute: bool,
581        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
582    {
583        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
584        // as_ptr() giving dangling pointers!
585        let c_name = CString::new(name).unwrap();
586
587        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
588            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
589        let ptr = unsafe { capi::pa_context_set_sink_mute_by_name(self.context, c_name.as_ptr(),
590            mute as i32, cb_fn, cb_data) };
591        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
592    }
593
594    /// Suspends/Resumes a sink.
595    ///
596    /// Panics on error, i.e. invalid arguments or state.
597    ///
598    /// The optional callback must accept a `bool`, which indicates success.
599    pub fn suspend_sink_by_name(&mut self, sink_name: &str, suspend: bool,
600        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
601    {
602        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
603        // as_ptr() giving dangling pointers!
604        let c_name = CString::new(sink_name).unwrap();
605
606        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
607            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
608        let ptr = unsafe { capi::pa_context_suspend_sink_by_name(self.context, c_name.as_ptr(),
609            suspend as i32, cb_fn, cb_data) };
610        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
611    }
612
613    /// Suspends/Resumes a sink.
614    ///
615    /// If `index` is [`def::INVALID_INDEX`] all sinks will be suspended.
616    /// Panics on error, i.e. invalid arguments or state.
617    ///
618    /// The optional callback must accept a `bool`, which indicates success.
619    pub fn suspend_sink_by_index(&mut self, index: u32, suspend: bool,
620        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
621    {
622        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
623            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
624        let ptr = unsafe { capi::pa_context_suspend_sink_by_index(self.context, index,
625            suspend as i32, cb_fn, cb_data) };
626        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
627    }
628
629    /// Changes the profile of a sink.
630    ///
631    /// Panics on error, i.e. invalid arguments or state.
632    ///
633    /// The optional callback must accept a `bool`, which indicates success.
634    pub fn set_sink_port_by_index(&mut self, index: u32, port: &str,
635        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
636    {
637        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
638        // as_ptr() giving dangling pointers!
639        let c_port = CString::new(port).unwrap();
640
641        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
642            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
643        let ptr = unsafe { capi::pa_context_set_sink_port_by_index(self.context, index,
644            c_port.as_ptr(), cb_fn, cb_data) };
645        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
646    }
647
648    /// Changes the profile of a sink.
649    ///
650    /// Panics on error, i.e. invalid arguments or state.
651    ///
652    /// The optional callback must accept a `bool`, which indicates success.
653    pub fn set_sink_port_by_name(&mut self, name: &str, port: &str,
654        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
655    {
656        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
657        // as_ptr() giving dangling pointers!
658        let c_name = CString::new(name).unwrap();
659        let c_port = CString::new(port).unwrap();
660
661        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
662            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
663        let ptr = unsafe { capi::pa_context_set_sink_port_by_name(self.context, c_name.as_ptr(),
664            c_port.as_ptr(), cb_fn, cb_data) };
665        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
666    }
667}
668
669/// Proxy for get sink info list callbacks.
670/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
671extern "C"
672fn get_sink_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInfoInternal, eol: i32,
673    userdata: *mut c_void)
674{
675    let _ = std::panic::catch_unwind(|| {
676        callback_for_list_instance(i, eol, userdata, SinkInfo::new_from_raw);
677    });
678}
679
680////////////////////////////////////////////////////////////////////////////////////////////////////
681// Source info
682////////////////////////////////////////////////////////////////////////////////////////////////////
683
684/// Stores information about a specific port of a source.
685///
686/// Please note that this structure can be extended as part of evolutionary API updates at any time
687/// in any new release.
688#[derive(Debug)]
689pub struct SourcePortInfo<'a> {
690    /// Name of this port.
691    pub name: Option<Cow<'a, str>>,
692    /// Description of this port.
693    pub description: Option<Cow<'a, str>>,
694    /// The higher this value is, the more useful this port is as a default.
695    pub priority: u32,
696    /// A flag indicating availability status of this port.
697    pub available: def::PortAvailable,
698    /// An indentifier for the group of ports that share their availability status with each other.
699    ///
700    /// This is meant especially for handling cases where one 3.5 mm connector is used for
701    /// headphones, headsets and microphones, and the hardware can only tell that something was
702    /// plugged in but not what exactly. In this situation the ports for all those devices share
703    /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and
704    /// some application may ask the user what was plugged in. Such applications should get a list
705    /// of all card ports and compare their `availability_group` fields. Ports that have the same
706    /// group are those that need input from the user to determine which device was plugged in. The
707    /// application should then activate the user-chosen port.
708    ///
709    /// May be `None`, in which case the port is not part of any availability group (which is the
710    /// same as having a group with only one member).
711    ///
712    /// The group identifier must be treated as an opaque identifier. The string may look like an
713    /// ALSA control name, but applications must not assume any such relationship. The group naming
714    /// scheme can change without a warning.
715    ///
716    /// Since one group can include both input and output ports, the grouping should be done using
717    /// `CardPortInfo` instead of `SourcePortInfo`, but this field is duplicated also in
718    /// `SourcePortInfo` (and `SinkPortInfo`) in case someone finds that convenient.
719    #[cfg(any(doc, feature = "pa_v14"))]
720    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
721    pub availability_group: Option<Cow<'a, str>>,
722    /// Port device type.
723    #[cfg(any(doc, feature = "pa_v14"))]
724    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
725    pub r#type: DevicePortType,
726}
727
728impl SourcePortInfo<'_> {
729    fn new_from_raw(p: *const SourcePortInfoInternal) -> Self {
730        assert!(!p.is_null());
731        let src = unsafe { &*p };
732        unsafe {
733            SourcePortInfo {
734                name: match src.name.is_null() {
735                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
736                    true => None,
737                },
738                description: match src.description.is_null() {
739                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
740                    true => None,
741                },
742                priority: src.priority,
743                available: def::PortAvailable::from_i32(src.available).unwrap(),
744                #[cfg(any(doc, feature = "pa_v14"))]
745                availability_group: match src.availability_group.is_null() {
746                    false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()),
747                    true => None,
748                },
749                #[cfg(any(doc, feature = "pa_v14"))]
750                r#type: DevicePortType::from_u32(src.r#type).unwrap(),
751            }
752        }
753    }
754
755    /// Creates a copy with owned data.
756    pub fn to_owned(&self) -> SourcePortInfo<'static> {
757        SourcePortInfo {
758            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
759            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
760            #[cfg(any(doc, feature = "pa_v14"))]
761            availability_group: self.availability_group.clone().map(|o| Cow::Owned(o.into_owned())),
762            ..*self
763        }
764    }
765}
766
767/// Stores information about sources.
768///
769/// Please note that this structure can be extended as part of evolutionary API updates at any time
770/// in any new release.
771#[derive(Debug)]
772pub struct SourceInfo<'a> {
773    /// Name of the source.
774    pub name: Option<Cow<'a, str>>,
775    /// Index of the source.
776    pub index: u32,
777    /// Description of this source.
778    pub description: Option<Cow<'a, str>>,
779    /// Sample spec of this source.
780    pub sample_spec: sample::Spec,
781    /// Channel map.
782    pub channel_map: channelmap::Map,
783    /// Owning module index, or `None`.
784    pub owner_module: Option<u32>,
785    /// Volume of the source.
786    pub volume: ChannelVolumes,
787    /// Mute switch of the sink.
788    pub mute: bool,
789    /// If this is a monitor source, the index of the owning sink, otherwise `None`.
790    pub monitor_of_sink: Option<u32>,
791    /// Name of the owning sink, or `None`.
792    pub monitor_of_sink_name: Option<Cow<'a, str>>,
793    /// Length of filled record buffer of this source.
794    pub latency: MicroSeconds,
795    /// Driver name.
796    pub driver: Option<Cow<'a, str>>,
797    /// Flags.
798    pub flags: def::SourceFlagSet,
799    /// Property list.
800    pub proplist: Proplist,
801    /// The latency this device has been configured to.
802    pub configured_latency: MicroSeconds,
803    /// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of
804    /// the input device.
805    pub base_volume: Volume,
806    /// State.
807    pub state: def::SourceState,
808    /// Number of volume steps for sources which do not support arbitrary volumes.
809    pub n_volume_steps: u32,
810    /// Card index, or `None`.
811    pub card: Option<u32>,
812    /// Set of available ports.
813    pub ports: Vec<SourcePortInfo<'a>>,
814    /// Pointer to active port in the set, or `None`.
815    pub active_port: Option<Box<SourcePortInfo<'a>>>,
816    /// Set of formats supported by the sink.
817    pub formats: Vec<format::Info>,
818}
819
820impl SourceInfo<'_> {
821    fn new_from_raw(p: *const SourceInfoInternal) -> Self {
822        assert!(!p.is_null());
823        let src = unsafe { &*p };
824
825        let mut port_vec = Vec::with_capacity(src.n_ports as usize);
826        assert!(src.n_ports == 0 || !src.ports.is_null());
827        for i in 0..src.n_ports as isize {
828            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut SourcePortInfoInternal };
829            if !indexed_ptr.is_null() {
830                port_vec.push(SourcePortInfo::new_from_raw(indexed_ptr));
831            }
832        }
833        let mut formats_vec = Vec::with_capacity(src.n_formats as usize);
834        assert!(src.n_formats == 0 || !src.formats.is_null());
835        for i in 0..src.n_formats as isize {
836            let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal };
837            if !indexed_ptr.is_null() {
838                formats_vec.push(format::Info::from_raw_weak(indexed_ptr));
839            }
840        }
841
842        unsafe {
843            SourceInfo {
844                name: match src.name.is_null() {
845                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
846                    true => None,
847                },
848                index: src.index,
849                description: match src.description.is_null() {
850                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
851                    true => None,
852                },
853                sample_spec: src.sample_spec.into(),
854                channel_map: src.channel_map.into(),
855                owner_module: match src.owner_module {
856                    def::INVALID_INDEX => None,
857                    i => Some(i),
858                },
859                volume: src.volume.into(),
860                mute: match src.mute {
861                    0 => false,
862                    _ => true,
863                },
864                monitor_of_sink: match src.monitor_of_sink {
865                    def::INVALID_INDEX => None,
866                    i => Some(i),
867                },
868                monitor_of_sink_name: match src.monitor_of_sink_name.is_null() {
869                    false => Some(CStr::from_ptr(src.monitor_of_sink_name).to_string_lossy()),
870                    true => None,
871                },
872                latency: MicroSeconds(src.latency),
873                driver: match src.driver.is_null() {
874                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
875                    true => None,
876                },
877                flags: def::SourceFlagSet::from_bits_truncate(src.flags),
878                proplist: Proplist::from_raw_weak(src.proplist),
879                configured_latency: MicroSeconds(src.configured_latency),
880                base_volume: Volume(src.base_volume),
881                state: src.state.into(),
882                n_volume_steps: src.n_volume_steps,
883                card: match src.card {
884                    def::INVALID_INDEX => None,
885                    i => Some(i),
886                },
887                ports: port_vec,
888                active_port: match src.active_port.is_null() {
889                    true => None,
890                    false => Some(Box::new(SourcePortInfo::new_from_raw(src.active_port))),
891                },
892                formats: formats_vec,
893            }
894        }
895    }
896
897    /// Creates a copy with owned data.
898    pub fn to_owned(&self) -> SourceInfo<'static> {
899        SourceInfo {
900            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
901            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
902            monitor_of_sink_name: self.monitor_of_sink_name.clone().map(|o| Cow::Owned(o.into_owned())),
903            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
904            proplist: self.proplist.to_owned(),
905            ports: self.ports.iter().map(SourcePortInfo::to_owned).collect(),
906            active_port: self.active_port.as_ref().map(|ap| Box::new(ap.as_ref().to_owned())),
907            formats: self.formats.iter().map(format::Info::to_owned).collect(),
908            ..*self
909        }
910    }
911}
912
913impl Introspector {
914    /// Gets information about a source by its name.
915    ///
916    /// Panics on error, i.e. invalid arguments or state.
917    pub fn get_source_info_by_name<F>(&self, name: &str, callback: F)
918        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
919        where F: FnMut(ListResult<&SourceInfo>) + 'static
920    {
921        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
922        // as_ptr() giving dangling pointers!
923        let c_name = CString::new(name).unwrap();
924
925        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
926        let ptr = unsafe { capi::pa_context_get_source_info_by_name(self.context, c_name.as_ptr(),
927            Some(get_source_info_list_cb_proxy), cb_data) };
928        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
929    }
930
931    /// Gets information about a source by its index.
932    ///
933    /// Panics on error, i.e. invalid arguments or state.
934    pub fn get_source_info_by_index<F>(&self, index: u32, callback: F)
935        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
936        where F: FnMut(ListResult<&SourceInfo>) + 'static
937    {
938        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
939        let ptr = unsafe { capi::pa_context_get_source_info_by_index(self.context, index,
940            Some(get_source_info_list_cb_proxy), cb_data) };
941        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
942    }
943
944    /// Gets the complete source list.
945    ///
946    /// Panics on error, i.e. invalid arguments or state.
947    pub fn get_source_info_list<F>(&self, callback: F)
948        -> Operation<dyn FnMut(ListResult<&SourceInfo>)>
949        where F: FnMut(ListResult<&SourceInfo>) + 'static
950    {
951        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceInfo>)>(Box::new(callback));
952        let ptr = unsafe { capi::pa_context_get_source_info_list(self.context,
953            Some(get_source_info_list_cb_proxy), cb_data) };
954        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceInfo>)>)
955    }
956
957    /// Sets the volume of a source device specified by its index.
958    ///
959    /// Panics on error, i.e. invalid arguments or state.
960    ///
961    /// The optional callback must accept a `bool`, which indicates success.
962    pub fn set_source_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes,
963        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
964    {
965        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
966            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
967        let ptr = unsafe { capi::pa_context_set_source_volume_by_index(self.context, index,
968            volume.as_ref(), cb_fn, cb_data) };
969        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
970    }
971
972    /// Sets the volume of a source device specified by its name.
973    ///
974    /// Panics on error, i.e. invalid arguments or state.
975    ///
976    /// The optional callback must accept a `bool`, which indicates success.
977    pub fn set_source_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes,
978        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
979    {
980        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
981        // as_ptr() giving dangling pointers!
982        let c_name = CString::new(name).unwrap();
983
984        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
985            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
986        let ptr = unsafe { capi::pa_context_set_source_volume_by_name(self.context,
987            c_name.as_ptr(), volume.as_ref(), cb_fn, cb_data) };
988        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
989    }
990
991    /// Sets the mute switch of a source device specified by its index.
992    ///
993    /// Panics on error, i.e. invalid arguments or state.
994    ///
995    /// The optional callback must accept a `bool`, which indicates success.
996    pub fn set_source_mute_by_index(&mut self, index: u32, mute: bool,
997        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
998    {
999        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1000            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1001        let ptr = unsafe { capi::pa_context_set_source_mute_by_index(self.context, index,
1002            mute as i32, cb_fn, cb_data) };
1003        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1004    }
1005
1006    /// Sets the mute switch of a source device specified by its name.
1007    ///
1008    /// Panics on error, i.e. invalid arguments or state.
1009    ///
1010    /// The optional callback must accept a `bool`, which indicates success.
1011    pub fn set_source_mute_by_name(&mut self, name: &str, mute: bool,
1012        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1013    {
1014        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1015        // as_ptr() giving dangling pointers!
1016        let c_name = CString::new(name).unwrap();
1017
1018        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1019            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1020        let ptr = unsafe { capi::pa_context_set_source_mute_by_name(self.context, c_name.as_ptr(),
1021            mute as i32, cb_fn, cb_data) };
1022        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1023    }
1024
1025    /// Suspends/Resumes a source.
1026    ///
1027    /// Panics on error, i.e. invalid arguments or state.
1028    ///
1029    /// The optional callback must accept a `bool`, which indicates success.
1030    pub fn suspend_source_by_name(&mut self, name: &str, suspend: bool,
1031        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1032    {
1033        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1034        // as_ptr() giving dangling pointers!
1035        let c_name = CString::new(name).unwrap();
1036
1037        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1038            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1039        let ptr = unsafe { capi::pa_context_suspend_source_by_name(self.context, c_name.as_ptr(),
1040            suspend as i32, cb_fn, cb_data) };
1041        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1042    }
1043
1044    /// Suspends/Resumes a source.
1045    ///
1046    /// If `index` is [`def::INVALID_INDEX`], all sources will be suspended.
1047    /// Panics on error, i.e. invalid arguments or state.
1048    ///
1049    /// The optional callback must accept a `bool`, which indicates success.
1050    pub fn suspend_source_by_index(&mut self, index: u32, suspend: bool,
1051        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1052    {
1053        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1054            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1055        let ptr = unsafe { capi::pa_context_suspend_source_by_index(self.context, index,
1056            suspend as i32, cb_fn, cb_data) };
1057        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1058    }
1059
1060    /// Changes the profile of a source.
1061    ///
1062    /// Panics on error, i.e. invalid arguments or state.
1063    ///
1064    /// The optional callback must accept a `bool`, which indicates success.
1065    pub fn set_source_port_by_index(&mut self, index: u32, port: &str,
1066        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1067    {
1068        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1069        // as_ptr() giving dangling pointers!
1070        let c_port = CString::new(port).unwrap();
1071
1072        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1073            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1074        let ptr = unsafe { capi::pa_context_set_source_port_by_index(self.context, index,
1075            c_port.as_ptr(), cb_fn, cb_data) };
1076        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1077    }
1078
1079    /// Changes the profile of a source.
1080    ///
1081    /// Panics on error, i.e. invalid arguments or state.
1082    ///
1083    /// The optional callback must accept a `bool`, which indicates success.
1084    pub fn set_source_port_by_name(&mut self, name: &str, port: &str,
1085        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1086    {
1087        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1088        // as_ptr() giving dangling pointers!
1089        let c_name = CString::new(name).unwrap();
1090        let c_port = CString::new(port).unwrap();
1091
1092        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1093            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1094        let ptr = unsafe { capi::pa_context_set_source_port_by_name(self.context, c_name.as_ptr(),
1095            c_port.as_ptr(), cb_fn, cb_data) };
1096        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1097    }
1098}
1099
1100/// Proxy for get source info list callbacks.
1101///
1102/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1103extern "C"
1104fn get_source_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceInfoInternal, eol: i32,
1105    userdata: *mut c_void)
1106{
1107    let _ = std::panic::catch_unwind(|| {
1108        callback_for_list_instance(i, eol, userdata, SourceInfo::new_from_raw);
1109    });
1110}
1111
1112////////////////////////////////////////////////////////////////////////////////////////////////////
1113// Server info
1114////////////////////////////////////////////////////////////////////////////////////////////////////
1115
1116/// Server information.
1117///
1118/// Please note that this structure can be extended as part of evolutionary API updates at any time
1119/// in any new release.
1120#[derive(Debug)]
1121pub struct ServerInfo<'a> {
1122    /// User name of the daemon process.
1123    pub user_name: Option<Cow<'a, str>>,
1124    /// Host name the daemon is running on.
1125    pub host_name: Option<Cow<'a, str>>,
1126    /// Version string of the daemon.
1127    pub server_version: Option<Cow<'a, str>>,
1128    /// Server package name (usually “pulseaudio”).
1129    pub server_name: Option<Cow<'a, str>>,
1130    /// Default sample specification.
1131    pub sample_spec: sample::Spec,
1132    /// Name of default sink.
1133    pub default_sink_name: Option<Cow<'a, str>>,
1134    /// Name of default source.
1135    pub default_source_name: Option<Cow<'a, str>>,
1136    /// A random cookie for identifying this instance of PulseAudio.
1137    pub cookie: u32,
1138    /// Default channel map.
1139    pub channel_map: channelmap::Map,
1140}
1141
1142impl ServerInfo<'_> {
1143    fn new_from_raw(p: *const ServerInfoInternal) -> Self {
1144        assert!(!p.is_null());
1145        let src = unsafe { &*p };
1146        unsafe {
1147            ServerInfo {
1148                user_name: match src.user_name.is_null() {
1149                    false => Some(CStr::from_ptr(src.user_name).to_string_lossy()),
1150                    true => None,
1151                },
1152                host_name: match src.host_name.is_null() {
1153                    false => Some(CStr::from_ptr(src.host_name).to_string_lossy()),
1154                    true => None,
1155                },
1156                server_version: match src.server_version.is_null() {
1157                    false => Some(CStr::from_ptr(src.server_version).to_string_lossy()),
1158                    true => None,
1159                },
1160                server_name: match src.server_name.is_null() {
1161                    false => Some(CStr::from_ptr(src.server_name).to_string_lossy()),
1162                    true => None,
1163                },
1164                sample_spec: src.sample_spec.into(),
1165                default_sink_name: match src.default_sink_name.is_null() {
1166                    false => Some(CStr::from_ptr(src.default_sink_name).to_string_lossy()),
1167                    true => None,
1168                },
1169                default_source_name: match src.default_source_name.is_null() {
1170                    false => Some(CStr::from_ptr(src.default_source_name).to_string_lossy()),
1171                    true => None,
1172                },
1173                cookie: src.cookie,
1174                channel_map: src.channel_map.into(),
1175            }
1176        }
1177    }
1178
1179    /// Creates a copy with owned data.
1180    pub fn to_owned(&self) -> ServerInfo<'static> {
1181        ServerInfo {
1182            user_name: self.user_name.clone().map(|o| Cow::Owned(o.into_owned())),
1183            host_name: self.host_name.clone().map(|o| Cow::Owned(o.into_owned())),
1184            server_version: self.server_version.clone().map(|o| Cow::Owned(o.into_owned())),
1185            server_name: self.server_name.clone().map(|o| Cow::Owned(o.into_owned())),
1186            default_sink_name: self.default_sink_name.clone().map(|o| Cow::Owned(o.into_owned())),
1187            default_source_name: self.default_source_name.clone().map(|o| Cow::Owned(o.into_owned())),
1188            ..*self
1189        }
1190    }
1191}
1192
1193impl Introspector {
1194    /// Gets some information about the server.
1195    ///
1196    /// Panics on error, i.e. invalid arguments or state.
1197    pub fn get_server_info<F>(&self, callback: F) -> Operation<dyn FnMut(&ServerInfo)>
1198        where F: FnMut(&ServerInfo) + 'static
1199    {
1200        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(&ServerInfo)>(Box::new(callback));
1201        let ptr = unsafe { capi::pa_context_get_server_info(self.context,
1202            Some(get_server_info_cb_proxy), cb_data) };
1203        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(&ServerInfo)>)
1204    }
1205}
1206
1207/// Proxy for get server info callbacks.
1208/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1209extern "C"
1210fn get_server_info_cb_proxy(_: *mut ContextInternal, i: *const ServerInfoInternal,
1211    userdata: *mut c_void)
1212{
1213    let _ = std::panic::catch_unwind(|| {
1214        assert!(!i.is_null());
1215        let obj = ServerInfo::new_from_raw(i);
1216
1217        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1218        let mut callback = get_su_callback::<dyn FnMut(&ServerInfo)>(userdata);
1219        (callback)(&obj);
1220    });
1221}
1222
1223////////////////////////////////////////////////////////////////////////////////////////////////////
1224// Module info
1225////////////////////////////////////////////////////////////////////////////////////////////////////
1226
1227/// Stores information about modules.
1228///
1229/// Please note that this structure can be extended as part of evolutionary API updates at any time
1230/// in any new release.
1231#[derive(Debug)]
1232pub struct ModuleInfo<'a> {
1233    /// Index of the module.
1234    pub index: u32,
1235    /// Name of the module.
1236    pub name: Option<Cow<'a, str>>,
1237    /// Argument string of the module.
1238    pub argument: Option<Cow<'a, str>>,
1239    /// Usage counter or `None` if invalid.
1240    pub n_used: Option<u32>,
1241    /// Property list.
1242    pub proplist: Proplist,
1243}
1244
1245impl ModuleInfo<'_> {
1246    fn new_from_raw(p: *const ModuleInfoInternal) -> Self {
1247        assert!(!p.is_null());
1248        let src = unsafe { &*p };
1249        unsafe {
1250            ModuleInfo {
1251                index: src.index,
1252                name: match src.name.is_null() {
1253                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1254                    true => None,
1255                },
1256                argument: match src.argument.is_null() {
1257                    false => Some(CStr::from_ptr(src.argument).to_string_lossy()),
1258                    true => None,
1259                },
1260                n_used: match src.n_used {
1261                    def::INVALID_INDEX => None,
1262                    i => Some(i),
1263                },
1264                proplist: Proplist::from_raw_weak(src.proplist),
1265            }
1266        }
1267    }
1268
1269    /// Creates a copy with owned data.
1270    pub fn to_owned(&self) -> ModuleInfo<'static> {
1271        ModuleInfo {
1272            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
1273            argument: self.argument.clone().map(|o| Cow::Owned(o.into_owned())),
1274            proplist: self.proplist.to_owned(),
1275            ..*self
1276        }
1277    }
1278}
1279
1280impl Introspector {
1281    /// Gets some information about a module by its index.
1282    ///
1283    /// Panics on error, i.e. invalid arguments or state.
1284    pub fn get_module_info<F>(&self, index: u32, callback: F)
1285        -> Operation<dyn FnMut(ListResult<&ModuleInfo>)>
1286        where F: FnMut(ListResult<&ModuleInfo>) + 'static
1287    {
1288        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ModuleInfo>)>(Box::new(callback));
1289        let ptr = unsafe { capi::pa_context_get_module_info(self.context, index,
1290            Some(mod_info_list_cb_proxy), cb_data) };
1291        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ModuleInfo>)>)
1292    }
1293
1294    /// Gets the complete list of currently loaded modules.
1295    ///
1296    /// Panics on error, i.e. invalid arguments or state.
1297    pub fn get_module_info_list<F>(&self, callback: F)
1298        -> Operation<dyn FnMut(ListResult<&ModuleInfo>)>
1299        where F: FnMut(ListResult<&ModuleInfo>) + 'static
1300    {
1301        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ModuleInfo>)>(Box::new(callback));
1302        let ptr = unsafe { capi::pa_context_get_module_info_list(self.context,
1303            Some(mod_info_list_cb_proxy), cb_data) };
1304        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ModuleInfo>)>)
1305    }
1306
1307    /// Loads a module.
1308    ///
1309    /// Panics on error, i.e. invalid arguments or state. The callback is provided with the
1310    /// index.
1311    pub fn load_module<F>(&mut self, name: &str, argument: &str, callback: F)
1312        -> Operation<dyn FnMut(u32)>
1313        where F: FnMut(u32) + 'static
1314    {
1315        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1316        // as_ptr() giving dangling pointers!
1317        let c_name = CString::new(name).unwrap();
1318        let c_arg = CString::new(argument).unwrap();
1319
1320        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(u32)>(Box::new(callback));
1321        let ptr = unsafe { capi::pa_context_load_module(self.context, c_name.as_ptr(),
1322            c_arg.as_ptr(), Some(context_index_cb_proxy), cb_data) };
1323        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(u32)>)
1324    }
1325
1326    /// Unloads a module.
1327    ///
1328    /// Panics on error, i.e. invalid arguments or state.
1329    ///
1330    /// The callback must accept a `bool`, which indicates success.
1331    pub fn unload_module<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
1332        where F: FnMut(bool) + 'static
1333    {
1334        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
1335        let ptr = unsafe { capi::pa_context_unload_module(self.context, index,
1336            Some(super::success_cb_proxy), cb_data) };
1337        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1338    }
1339}
1340
1341/// Proxy for get module info list callbacks.
1342///
1343/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1344extern "C"
1345fn mod_info_list_cb_proxy(_: *mut ContextInternal, i: *const ModuleInfoInternal, eol: i32,
1346    userdata: *mut c_void)
1347{
1348    let _ = std::panic::catch_unwind(|| {
1349        callback_for_list_instance(i, eol, userdata, ModuleInfo::new_from_raw);
1350    });
1351}
1352
1353/// Proxy for context index callbacks.
1354///
1355/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1356extern "C"
1357fn context_index_cb_proxy(_: *mut ContextInternal, index: u32, userdata: *mut c_void) {
1358    let _ = std::panic::catch_unwind(|| {
1359        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1360        let mut callback = get_su_callback::<dyn FnMut(u32)>(userdata);
1361        (callback)(index);
1362    });
1363}
1364
1365////////////////////////////////////////////////////////////////////////////////////////////////////
1366// Messages
1367////////////////////////////////////////////////////////////////////////////////////////////////////
1368
1369impl Introspector {
1370    /// Send a message to an object that registered a message handler.
1371    ///
1372    /// The callback must accept two params, firstly a boolean indicating success if `true`, and
1373    /// secondly, the response string. The response string may possibly not be given if
1374    /// unsuccessful.
1375    ///
1376    /// For more information see the [messaging_api.txt] documentation in the PulseAudio repository.
1377    ///
1378    /// [messaging_api.txt]: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/blob/master/doc/messaging_api.txt
1379    #[cfg(any(doc, feature = "pa_v15"))]
1380    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
1381    pub fn send_message_to_object<F>(&mut self, recipient_name: &str, message: &str,
1382        message_parameters: &str, callback: F) -> Operation<dyn FnMut(bool, Option<String>)>
1383        where F: FnMut(bool, Option<String>) + 'static
1384    {
1385        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1386        // as_ptr() giving dangling pointers!
1387        let c_recipient_name = CString::new(recipient_name.clone()).unwrap();
1388        let c_message = CString::new(message.clone()).unwrap();
1389        let c_message_parameters = CString::new(message_parameters.clone()).unwrap();
1390
1391        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool, Option<String>)>(Box::new(callback));
1392        let ptr = unsafe { capi::pa_context_send_message_to_object(self.context,
1393            c_recipient_name.as_ptr(), c_message.as_ptr(), c_message_parameters.as_ptr(),
1394            Some(send_message_to_object_cb_proxy), cb_data) };
1395        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool, Option<String>)>)
1396    }
1397}
1398
1399/// Proxy for send message to object callbacks.
1400///
1401/// Warning: This is for single-use cases only! It destroys the actual closure callback.
1402#[cfg(any(doc, feature = "pa_v15"))]
1403extern "C"
1404fn send_message_to_object_cb_proxy(_: *mut ContextInternal, success: i32, response: *const c_char,
1405    userdata: *mut c_void)
1406{
1407    let success_actual = match success {
1408        0 => false,
1409        _ => true,
1410    };
1411    let _ = std::panic::catch_unwind(|| {
1412        let r = response.is_null().then(|| {
1413            unsafe { CStr::from_ptr(response) }.to_string_lossy().into_owned()
1414        });
1415        // Note, destroys closure callback after use - restoring outer box means it gets dropped
1416        let mut callback = get_su_callback::<dyn FnMut(bool, Option<String>)>(userdata);
1417        (callback)(success_actual, r);
1418    });
1419}
1420
1421////////////////////////////////////////////////////////////////////////////////////////////////////
1422// Client info
1423////////////////////////////////////////////////////////////////////////////////////////////////////
1424
1425/// Stores information about clients.
1426///
1427/// Please note that this structure can be extended as part of evolutionary API updates at any time
1428/// in any new release.
1429#[derive(Debug)]
1430pub struct ClientInfo<'a> {
1431    /// Index of this client.
1432    pub index: u32,
1433    /// Name of this client.
1434    pub name: Option<Cow<'a, str>>,
1435    /// Index of the owning module, or `None`.
1436    pub owner_module: Option<u32>,
1437    /// Driver name.
1438    pub driver: Option<Cow<'a, str>>,
1439    /// Property list.
1440    pub proplist: Proplist,
1441}
1442
1443impl ClientInfo<'_> {
1444    fn new_from_raw(p: *const ClientInfoInternal) -> Self {
1445        assert!(!p.is_null());
1446        let src = unsafe { &*p };
1447        unsafe {
1448            ClientInfo {
1449                index: src.index,
1450                name: match src.name.is_null() {
1451                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1452                    true => None,
1453                },
1454                owner_module: match src.owner_module {
1455                    def::INVALID_INDEX => None,
1456                    i => Some(i),
1457                },
1458                driver: match src.driver.is_null() {
1459                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1460                    true => None,
1461                },
1462                proplist: Proplist::from_raw_weak(src.proplist),
1463            }
1464        }
1465    }
1466
1467    /// Creates a copy with owned data.
1468    pub fn to_owned(&self) -> ClientInfo<'static> {
1469        ClientInfo {
1470            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
1471            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
1472            proplist: self.proplist.to_owned(),
1473            ..*self
1474        }
1475    }
1476}
1477
1478impl Introspector {
1479    /// Gets information about a client by its index.
1480    ///
1481    /// Panics on error, i.e. invalid arguments or state.
1482    pub fn get_client_info<F>(&self, index: u32, callback: F)
1483        -> Operation<dyn FnMut(ListResult<&ClientInfo>)>
1484        where F: FnMut(ListResult<&ClientInfo>) + 'static
1485    {
1486        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ClientInfo>)>(Box::new(callback));
1487        let ptr = unsafe { capi::pa_context_get_client_info(self.context, index,
1488            Some(get_client_info_list_cb_proxy), cb_data) };
1489        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ClientInfo>)>)
1490    }
1491
1492    /// Gets the complete client list.
1493    ///
1494    /// Panics on error, i.e. invalid arguments or state.
1495    pub fn get_client_info_list<F>(&self, callback: F)
1496        -> Operation<dyn FnMut(ListResult<&ClientInfo>)>
1497        where F: FnMut(ListResult<&ClientInfo>) + 'static
1498    {
1499        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&ClientInfo>)>(Box::new(callback));
1500        let ptr = unsafe { capi::pa_context_get_client_info_list(self.context,
1501            Some(get_client_info_list_cb_proxy), cb_data) };
1502        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&ClientInfo>)>)
1503    }
1504
1505    /// Kills a client.
1506    ///
1507    /// Panics on error, i.e. invalid arguments or state.
1508    ///
1509    /// The callback must accept a `bool`, which indicates success.
1510    pub fn kill_client<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
1511        where F: FnMut(bool) + 'static
1512    {
1513        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
1514        let ptr = unsafe { capi::pa_context_kill_client(self.context, index,
1515            Some(super::success_cb_proxy), cb_data) };
1516        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1517    }
1518}
1519
1520/// Proxy for get sink info list callbacks.
1521///
1522/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1523extern "C"
1524fn get_client_info_list_cb_proxy(_: *mut ContextInternal, i: *const ClientInfoInternal, eol: i32,
1525    userdata: *mut c_void)
1526{
1527    let _ = std::panic::catch_unwind(|| {
1528        callback_for_list_instance(i, eol, userdata, ClientInfo::new_from_raw);
1529    });
1530}
1531
1532////////////////////////////////////////////////////////////////////////////////////////////////////
1533// Card info
1534////////////////////////////////////////////////////////////////////////////////////////////////////
1535
1536/// Stores information about a specific profile of a card.
1537///
1538/// Please note that this structure can be extended as part of evolutionary API updates at any time
1539/// in any new release.
1540#[derive(Debug)]
1541pub struct CardProfileInfo<'a> {
1542    /// Name of this profile.
1543    pub name: Option<Cow<'a, str>>,
1544    /// Description of this profile.
1545    pub description: Option<Cow<'a, str>>,
1546    /// Number of sinks this profile would create.
1547    pub n_sinks: u32,
1548    /// Number of sources this profile would create.
1549    pub n_sources: u32,
1550    /// The higher this value is, the more useful this profile is as a default.
1551    pub priority: u32,
1552    /// Is this profile available? If this is `false`, meaning “unavailable”, then it makes no sense
1553    /// to try to activate this profile. If this is `true`, it’s still not a guarantee that
1554    /// activating the profile will result in anything useful, it just means that the server isn’t
1555    /// aware of any reason why the profile would definitely be useless.
1556    pub available: bool,
1557}
1558
1559impl CardProfileInfo<'_> {
1560    fn new_from_raw(p: *const CardProfileInfoInternal) -> Self {
1561        assert!(!p.is_null());
1562        let src = unsafe { &*p };
1563        unsafe {
1564            CardProfileInfo {
1565                name: match src.name.is_null() {
1566                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1567                    true => None,
1568                },
1569                description: match src.description.is_null() {
1570                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
1571                    true => None,
1572                },
1573                n_sinks: src.n_sinks,
1574                n_sources: src.n_sources,
1575                priority: src.priority,
1576                available: match src.available {
1577                    0 => false,
1578                    _ => true,
1579                },
1580            }
1581        }
1582    }
1583
1584    /// Creates a copy with owned data.
1585    pub fn to_owned(&self) -> CardProfileInfo<'static> {
1586        CardProfileInfo {
1587            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
1588            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
1589            ..*self
1590        }
1591    }
1592}
1593
1594/// Stores information about a specific port of a card.
1595///
1596/// Please note that this structure can be extended as part of evolutionary API updates at any time
1597/// in any new release.
1598#[derive(Debug)]
1599pub struct CardPortInfo<'a> {
1600    /// Name of this port.
1601    pub name: Option<Cow<'a, str>>,
1602    /// Description of this port.
1603    pub description: Option<Cow<'a, str>>,
1604    /// The higher this value is, the more useful this port is as a default.
1605    pub priority: u32,
1606    /// Availability status of this port.
1607    pub available: def::PortAvailable,
1608    /// The direction of this port.
1609    pub direction: direction::FlagSet,
1610    /// Property list.
1611    pub proplist: Proplist,
1612    /// Latency offset of the port that gets added to the sink/source latency when the port is
1613    /// active.
1614    pub latency_offset: i64,
1615    /// Set of available profiles.
1616    pub profiles: Vec<CardProfileInfo<'a>>,
1617    /// An indentifier for the group of ports that share their availability status with each other.
1618    ///
1619    /// This is meant especially for handling cases where one 3.5 mm connector is used for
1620    /// headphones, headsets and microphones, and the hardware can only tell that something was
1621    /// plugged in but not what exactly. In this situation the ports for all those devices share
1622    /// their availability status, and PulseAudio can’t tell which one is actually plugged in, and
1623    /// some application may ask the user what was plugged in. Such applications should get a list
1624    /// of all card ports and compare their `availability_group` fields. Ports that have the same
1625    /// group are those that need input from the user to determine which device was plugged in. The
1626    /// application should then activate the user-chosen port.
1627    ///
1628    /// May be `None`, in which case the port is not part of any availability group (which is the
1629    /// same as having a group with only one member).
1630    ///
1631    /// The group identifier must be treated as an opaque identifier. The string may look like an
1632    /// ALSA control name, but applications must not assume any such relationship. The group naming
1633    /// scheme can change without a warning.
1634    #[cfg(any(doc, feature = "pa_v14"))]
1635    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
1636    pub availability_group: Option<Cow<'a, str>>,
1637    /// Port device type.
1638    #[cfg(any(doc, feature = "pa_v14"))]
1639    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v14")))]
1640    pub r#type: DevicePortType,
1641}
1642
1643impl CardPortInfo<'_> {
1644    fn new_from_raw(p: *const CardPortInfoInternal) -> Self {
1645        assert!(!p.is_null());
1646        let src = unsafe { &*p };
1647
1648        let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
1649
1650        assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1651        for i in 0..src.n_profiles as isize {
1652            let indexed_ptr =
1653                unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
1654            if !indexed_ptr.is_null() {
1655                profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1656            }
1657        }
1658
1659        unsafe {
1660            CardPortInfo {
1661                name: match src.name.is_null() {
1662                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1663                    true => None,
1664                },
1665                description: match src.description.is_null() {
1666                    false => Some(CStr::from_ptr(src.description).to_string_lossy()),
1667                    true => None,
1668                },
1669                priority: src.priority,
1670                available: def::PortAvailable::from_i32(src.available).unwrap(),
1671                direction: direction::FlagSet::from_bits_truncate(src.direction),
1672                proplist: Proplist::from_raw_weak(src.proplist),
1673                latency_offset: src.latency_offset,
1674                profiles: profiles_vec,
1675                #[cfg(any(doc, feature = "pa_v14"))]
1676                availability_group: match src.availability_group.is_null() {
1677                    false => Some(CStr::from_ptr(src.availability_group).to_string_lossy()),
1678                    true => None,
1679                },
1680                #[cfg(any(doc, feature = "pa_v14"))]
1681                r#type: DevicePortType::from_u32(src.r#type).unwrap(),
1682            }
1683        }
1684    }
1685
1686    /// Creates a copy with owned data.
1687    pub fn to_owned(&self) -> CardPortInfo<'static> {
1688        CardPortInfo {
1689            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
1690            description: self.description.clone().map(|o| Cow::Owned(o.into_owned())),
1691            proplist: self.proplist.to_owned(),
1692            profiles: self.profiles.iter().map(CardProfileInfo::to_owned).collect(),
1693            #[cfg(any(doc, feature = "pa_v14"))]
1694            availability_group: self.availability_group.clone().map(|o| Cow::Owned(o.into_owned())),
1695            ..*self
1696        }
1697    }
1698}
1699
1700/// Stores information about cards.
1701///
1702/// Please note that this structure can be extended as part of evolutionary API updates at any time
1703/// in any new release.
1704#[derive(Debug)]
1705pub struct CardInfo<'a> {
1706    /// Index of this card.
1707    pub index: u32,
1708    /// Name of this card.
1709    pub name: Option<Cow<'a, str>>,
1710    /// Index of the owning module, or `None`.
1711    pub owner_module: Option<u32>,
1712    /// Driver name.
1713    pub driver: Option<Cow<'a, str>>,
1714    /// Property list.
1715    pub proplist: Proplist,
1716    /// Set of ports.
1717    pub ports: Vec<CardPortInfo<'a>>,
1718    /// Set of available profiles.
1719    pub profiles: Vec<CardProfileInfo<'a>>,
1720    /// Pointer to active profile in the set, or `None`.
1721    pub active_profile: Option<Box<CardProfileInfo<'a>>>,
1722}
1723
1724impl CardInfo<'_> {
1725    fn new_from_raw(p: *const CardInfoInternal) -> Self {
1726        assert!(!p.is_null());
1727        let src = unsafe { &*p };
1728
1729        let mut ports_vec = Vec::with_capacity(src.n_ports as usize);
1730        assert!(src.n_ports == 0 || !src.ports.is_null());
1731        for i in 0..src.n_ports as isize {
1732            let indexed_ptr = unsafe { (*src.ports.offset(i)) as *mut CardPortInfoInternal };
1733            if !indexed_ptr.is_null() {
1734                ports_vec.push(CardPortInfo::new_from_raw(indexed_ptr));
1735            }
1736        }
1737        let mut profiles_vec = Vec::with_capacity(src.n_profiles as usize);
1738
1739        assert!(src.n_profiles == 0 || !src.profiles2.is_null());
1740        for i in 0..src.n_profiles as isize {
1741            let indexed_ptr =
1742                unsafe { (*src.profiles2.offset(i)) as *mut CardProfileInfoInternal };
1743            if !indexed_ptr.is_null() {
1744                profiles_vec.push(CardProfileInfo::new_from_raw(indexed_ptr));
1745            }
1746        }
1747
1748        unsafe {
1749            CardInfo {
1750                index: src.index,
1751                name: match src.name.is_null() {
1752                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1753                    true => None,
1754                },
1755                owner_module: match src.owner_module {
1756                    def::INVALID_INDEX => None,
1757                    i => Some(i),
1758                },
1759                driver: match src.driver.is_null() {
1760                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1761                    true => None,
1762                },
1763                proplist: Proplist::from_raw_weak(src.proplist),
1764                ports: ports_vec,
1765                profiles: profiles_vec,
1766                active_profile: match src.active_profile2.is_null() {
1767                    true => None,
1768                    false => Some(Box::new(CardProfileInfo::new_from_raw(src.active_profile2))),
1769                },
1770            }
1771        }
1772    }
1773
1774    /// Creates a copy with owned data.
1775    pub fn to_owned(&self) -> CardInfo<'static> {
1776        CardInfo {
1777            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
1778            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
1779            proplist: self.proplist.to_owned(),
1780            ports: self.ports.iter().map(CardPortInfo::to_owned).collect(),
1781            profiles: self.profiles.iter().map(CardProfileInfo::to_owned).collect(),
1782            active_profile: self.active_profile.as_ref().map(|ap| Box::new(ap.as_ref().to_owned())),
1783            ..*self
1784        }
1785    }
1786}
1787
1788impl Introspector {
1789    /// Gets information about a card by its index.
1790    ///
1791    /// Panics on error, i.e. invalid arguments or state.
1792    pub fn get_card_info_by_index<F>(&self, index: u32, callback: F)
1793        -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1794        where F: FnMut(ListResult<&CardInfo>) + 'static
1795    {
1796        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1797        let ptr = unsafe { capi::pa_context_get_card_info_by_index(self.context, index,
1798            Some(get_card_info_list_cb_proxy), cb_data) };
1799        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1800    }
1801
1802    /// Gets information about a card by its name.
1803    ///
1804    /// Panics on error, i.e. invalid arguments or state.
1805    pub fn get_card_info_by_name<F>(&self, name: &str, callback: F)
1806        -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1807        where F: FnMut(ListResult<&CardInfo>) + 'static
1808    {
1809        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1810        // as_ptr() giving dangling pointers!
1811        let c_name = CString::new(name).unwrap();
1812
1813        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1814        let ptr = unsafe { capi::pa_context_get_card_info_by_name(self.context, c_name.as_ptr(),
1815            Some(get_card_info_list_cb_proxy), cb_data) };
1816        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1817    }
1818
1819    /// Gets the complete card list.
1820    ///
1821    /// Panics on error, i.e. invalid arguments or state.
1822    pub fn get_card_info_list<F>(&self, callback: F) -> Operation<dyn FnMut(ListResult<&CardInfo>)>
1823        where F: FnMut(ListResult<&CardInfo>) + 'static
1824    {
1825        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&CardInfo>)>(Box::new(callback));
1826        let ptr = unsafe { capi::pa_context_get_card_info_list(self.context,
1827            Some(get_card_info_list_cb_proxy), cb_data) };
1828        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&CardInfo>)>)
1829    }
1830
1831    /// Changes the profile of a card.
1832    ///
1833    /// Panics on error, i.e. invalid arguments or state.
1834    ///
1835    /// The optional callback must accept a `bool`, which indicates success.
1836    pub fn set_card_profile_by_index(&mut self, index: u32, profile: &str,
1837        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1838    {
1839        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1840        // as_ptr() giving dangling pointers!
1841        let c_profile = CString::new(profile).unwrap();
1842
1843        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1844            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1845        let ptr = unsafe { capi::pa_context_set_card_profile_by_index(self.context, index,
1846            c_profile.as_ptr(), cb_fn, cb_data) };
1847        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1848    }
1849
1850    /// Changes the profile of a card.
1851    ///
1852    /// Panics on error, i.e. invalid arguments or state.
1853    ///
1854    /// The optional callback must accept a `bool`, which indicates success.
1855    pub fn set_card_profile_by_name(&mut self, name: &str, profile: &str,
1856        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1857    {
1858        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1859        // as_ptr() giving dangling pointers!
1860        let c_name = CString::new(name).unwrap();
1861        let c_profile = CString::new(profile).unwrap();
1862
1863        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1864            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1865        let ptr = unsafe { capi::pa_context_set_card_profile_by_name(self.context, c_name.as_ptr(),
1866            c_profile.as_ptr(), cb_fn, cb_data) };
1867        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1868    }
1869
1870    /// Sets the latency offset of a port.
1871    ///
1872    /// Panics on error, i.e. invalid arguments or state.
1873    ///
1874    /// The optional callback must accept a `bool`, which indicates success.
1875    pub fn set_port_latency_offset(&mut self, card_name: &str, port_name: &str, offset: i64,
1876        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
1877    {
1878        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
1879        // as_ptr() giving dangling pointers!
1880        let c_name = CString::new(card_name).unwrap();
1881        let c_port = CString::new(port_name).unwrap();
1882
1883        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
1884            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
1885        let ptr = unsafe { capi::pa_context_set_port_latency_offset(self.context, c_name.as_ptr(),
1886            c_port.as_ptr(), offset, cb_fn, cb_data) };
1887        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
1888    }
1889}
1890
1891/// Proxy for get card info list callbacks.
1892///
1893/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
1894extern "C"
1895fn get_card_info_list_cb_proxy(_: *mut ContextInternal, i: *const CardInfoInternal, eol: i32,
1896    userdata: *mut c_void)
1897{
1898    let _ = std::panic::catch_unwind(|| {
1899        callback_for_list_instance(i, eol, userdata, CardInfo::new_from_raw);
1900    });
1901}
1902
1903////////////////////////////////////////////////////////////////////////////////////////////////////
1904// Sink input info
1905////////////////////////////////////////////////////////////////////////////////////////////////////
1906
1907/// Stores information about sink inputs.
1908///
1909/// Please note that this structure can be extended as part of evolutionary API updates at any time
1910/// in any new release.
1911#[derive(Debug)]
1912pub struct SinkInputInfo<'a> {
1913    /// Index of the sink input.
1914    pub index: u32,
1915    /// Name of the sink input.
1916    pub name: Option<Cow<'a, str>>,
1917    /// Index of the module this sink input belongs to, or `None` when it does not belong to any
1918    /// module.
1919    pub owner_module: Option<u32>,
1920    /// Index of the client this sink input belongs to, or invalid when it does not belong to any
1921    /// client.
1922    pub client: Option<u32>,
1923    /// Index of the connected sink.
1924    pub sink: u32,
1925    /// The sample specification of the sink input.
1926    pub sample_spec: sample::Spec,
1927    /// Channel map.
1928    pub channel_map: channelmap::Map,
1929    /// The volume of this sink input.
1930    pub volume: ChannelVolumes,
1931    /// Latency due to buffering in sink input, see [`TimingInfo`](crate::def::TimingInfo) for
1932    /// details.
1933    pub buffer_usec: MicroSeconds,
1934    /// Latency of the sink device, see [`TimingInfo`](crate::def::TimingInfo) for details.
1935    pub sink_usec: MicroSeconds,
1936    /// The resampling method used by this sink input.
1937    pub resample_method: Option<Cow<'a, str>>,
1938    /// Driver name.
1939    pub driver: Option<Cow<'a, str>>,
1940    /// Stream muted.
1941    pub mute: bool,
1942    /// Property list.
1943    pub proplist: Proplist,
1944    /// Stream corked.
1945    pub corked: bool,
1946    /// Stream has volume. If not set, then the meaning of this struct’s volume member is
1947    /// unspecified.
1948    pub has_volume: bool,
1949    /// The volume can be set. If not set, the volume can still change even though clients can’t
1950    /// control the volume.
1951    pub volume_writable: bool,
1952    /// Stream format information.
1953    pub format: format::Info,
1954}
1955
1956impl SinkInputInfo<'_> {
1957    fn new_from_raw(p: *const SinkInputInfoInternal) -> Self {
1958        assert!(!p.is_null());
1959        let src = unsafe { &*p };
1960        unsafe {
1961            SinkInputInfo {
1962                index: src.index,
1963                name: match src.name.is_null() {
1964                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
1965                    true => None,
1966                },
1967                owner_module: match src.owner_module {
1968                    def::INVALID_INDEX => None,
1969                    i => Some(i),
1970                },
1971                client: match src.client {
1972                    def::INVALID_INDEX => None,
1973                    i => Some(i),
1974                },
1975                sink: src.sink,
1976                sample_spec: src.sample_spec.into(),
1977                channel_map: src.channel_map.into(),
1978                volume: src.volume.into(),
1979                buffer_usec: MicroSeconds(src.buffer_usec),
1980                sink_usec: MicroSeconds(src.sink_usec),
1981                resample_method: match src.resample_method.is_null() {
1982                    false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()),
1983                    true => None,
1984                },
1985                driver: match src.driver.is_null() {
1986                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
1987                    true => None,
1988                },
1989                mute: match src.mute {
1990                    0 => false,
1991                    _ => true,
1992                },
1993                proplist: Proplist::from_raw_weak(src.proplist),
1994                corked: match src.corked {
1995                    0 => false,
1996                    _ => true,
1997                },
1998                has_volume: match src.has_volume {
1999                    0 => false,
2000                    _ => true,
2001                },
2002                volume_writable: match src.volume_writable {
2003                    0 => false,
2004                    _ => true,
2005                },
2006                format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal),
2007            }
2008        }
2009    }
2010
2011    /// Creates a copy with owned data.
2012    pub fn to_owned(&self) -> SinkInputInfo<'static> {
2013        SinkInputInfo {
2014            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
2015            resample_method: self.resample_method.clone().map(|o| Cow::Owned(o.into_owned())),
2016            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
2017            proplist: self.proplist.to_owned(),
2018            format: self.format.to_owned(),
2019            ..*self
2020        }
2021    }
2022}
2023
2024impl Introspector {
2025    /// Gets some information about a sink input by its index.
2026    ///
2027    /// Panics on error, i.e. invalid arguments or state.
2028    pub fn get_sink_input_info<F>(&self, index: u32, callback: F)
2029        -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
2030        where F: FnMut(ListResult<&SinkInputInfo>) + 'static
2031    {
2032        let cb_data =
2033            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInputInfo>)>(Box::new(callback));
2034        let ptr = unsafe { capi::pa_context_get_sink_input_info(self.context, index,
2035            Some(get_sink_input_info_list_cb_proxy), cb_data) };
2036        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInputInfo>)>)
2037    }
2038
2039    /// Gets the complete sink input list.
2040    ///
2041    /// Panics on error, i.e. invalid arguments or state.
2042    pub fn get_sink_input_info_list<F>(&self, callback: F)
2043        -> Operation<dyn FnMut(ListResult<&SinkInputInfo>)>
2044        where F: FnMut(ListResult<&SinkInputInfo>) + 'static
2045    {
2046        let cb_data =
2047            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SinkInputInfo>)>(Box::new(callback));
2048        let ptr = unsafe { capi::pa_context_get_sink_input_info_list(self.context,
2049            Some(get_sink_input_info_list_cb_proxy), cb_data) };
2050        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SinkInputInfo>)>)
2051    }
2052
2053    /// Moves the specified sink input to a different sink.
2054    ///
2055    /// Panics on error, i.e. invalid arguments or state.
2056    ///
2057    /// The optional callback must accept a `bool`, which indicates success.
2058    pub fn move_sink_input_by_name(&mut self, index: u32, sink_name: &str,
2059        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2060    {
2061        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
2062        // as_ptr() giving dangling pointers!
2063        let c_name = CString::new(sink_name).unwrap();
2064
2065        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2066            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2067        let ptr = unsafe { capi::pa_context_move_sink_input_by_name(self.context, index,
2068            c_name.as_ptr(), cb_fn, cb_data) };
2069        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2070    }
2071
2072    /// Moves the specified sink input to a different sink.
2073    ///
2074    /// Panics on error, i.e. invalid arguments or state.
2075    ///
2076    /// The optional callback must accept a `bool`, which indicates success.
2077    pub fn move_sink_input_by_index(&mut self, index: u32, sink_index: u32,
2078        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2079    {
2080        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2081            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2082        let ptr = unsafe { capi::pa_context_move_sink_input_by_index(self.context, index,
2083            sink_index, cb_fn, cb_data) };
2084        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2085    }
2086
2087    /// Sets the volume of a sink input stream.
2088    ///
2089    /// Panics on error, i.e. invalid arguments or state.
2090    ///
2091    /// The optional callback must accept a `bool`, which indicates success.
2092    pub fn set_sink_input_volume(&mut self, index: u32, volume: &ChannelVolumes,
2093        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2094    {
2095        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2096            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2097        let ptr = unsafe { capi::pa_context_set_sink_input_volume(self.context, index,
2098            volume.as_ref(), cb_fn, cb_data) };
2099        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2100    }
2101
2102    /// Sets the mute switch of a sink input stream.
2103    ///
2104    /// Panics on error, i.e. invalid arguments or state.
2105    ///
2106    /// The optional callback must accept a `bool`, which indicates success.
2107    pub fn set_sink_input_mute(&mut self, index: u32, mute: bool,
2108        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2109    {
2110        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2111            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2112        let ptr = unsafe { capi::pa_context_set_sink_input_mute(self.context, index, mute as i32,
2113            cb_fn, cb_data) };
2114        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2115    }
2116
2117    /// Kills a sink input.
2118    ///
2119    /// Panics on error, i.e. invalid arguments or state.
2120    ///
2121    /// The callback must accept a `bool`, which indicates success.
2122    pub fn kill_sink_input<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
2123        where F: FnMut(bool) + 'static
2124    {
2125        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
2126        let ptr = unsafe { capi::pa_context_kill_sink_input(self.context, index,
2127            Some(super::success_cb_proxy), cb_data) };
2128        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2129    }
2130}
2131
2132/// Proxy for get sink input info list callbacks.
2133///
2134/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2135extern "C"
2136fn get_sink_input_info_list_cb_proxy(_: *mut ContextInternal, i: *const SinkInputInfoInternal,
2137    eol: i32, userdata: *mut c_void)
2138{
2139    let _ = std::panic::catch_unwind(|| {
2140        callback_for_list_instance(i, eol, userdata, SinkInputInfo::new_from_raw);
2141    });
2142}
2143
2144////////////////////////////////////////////////////////////////////////////////////////////////////
2145// Source output info
2146////////////////////////////////////////////////////////////////////////////////////////////////////
2147
2148/// Stores information about source outputs.
2149///
2150/// Please note that this structure can be extended as part of evolutionary API updates at any time
2151/// in any new release.
2152#[derive(Debug)]
2153pub struct SourceOutputInfo<'a> {
2154    /// Index of the source output.
2155    pub index: u32,
2156    /// Name of the source output.
2157    pub name: Option<Cow<'a, str>>,
2158    /// Index of the module this source output belongs to, or `None` when it does not belong to any
2159    /// module.
2160    pub owner_module: Option<u32>,
2161    /// Index of the client this source output belongs to, or `None` when it does not belong to any
2162    /// client.
2163    pub client: Option<u32>,
2164    /// Index of the connected source.
2165    pub source: u32,
2166    /// The sample specification of the source output.
2167    pub sample_spec: sample::Spec,
2168    /// Channel map.
2169    pub channel_map: channelmap::Map,
2170    /// Latency due to buffering in the source output, see [`TimingInfo`](crate::def::TimingInfo)
2171    /// for details.
2172    pub buffer_usec: MicroSeconds,
2173    /// Latency of the source device, see [`TimingInfo`](crate::def::TimingInfo) for details.
2174    pub source_usec: MicroSeconds,
2175    /// The resampling method used by this source output.
2176    pub resample_method: Option<Cow<'a, str>>,
2177    /// Driver name.
2178    pub driver: Option<Cow<'a, str>>,
2179    /// Property list.
2180    pub proplist: Proplist,
2181    /// Stream corked.
2182    pub corked: bool,
2183    /// The volume of this source output.
2184    pub volume: ChannelVolumes,
2185    /// Stream muted.
2186    pub mute: bool,
2187    /// Stream has volume. If not set, then the meaning of this struct’s volume member is
2188    /// unspecified.
2189    pub has_volume: bool,
2190    /// The volume can be set. If not set, the volume can still change even though clients can’t
2191    /// control the volume.
2192    pub volume_writable: bool,
2193    /// Stream format information.
2194    pub format: format::Info,
2195}
2196
2197impl SourceOutputInfo<'_> {
2198    fn new_from_raw(p: *const SourceOutputInfoInternal) -> Self {
2199        assert!(!p.is_null());
2200        let src = unsafe { &*p };
2201        unsafe {
2202            SourceOutputInfo {
2203                index: src.index,
2204                name: match src.name.is_null() {
2205                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
2206                    true => None,
2207                },
2208                owner_module: match src.owner_module {
2209                    def::INVALID_INDEX => None,
2210                    i => Some(i),
2211                },
2212                client: match src.client {
2213                    def::INVALID_INDEX => None,
2214                    i => Some(i),
2215                },
2216                source: src.source,
2217                sample_spec: src.sample_spec.into(),
2218                channel_map: src.channel_map.into(),
2219                buffer_usec: MicroSeconds(src.buffer_usec),
2220                source_usec: MicroSeconds(src.source_usec),
2221                resample_method: match src.resample_method.is_null() {
2222                    false => Some(CStr::from_ptr(src.resample_method).to_string_lossy()),
2223                    true => None,
2224                },
2225                driver: match src.driver.is_null() {
2226                    false => Some(CStr::from_ptr(src.driver).to_string_lossy()),
2227                    true => None,
2228                },
2229                proplist: Proplist::from_raw_weak(src.proplist),
2230                corked: match src.corked {
2231                    0 => false,
2232                    _ => true,
2233                },
2234                volume: src.volume.into(),
2235                mute: match src.mute {
2236                    0 => false,
2237                    _ => true,
2238                },
2239                has_volume: match src.has_volume {
2240                    0 => false,
2241                    _ => true,
2242                },
2243                volume_writable: match src.volume_writable {
2244                    0 => false,
2245                    _ => true,
2246                },
2247                format: format::Info::from_raw_weak(src.format as *mut format::InfoInternal),
2248            }
2249        }
2250    }
2251
2252    /// Creates a copy with owned data.
2253    pub fn to_owned(&self) -> SourceOutputInfo<'static> {
2254        SourceOutputInfo {
2255            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
2256            resample_method: self.resample_method.clone().map(|o| Cow::Owned(o.into_owned())),
2257            driver: self.driver.clone().map(|o| Cow::Owned(o.into_owned())),
2258            proplist: self.proplist.to_owned(),
2259            format: self.format.to_owned(),
2260            ..*self
2261        }
2262    }
2263}
2264
2265impl Introspector {
2266    /// Gets information about a source output by its index.
2267    ///
2268    /// Panics on error, i.e. invalid arguments or state.
2269    pub fn get_source_output_info<F>(&self, index: u32, callback: F)
2270        -> Operation<dyn FnMut(ListResult<&SourceOutputInfo>)>
2271        where F: FnMut(ListResult<&SourceOutputInfo>) + 'static
2272    {
2273        let cb_data =
2274            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceOutputInfo>)>(Box::new(callback));
2275        let ptr = unsafe { capi::pa_context_get_source_output_info(self.context, index,
2276            Some(get_source_output_info_list_cb_proxy), cb_data) };
2277        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceOutputInfo>)>)
2278    }
2279
2280    /// Gets the complete list of source outputs.
2281    ///
2282    /// Panics on error, i.e. invalid arguments or state.
2283    pub fn get_source_output_info_list<F>(&self, callback: F)
2284        -> Operation<dyn FnMut(ListResult<&SourceOutputInfo>)>
2285        where F: FnMut(ListResult<&SourceOutputInfo>) + 'static
2286    {
2287        let cb_data =
2288            box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SourceOutputInfo>)>(Box::new(callback));
2289        let ptr = unsafe { capi::pa_context_get_source_output_info_list(self.context,
2290            Some(get_source_output_info_list_cb_proxy), cb_data) };
2291        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SourceOutputInfo>)>)
2292    }
2293
2294    /// Moves the specified source output to a different source.
2295    ///
2296    /// Panics on error, i.e. invalid arguments or state.
2297    ///
2298    /// The optional callback must accept a `bool`, which indicates success.
2299    pub fn move_source_output_by_name(&mut self, index: u32, source_name: &str,
2300        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2301    {
2302        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
2303        // as_ptr() giving dangling pointers!
2304        let c_name = CString::new(source_name).unwrap();
2305
2306        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2307            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2308        let ptr = unsafe { capi::pa_context_move_source_output_by_name(self.context, index,
2309            c_name.as_ptr(), cb_fn, cb_data) };
2310        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2311    }
2312
2313    /// Moves the specified source output to a different source.
2314    ///
2315    /// Panics on error, i.e. invalid arguments or state.
2316    ///
2317    /// The optional callback must accept a `bool`, which indicates success.
2318    pub fn move_source_output_by_index(&mut self, index: u32, source_index: u32,
2319        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2320    {
2321        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2322            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2323        let ptr = unsafe { capi::pa_context_move_source_output_by_index(self.context, index,
2324            source_index, cb_fn, cb_data) };
2325        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2326    }
2327
2328    /// Sets the volume of a source output stream.
2329    ///
2330    /// Panics on error, i.e. invalid arguments or state.
2331    ///
2332    /// The optional callback must accept a `bool`, which indicates success.
2333    pub fn set_source_output_volume(&mut self, index: u32, volume: &ChannelVolumes,
2334        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2335    {
2336        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2337            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2338        let ptr = unsafe { capi::pa_context_set_source_output_volume(self.context, index,
2339            volume.as_ref(), cb_fn, cb_data) };
2340        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2341    }
2342
2343    /// Sets the mute switch of a source output stream.
2344    ///
2345    /// Panics on error, i.e. invalid arguments or state.
2346    ///
2347    /// The optional callback must accept a `bool`, which indicates success.
2348    pub fn set_source_output_mute(&mut self, index: u32, mute: bool,
2349        callback: Option<Box<dyn FnMut(bool) + 'static>>) -> Operation<dyn FnMut(bool)>
2350    {
2351        let (cb_fn, cb_data): (Option<extern "C" fn(_, _, _)>, _) =
2352            get_su_capi_params::<_, _>(callback, super::success_cb_proxy);
2353        let ptr = unsafe { capi::pa_context_set_source_output_mute(self.context, index, mute as i32,
2354            cb_fn, cb_data) };
2355        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2356    }
2357
2358    /// Kills a source output.
2359    ///
2360    /// Panics on error, i.e. invalid arguments or state.
2361    ///
2362    /// The callback must accept a `bool`, which indicates success.
2363    pub fn kill_source_output<F>(&mut self, index: u32, callback: F) -> Operation<dyn FnMut(bool)>
2364        where F: FnMut(bool) + 'static
2365    {
2366        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
2367        let ptr = unsafe { capi::pa_context_kill_source_output(self.context, index,
2368            Some(super::success_cb_proxy), cb_data) };
2369        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
2370    }
2371}
2372
2373/// Proxy for get source output info list callbacks.
2374///
2375/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2376extern "C"
2377fn get_source_output_info_list_cb_proxy(_: *mut ContextInternal, i: *const SourceOutputInfoInternal,
2378    eol: i32, userdata: *mut c_void)
2379{
2380    let _ = std::panic::catch_unwind(|| {
2381        callback_for_list_instance(i, eol, userdata, SourceOutputInfo::new_from_raw);
2382    });
2383}
2384
2385////////////////////////////////////////////////////////////////////////////////////////////////////
2386// Stat info
2387////////////////////////////////////////////////////////////////////////////////////////////////////
2388
2389impl Introspector {
2390    /// Gets daemon memory block statistics.
2391    ///
2392    /// Panics on error, i.e. invalid arguments or state.
2393    pub fn stat<F>(&self, callback: F) -> Operation<dyn FnMut(&StatInfo)>
2394        where F: FnMut(&StatInfo) + 'static
2395    {
2396        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(&StatInfo)>(Box::new(callback));
2397        let ptr =
2398            unsafe { capi::pa_context_stat(self.context, Some(get_stat_info_cb_proxy), cb_data) };
2399        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(&StatInfo)>)
2400    }
2401}
2402
2403/// Proxy for get stat info callbacks.
2404///
2405/// Warning: This is for single-use cases only! It destroys the actual closure callback.
2406extern "C"
2407fn get_stat_info_cb_proxy(_: *mut ContextInternal, i: *const StatInfo, userdata: *mut c_void) {
2408    let _ = std::panic::catch_unwind(|| {
2409        assert!(!i.is_null());
2410        // Note, destroys closure callback after use - restoring outer box means it gets dropped
2411        let mut callback = get_su_callback::<dyn FnMut(&StatInfo)>(userdata);
2412        (callback)(unsafe { &*i });
2413    });
2414}
2415
2416////////////////////////////////////////////////////////////////////////////////////////////////////
2417// Sample info
2418////////////////////////////////////////////////////////////////////////////////////////////////////
2419
2420/// Stores information about sample cache entries.
2421///
2422/// Please note that this structure can be extended as part of evolutionary API updates at any time
2423/// in any new release.
2424#[derive(Debug)]
2425pub struct SampleInfo<'a> {
2426    /// Index of this entry.
2427    pub index: u32,
2428    /// Name of this entry.
2429    pub name: Option<Cow<'a, str>>,
2430    /// Default volume of this entry.
2431    pub volume: ChannelVolumes,
2432    /// Sample specification of the sample.
2433    pub sample_spec: sample::Spec,
2434    /// The channel map.
2435    pub channel_map: channelmap::Map,
2436    /// Duration of this entry.
2437    pub duration: MicroSeconds,
2438    /// Length of this sample in bytes.
2439    pub bytes: u32,
2440    /// Non-zero when this is a lazy cache entry.
2441    pub lazy: bool,
2442    /// In case this is a lazy cache entry, the filename for the sound file to be loaded on demand.
2443    pub filename: Option<Cow<'a, str>>,
2444    /// Property list for this sample.
2445    pub proplist: Proplist,
2446}
2447
2448impl SampleInfo<'_> {
2449    fn new_from_raw(p: *const SampleInfoInternal) -> Self {
2450        assert!(!p.is_null());
2451        let src = unsafe { &*p };
2452        unsafe {
2453            SampleInfo {
2454                index: src.index,
2455                name: match src.name.is_null() {
2456                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
2457                    true => None,
2458                },
2459                volume: src.volume.into(),
2460                sample_spec: src.sample_spec.into(),
2461                channel_map: src.channel_map.into(),
2462                duration: MicroSeconds(src.duration),
2463                bytes: src.bytes,
2464                lazy: match src.lazy {
2465                    0 => false,
2466                    _ => true,
2467                },
2468                filename: match src.filename.is_null() {
2469                    false => Some(CStr::from_ptr(src.filename).to_string_lossy()),
2470                    true => None,
2471                },
2472                proplist: Proplist::from_raw_weak(src.proplist),
2473            }
2474        }
2475    }
2476
2477    /// Creates a copy with owned data.
2478    pub fn to_owned(&self) -> SampleInfo<'static> {
2479        SampleInfo {
2480            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
2481            filename: self.filename.clone().map(|o| Cow::Owned(o.into_owned())),
2482            proplist: self.proplist.to_owned(),
2483            ..*self
2484        }
2485    }
2486}
2487
2488impl Introspector {
2489    /// Gets information about a sample by its name.
2490    ///
2491    /// Panics on error, i.e. invalid arguments or state.
2492    pub fn get_sample_info_by_name<F>(&self, name: &str, callback: F)
2493        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2494        where F: FnMut(ListResult<&SampleInfo>) + 'static
2495    {
2496        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
2497        // as_ptr() giving dangling pointers!
2498        let c_name = CString::new(name).unwrap();
2499
2500        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2501        let ptr = unsafe { capi::pa_context_get_sample_info_by_name(self.context, c_name.as_ptr(),
2502            Some(get_sample_info_list_cb_proxy), cb_data) };
2503        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2504    }
2505
2506    /// Gets information about a sample by its index.
2507    ///
2508    /// Panics on error, i.e. invalid arguments or state.
2509    pub fn get_sample_info_by_index<F>(&self, index: u32, callback: F)
2510        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2511        where F: FnMut(ListResult<&SampleInfo>) + 'static
2512    {
2513        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2514        let ptr = unsafe { capi::pa_context_get_sample_info_by_index(self.context, index,
2515            Some(get_sample_info_list_cb_proxy), cb_data) };
2516        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2517    }
2518
2519    /// Gets the complete list of samples stored in the daemon.
2520    ///
2521    /// Panics on error, i.e. invalid arguments or state.
2522    pub fn get_sample_info_list<F>(&self, callback: F)
2523        -> Operation<dyn FnMut(ListResult<&SampleInfo>)>
2524        where F: FnMut(ListResult<&SampleInfo>) + 'static
2525    {
2526        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&SampleInfo>)>(Box::new(callback));
2527        let ptr = unsafe { capi::pa_context_get_sample_info_list(self.context,
2528            Some(get_sample_info_list_cb_proxy), cb_data) };
2529        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&SampleInfo>)>)
2530    }
2531}
2532
2533/// Proxy for get sample info list callbacks.
2534///
2535/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
2536extern "C"
2537fn get_sample_info_list_cb_proxy(_: *mut ContextInternal, i: *const SampleInfoInternal, eol: i32,
2538    userdata: *mut c_void)
2539{
2540    let _ = std::panic::catch_unwind(|| {
2541        callback_for_list_instance(i, eol, userdata, SampleInfo::new_from_raw);
2542    });
2543}