libpulse_binding/context/
ext_stream_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-stream-restore.
15
16use std::os::raw::{c_char, c_void};
17use std::ffi::{CStr, CString};
18use std::borrow::Cow;
19use std::ptr::{null, null_mut};
20use std::mem;
21use capi::pa_ext_stream_restore_info as InfoInternal;
22use super::{ContextInternal, Context};
23use crate::{channelmap, proplist};
24use crate::callbacks::{ListResult, box_closure_get_capi_ptr, callback_for_list_instance};
25use crate::{operation::Operation, volume::ChannelVolumes};
26
27/// Stores information about one entry in the stream database that is maintained by
28/// module-stream-restore.
29#[derive(Debug)]
30pub struct Info<'a> {
31    /// Identifier string of the stream. A string like “sink-input-by-role:” or similar followed by
32    /// some arbitrary property value.
33    pub name: Option<Cow<'a, str>>,
34    /// The channel map for the volume field, if applicable.
35    pub channel_map: channelmap::Map,
36    /// The volume of the stream when it was seen last, if applicable and saved.
37    pub volume: ChannelVolumes,
38    /// The sink/source of the stream when it was last seen, if applicable and saved.
39    pub device: Option<Cow<'a, str>>,
40    /// The boolean mute state of the stream when it was last seen, if applicable and saved.
41    pub mute: bool,
42}
43
44impl Info<'_> {
45    fn new_from_raw(p: *const InfoInternal) -> Self {
46        assert!(!p.is_null());
47        let src = unsafe { p.as_ref().unwrap() };
48        unsafe {
49            Info {
50                name: match src.name.is_null() {
51                    false => Some(CStr::from_ptr(src.name).to_string_lossy()),
52                    true => None,
53                },
54                channel_map: src.channel_map.into(),
55                volume: src.volume.into(),
56                device: match src.device.is_null() {
57                    false => Some(CStr::from_ptr(src.device).to_string_lossy()),
58                    true => None,
59                },
60                mute: match src.mute {
61                    0 => false,
62                    _ => true,
63                },
64            }
65        }
66    }
67
68    /// Creates a copy with owned data.
69    pub fn to_owned(&self) -> Info<'static> {
70        Info {
71            name: self.name.clone().map(|o| Cow::Owned(o.into_owned())),
72            device: self.device.clone().map(|o| Cow::Owned(o.into_owned())),
73            ..*self
74        }
75    }
76}
77
78/// A wrapper object providing stream restore routines to a context.
79///
80/// Note: Saves a copy of active multi-use closure callbacks, which it frees on drop.
81pub struct StreamRestore {
82    context: *mut ContextInternal,
83    /// Multi-use callback closure pointers
84    cb_ptrs: CallbackPointers,
85}
86
87unsafe impl Send for StreamRestore {}
88unsafe impl Sync for StreamRestore {}
89
90/// Holds copies of callback closure pointers, for those that are “multi-use” (may be fired multiple
91/// times), for freeing at the appropriate time.
92#[derive(Default)]
93struct CallbackPointers {
94    subscribe: super::ExtSubscribeCb,
95}
96
97impl Context {
98    /// Gets a stream restore object linked to the current context, giving access to stream restore
99    /// routines.
100    ///
101    /// See [`context::ext_stream_restore`](mod@crate::context::ext_stream_restore).
102    pub fn stream_restore(&self) -> StreamRestore {
103        unsafe { capi::pa_context_ref(self.ptr) };
104        StreamRestore::from_raw(self.ptr)
105    }
106}
107
108impl StreamRestore {
109    /// Creates a new `StreamRestore` from an existing [`ContextInternal`] pointer.
110    fn from_raw(context: *mut ContextInternal) -> Self {
111        Self { context: context, cb_ptrs: Default::default() }
112    }
113
114    /// Tests if this extension module is available in the server.
115    ///
116    /// Panics if the underlying C function returns a null pointer.
117    pub fn test<F>(&mut self, callback: F) -> Operation<dyn FnMut(u32)>
118        where F: FnMut(u32) + 'static
119    {
120        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(u32)>(Box::new(callback));
121        let ptr = unsafe { capi::pa_ext_stream_restore_test(self.context,
122            Some(super::ext_test_cb_proxy), cb_data) };
123        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(u32)>)
124    }
125
126    /// Reads all entries from the stream database.
127    ///
128    /// Panics if the underlying C function returns a null pointer.
129    pub fn read<F>(&mut self, callback: F) -> Operation<dyn FnMut(ListResult<&Info>)>
130        where F: FnMut(ListResult<&Info>) + 'static
131    {
132        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(ListResult<&Info>)>(Box::new(callback));
133        let ptr = unsafe { capi::pa_ext_stream_restore_read(self.context, Some(read_list_cb_proxy),
134            cb_data) };
135        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(ListResult<&Info>)>)
136    }
137
138    /// Stores entries in the stream database.
139    ///
140    /// The callback must accept a `bool`, which indicates success.
141    ///
142    /// Panics if the underlying C function returns a null pointer, or if the length of the array is
143    /// too long to be communicated to the C function.
144    pub fn write<F>(&mut self, mode: proplist::UpdateMode, data: &[&Info],
145        apply_immediately: bool, callback: F) -> Operation<dyn FnMut(bool)>
146        where F: FnMut(bool) + 'static
147    {
148        assert!(data.len() <= u32::MAX as usize);
149        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
150        let ptr = unsafe {
151            capi::pa_ext_stream_restore_write(self.context, mode, mem::transmute(data.as_ptr()),
152                data.len() as u32, apply_immediately as i32, Some(super::success_cb_proxy),
153                cb_data)
154        };
155        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
156    }
157
158    /// Deletes entries from the stream database.
159    ///
160    /// The callback must accept a `bool`, which indicates success.
161    ///
162    /// Panics if the underlying C function returns a null pointer.
163    pub fn delete<F>(&mut self, streams: &[&str], callback: F) -> Operation<dyn FnMut(bool)>
164        where F: FnMut(bool) + 'static
165    {
166        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
167        // as_ptr() giving dangling pointers!
168        let mut c_streams: Vec<CString> = Vec::with_capacity(streams.len());
169        for stream in streams {
170            c_streams.push(CString::new(*stream).unwrap());
171        }
172
173        // Capture array of pointers to the above CString values.
174        // We also add a `NULL` pointer entry on the end, as expected by the C function called here.
175        let mut c_stream_ptrs: Vec<*const c_char> = Vec::with_capacity(c_streams.len() + 1);
176        for c_stream in &c_streams {
177            c_stream_ptrs.push(c_stream.as_ptr());
178        }
179        c_stream_ptrs.push(null());
180
181        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
182        let ptr = unsafe { capi::pa_ext_stream_restore_delete(self.context, c_stream_ptrs.as_ptr(),
183            Some(super::success_cb_proxy), cb_data) };
184        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
185    }
186
187    /// Subscribes to changes in the stream database.
188    ///
189    /// The callback must accept a `bool`, which indicates success.
190    ///
191    /// Panics if the underlying C function returns a null pointer.
192    pub fn subscribe<F>(&mut self, enable: bool, callback: F) -> Operation<dyn FnMut(bool)>
193        where F: FnMut(bool) + 'static
194    {
195        let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
196        let ptr = unsafe { capi::pa_ext_stream_restore_subscribe(self.context, enable as i32,
197            Some(super::success_cb_proxy), cb_data) };
198        Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
199    }
200
201    /// Sets the subscription callback that is called when [`subscribe()`](Self::subscribe) was
202    /// called.
203    pub fn set_subscribe_cb<F>(&mut self, callback: F)
204        where F: FnMut() + 'static
205    {
206        let saved = &mut self.cb_ptrs.subscribe;
207        *saved = super::ExtSubscribeCb::new(Some(Box::new(callback)));
208        let (cb_fn, cb_data) = saved.get_capi_params(super::ext_subscribe_cb_proxy);
209        unsafe { capi::pa_ext_stream_restore_set_subscribe_cb(self.context, cb_fn, cb_data); }
210    }
211}
212
213impl Drop for StreamRestore {
214    fn drop(&mut self) {
215        unsafe { capi::pa_context_unref(self.context) };
216        self.context = null_mut::<ContextInternal>();
217    }
218}
219
220/// Proxy for read list callbacks.
221///
222/// Warning: This is for list cases only! On EOL it destroys the actual closure callback.
223extern "C"
224fn read_list_cb_proxy(_: *mut ContextInternal, i: *const InfoInternal, eol: i32,
225    userdata: *mut c_void)
226{
227    let _ = std::panic::catch_unwind(|| {
228        callback_for_list_instance(i, eol, userdata, Info::new_from_raw);
229    });
230}