libpulse_binding/context/
ext_device_restore.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 controlling module-device-restore.
15
16use std::os::raw::c_void;
17use std::ptr::null_mut;
18use std::mem;
19use capi::pa_ext_device_restore_info as InfoInternal;
20use super::{ContextInternal, Context};
21use crate::{def, format};
22use crate::callbacks::{
23    ListResult, box_closure_get_capi_ptr, callback_for_list_instance, MultiUseCallback
24};
25use crate::operation::Operation;
26
27/// Stores information about one device in the device database that is maintained by
28/// module-device-manager.
29#[derive(Debug)]
30pub struct Info {
31    /// Device type sink or source?
32    pub dtype: def::Device,
33    /// The device index.
34    pub index: u32,
35    /// A set of formats.
36    pub formats: Vec<format::Info>,
37}
38
39impl Info {
40    fn new_from_raw(p: *const InfoInternal) -> Self {
41        assert!(!p.is_null());
42        let src = unsafe { p.as_ref().unwrap() };
43
44        let mut formats_vec = Vec::with_capacity(src.n_formats as usize);
45        assert!(src.n_formats == 0 || !src.formats.is_null());
46        for i in 0..src.n_formats as isize {
47            let indexed_ptr = unsafe { (*src.formats.offset(i)) as *mut format::InfoInternal };
48            if !indexed_ptr.is_null() {
49                formats_vec.push(format::Info::from_raw_weak(indexed_ptr));
50            }
51        }
52
53        Info { dtype: src.dtype, index: src.index, formats: formats_vec }
54    }
55}
56
57/// A wrapper object providing device restore routines to a context.
58///
59/// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop.
60pub struct DeviceRestore {
61    context: *mut ContextInternal,
62    /// Multi-use callback closure pointers
63    cb_ptrs: CallbackPointers,
64}
65
66unsafe impl Send for DeviceRestore {}
67unsafe impl Sync for DeviceRestore {}
68
69/// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple
70/// times), for freeing at the appropriate time.
71#[derive(Default)]
72struct CallbackPointers {
73    subscribe: SubscribeCb,
74}
75
76type SubscribeCb = MultiUseCallback<dyn FnMut(def::Device, u32),
77    extern "C" fn(*mut ContextInternal, def::Device, u32, *mut c_void)>;
78
79impl Context {
80    /// Gets a device restore object linked to the current context, giving access to device restore
81    /// routines.
82    ///
83    /// See [`context::ext_device_restore`](mod@crate::context::ext_device_restore).
84    pub fn device_restore(&self) -> DeviceRestore {
85        unsafe { capi::pa_context_ref(self.ptr) };
86        DeviceRestore::from_raw(self.ptr)
87    }
88}
89
90impl DeviceRestore {
91    /// Creates a new `DeviceRestore` from an existing [`ContextInternal`] pointer.
92    fn from_raw(context: *mut ContextInternal) -> Self {
93        Self { context: context, cb_ptrs: Default::default() }
94    }
95
96    /// Tests if this extension module is available in the server.
97    ///
98    /// The callback must accept an integer, which indicates version.
99    ///
100    /// Panics if the underlying C function returns a null pointer.
101    pub fn test<F>(&mut self, callback: F) -> Operation<dyn FnMut(u32)>
102        where F: FnMut(u32) + 'static
103    {
104        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(u32)>(Box::new(callback));
105        let ptr = unsafe { capi::pa_ext_device_restore_test(self.context,
106            Some(super::ext_test_cb_proxy), cb_data) };
107        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(u32)>)
108    }
109
110    /// Subscribes to changes in the device database.
111    ///
112    /// The callback must accept a `bool`, which indicates success.
113    ///
114    /// Panics if the underlying C function returns a null pointer.
115    pub fn subscribe<F>(&mut self, enable: bool, callback: F) -> Operation<dyn FnMut(bool)>
116        where F: FnMut(bool) + 'static
117    {
118        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
119        let ptr = unsafe { capi::pa_ext_device_restore_subscribe(self.context, enable as i32,
120            Some(super::success_cb_proxy), cb_data) };
121        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
122    }
123
124    /// Sets the subscription callback that is called when [`subscribe()`](Self::subscribe) was
125    /// called.
126    ///
127    /// The callback must accept two parameters, firstly a [`Device`] variant, and secondly an
128    /// integer index.
129    ///
130    /// [`Device`]: crate::def::Device
131    pub fn set_subscribe_cb<F>(&mut self, callback: F)
132        where F: FnMut(def::Device, u32) + 'static
133    {
134        let saved = &mut self.cb_ptrs.subscribe;
135        *saved = SubscribeCb::new(Some(Box::new(callback)));
136        let (cb_fn, cb_data) = saved.get_capi_params(ext_subscribe_cb_proxy);
137        unsafe { capi::pa_ext_device_restore_set_subscribe_cb(self.context, cb_fn, cb_data); }
138    }
139
140    /// Reads the formats for all present devices from the device database.
141    ///
142    /// Panics if the underlying C function returns a null pointer.
143    pub fn read_formats_all<F>(&mut self, callback: F) -> Operation<dyn FnMut(ListResult<&Info>)>
144        where F: FnMut(ListResult<&Info>) + 'static
145    {
146        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&Info>)>(Box::new(callback));
147        let ptr = unsafe { capi::pa_ext_device_restore_read_formats_all(self.context,
148            Some(read_list_cb_proxy), cb_data) };
149        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&Info>)>)
150    }
151
152    /// Reads an entry from the device database.
153    ///
154    /// Panics if the underlying C function returns a null pointer.
155    pub fn read_formats<F>(&mut self, type_: def::Device, index: u32, callback: F)
156        -> Operation<dyn FnMut(ListResult<&Info>)>
157        where F: FnMut(ListResult<&Info>) + 'static
158    {
159        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&Info>)>(Box::new(callback));
160        let ptr = unsafe { capi::pa_ext_device_restore_read_formats(self.context, type_, index,
161            Some(read_list_cb_proxy), cb_data) };
162        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&Info>)>)
163    }
164
165    /// Reads an entry from the device database.
166    ///
167    /// The callback must accept a `bool`, which indicates success.
168    ///
169    /// Panics if the underlying C function returns a null pointer.
170    pub fn save_formats<F>(&mut self, type_: def::Device, index: u32,
171        formats: &mut [&mut format::Info], callback: F) -> Operation<dyn FnMut(bool)>
172        where F: FnMut(bool) + 'static
173    {
174        // Capture array of pointers to the above `format::InfoInternal` objects
175        let mut format_ptrs: Vec<*mut capi::pa_format_info> = Vec::with_capacity(formats.len());
176        for format in formats {
177            format_ptrs.push(unsafe { mem::transmute(&format.ptr) });
178        }
179
180        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
181        let ptr = unsafe {
182            capi::pa_ext_device_restore_save_formats(self.context, type_, index,
183                format_ptrs.len() as u8, format_ptrs.as_ptr(), Some(super::success_cb_proxy),
184                cb_data)
185        };
186        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
187    }
188}
189
190impl Drop for DeviceRestore {
191    fn drop(&mut self) {
192        unsafe { capi::pa_context_unref(self.context) };
193        self.context = null_mut::<ContextInternal>();
194    }
195}
196
197/// Proxy for the extension subscribe callback.
198///
199/// Warning: This is for multi-use cases! It does **not** destroy the actual closure callback, which
200/// must be accomplished separately to avoid a memory leak.
201extern "C"
202fn ext_subscribe_cb_proxy(_: *mut ContextInternal, type_: def::Device, index: u32,
203    userdata: *mut c_void)
204{
205    let _ = std::panic::catch_unwind(|| {
206        let callback = SubscribeCb::get_callback(userdata);
207        (callback)(type_, index);
208    });
209}
210
211/// Proxy for read list callbacks.
212///
213/// Warning: This is for list cases only! On EOL or error it destroys the actual closure callback.
214extern "C"
215fn read_list_cb_proxy(_: *mut ContextInternal, i: *const InfoInternal, eol: i32,
216    userdata: *mut c_void)
217{
218    let _ = std::panic::catch_unwind(|| {
219        callback_for_list_instance(i, eol, userdata, Info::new_from_raw);
220    });
221}