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