mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
source code
This commit is contained in:
51
libs/pulsectl/src/controllers/errors.rs
Normal file
51
libs/pulsectl/src/controllers/errors.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::PulseCtlError;
|
||||
|
||||
/// if the error occurs within the Mainloop, we bubble up the error with
|
||||
/// this conversion
|
||||
impl From<PulseCtlError> for ControllerError {
|
||||
fn from(error: super::errors::PulseCtlError) -> Self {
|
||||
ControllerError {
|
||||
error: ControllerErrorType::PulseCtlError,
|
||||
message: format!("{:?}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ControllerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut error_string = String::new();
|
||||
match self.error {
|
||||
ControllerErrorType::PulseCtlError => {
|
||||
error_string.push_str("PulseCtlError");
|
||||
}
|
||||
ControllerErrorType::GetInfoError => {
|
||||
error_string.push_str("GetInfoError");
|
||||
}
|
||||
}
|
||||
write!(f, "[{}]: {}", error_string, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum ControllerErrorType {
|
||||
PulseCtlError,
|
||||
GetInfoError,
|
||||
}
|
||||
|
||||
/// Error thrown while fetching data from pulseaudio,
|
||||
/// has two variants: PulseCtlError for when PulseAudio returns an error code
|
||||
/// and GetInfoError when a request for data fails for whatever reason
|
||||
pub struct ControllerError {
|
||||
error: ControllerErrorType,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl ControllerError {
|
||||
pub(crate) fn new(err: ControllerErrorType, msg: &str) -> Self {
|
||||
ControllerError {
|
||||
error: err,
|
||||
message: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
595
libs/pulsectl/src/controllers/mod.rs
Normal file
595
libs/pulsectl/src/controllers/mod.rs
Normal file
@@ -0,0 +1,595 @@
|
||||
/// Source = microphone etc. something that takes in audio
|
||||
/// Source Output = application consuming that audio
|
||||
///
|
||||
/// Sink = headphones etc. something that plays out audio
|
||||
/// Sink Input = application producing that audio
|
||||
/// When you create a `SinkController`, you are working with audio playback devices and applications
|
||||
/// if you want to manipulate recording devices such as microphone volume,
|
||||
/// you'll need to use a `SourceController`. Both of these implement the same api, defined by
|
||||
/// the traits DeviceControl and AppControl
|
||||
use std::cell::RefCell;
|
||||
use std::clone::Clone;
|
||||
use std::rc::Rc;
|
||||
|
||||
use pulse::{
|
||||
callbacks::ListResult,
|
||||
context::introspect,
|
||||
volume::{ChannelVolumes, Volume},
|
||||
};
|
||||
|
||||
use errors::{ControllerError, ControllerErrorType::*};
|
||||
use types::{ApplicationInfo, DeviceInfo, ServerInfo};
|
||||
|
||||
use crate::Handler;
|
||||
|
||||
pub(crate) mod errors;
|
||||
pub mod types;
|
||||
|
||||
pub trait DeviceControl<T> {
|
||||
fn get_default_device(&mut self) -> Result<T, ControllerError>;
|
||||
fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError>;
|
||||
|
||||
fn list_devices(&mut self) -> Result<Vec<T>, ControllerError>;
|
||||
fn get_device_by_index(&mut self, index: u32) -> Result<T, ControllerError>;
|
||||
fn get_device_by_name(&mut self, name: &str) -> Result<T, ControllerError>;
|
||||
fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes);
|
||||
fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes);
|
||||
fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64);
|
||||
fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64);
|
||||
}
|
||||
|
||||
pub trait AppControl<T> {
|
||||
fn list_applications(&mut self) -> Result<Vec<T>, ControllerError>;
|
||||
|
||||
fn get_app_by_index(&mut self, index: u32) -> Result<T, ControllerError>;
|
||||
fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64);
|
||||
fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64);
|
||||
|
||||
fn move_app_by_index(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_index: u32,
|
||||
) -> Result<bool, ControllerError>;
|
||||
fn move_app_by_name(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_name: &str,
|
||||
) -> Result<bool, ControllerError>;
|
||||
fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError>;
|
||||
}
|
||||
|
||||
fn volume_from_percent(volume: f64) -> f64 {
|
||||
(volume * 100.0) * (f64::from(pulse::volume::VOLUME_NORM.0) / 100.0)
|
||||
}
|
||||
|
||||
pub struct SinkController {
|
||||
pub handler: Handler,
|
||||
}
|
||||
|
||||
impl SinkController {
|
||||
pub fn create() -> Result<Self, ControllerError> {
|
||||
let handler = Handler::connect("SinkController")?;
|
||||
Ok(SinkController { handler })
|
||||
}
|
||||
|
||||
pub fn get_server_info(&mut self) -> Result<ServerInfo, ControllerError> {
|
||||
let server = Rc::new(RefCell::new(Some(None)));
|
||||
let server_ref = server.clone();
|
||||
|
||||
let op = self.handler.introspect.get_server_info(move |res| {
|
||||
server_ref
|
||||
.borrow_mut()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.replace(res.into());
|
||||
});
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = server.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting information about the server",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceControl<DeviceInfo> for SinkController {
|
||||
fn get_default_device(&mut self) -> Result<DeviceInfo, ControllerError> {
|
||||
let server_info = self.get_server_info();
|
||||
match server_info {
|
||||
Ok(info) => self.get_device_by_name(info.default_sink_name.unwrap().as_ref()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
|
||||
let op = self
|
||||
.handler
|
||||
.context
|
||||
.borrow_mut()
|
||||
.set_default_sink(name, move |res| success_ref.borrow_mut().clone_from(&res));
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn list_devices(&mut self) -> Result<Vec<DeviceInfo>, ControllerError> {
|
||||
let list = Rc::new(RefCell::new(Some(Vec::new())));
|
||||
let list_ref = list.clone();
|
||||
|
||||
let op = self.handler.introspect.get_sink_info_list(
|
||||
move |sink_list: ListResult<&introspect::SinkInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
list_ref.borrow_mut().as_mut().unwrap().push(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = list.borrow_mut();
|
||||
result.take().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting device list",
|
||||
))
|
||||
}
|
||||
fn get_device_by_index(&mut self, index: u32) -> Result<DeviceInfo, ControllerError> {
|
||||
let device = Rc::new(RefCell::new(Some(None)));
|
||||
let dev_ref = device.clone();
|
||||
let op = self.handler.introspect.get_sink_info_by_index(
|
||||
index,
|
||||
move |sink_list: ListResult<&introspect::SinkInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = device.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting requested device",
|
||||
))
|
||||
}
|
||||
fn get_device_by_name(&mut self, name: &str) -> Result<DeviceInfo, ControllerError> {
|
||||
let device = Rc::new(RefCell::new(Some(None)));
|
||||
let dev_ref = device.clone();
|
||||
let op = self.handler.introspect.get_sink_info_by_name(
|
||||
name,
|
||||
move |sink_list: ListResult<&introspect::SinkInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = device.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting requested device",
|
||||
))
|
||||
}
|
||||
|
||||
fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_volume_by_index(index, volume, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_volume_by_name(name, volume, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut dev_ref) = self.get_device_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = dev_ref.volume.increase(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_volume_by_index(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut dev_ref) = self.get_device_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = dev_ref.volume.decrease(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_volume_by_index(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppControl<ApplicationInfo> for SinkController {
|
||||
fn list_applications(&mut self) -> Result<Vec<ApplicationInfo>, ControllerError> {
|
||||
let list = Rc::new(RefCell::new(Some(Vec::new())));
|
||||
let list_ref = list.clone();
|
||||
|
||||
let op = self.handler.introspect.get_sink_input_info_list(
|
||||
move |sink_list: ListResult<&introspect::SinkInputInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
list_ref.borrow_mut().as_mut().unwrap().push(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = list.borrow_mut();
|
||||
result.take().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
|
||||
fn get_app_by_index(&mut self, index: u32) -> Result<ApplicationInfo, ControllerError> {
|
||||
let app = Rc::new(RefCell::new(Some(None)));
|
||||
let app_ref = app.clone();
|
||||
let op = self.handler.introspect.get_sink_input_info(
|
||||
index,
|
||||
move |sink_list: ListResult<&introspect::SinkInputInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
app_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = app.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting requested app",
|
||||
))
|
||||
}
|
||||
|
||||
fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut app_ref) = self.get_app_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = app_ref.volume.increase(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_input_volume(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut app_ref) = self.get_app_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = app_ref.volume.decrease(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_sink_input_volume(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_app_by_index(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_index: u32,
|
||||
) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.move_sink_input_by_index(
|
||||
stream_index,
|
||||
device_index,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn move_app_by_name(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_name: &str,
|
||||
) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.move_sink_input_by_name(
|
||||
stream_index,
|
||||
device_name,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.set_sink_input_mute(
|
||||
index,
|
||||
mute,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SourceController {
|
||||
pub handler: Handler,
|
||||
}
|
||||
|
||||
impl SourceController {
|
||||
pub fn create() -> Result<Self, ControllerError> {
|
||||
let handler = Handler::connect("SourceController")?;
|
||||
Ok(SourceController { handler })
|
||||
}
|
||||
|
||||
pub fn get_server_info(&mut self) -> Result<ServerInfo, ControllerError> {
|
||||
let server = Rc::new(RefCell::new(Some(None)));
|
||||
let server_ref = server.clone();
|
||||
|
||||
let op = self.handler.introspect.get_server_info(move |res| {
|
||||
server_ref
|
||||
.borrow_mut()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.replace(res.into());
|
||||
});
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = server.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceControl<DeviceInfo> for SourceController {
|
||||
fn get_default_device(&mut self) -> Result<DeviceInfo, ControllerError> {
|
||||
let server_info = self.get_server_info();
|
||||
match server_info {
|
||||
Ok(info) => self.get_device_by_name(info.default_sink_name.unwrap().as_ref()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
|
||||
let op = self
|
||||
.handler
|
||||
.context
|
||||
.borrow_mut()
|
||||
.set_default_source(name, move |res| success_ref.borrow_mut().clone_from(&res));
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn list_devices(&mut self) -> Result<Vec<DeviceInfo>, ControllerError> {
|
||||
let list = Rc::new(RefCell::new(Some(Vec::new())));
|
||||
let list_ref = list.clone();
|
||||
|
||||
let op = self.handler.introspect.get_source_info_list(
|
||||
move |sink_list: ListResult<&introspect::SourceInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
list_ref.borrow_mut().as_mut().unwrap().push(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = list.borrow_mut();
|
||||
result.take().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
fn get_device_by_index(&mut self, index: u32) -> Result<DeviceInfo, ControllerError> {
|
||||
let device = Rc::new(RefCell::new(Some(None)));
|
||||
let dev_ref = device.clone();
|
||||
let op = self.handler.introspect.get_source_info_by_index(
|
||||
index,
|
||||
move |sink_list: ListResult<&introspect::SourceInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = device.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
fn get_device_by_name(&mut self, name: &str) -> Result<DeviceInfo, ControllerError> {
|
||||
let device = Rc::new(RefCell::new(Some(None)));
|
||||
let dev_ref = device.clone();
|
||||
let op = self.handler.introspect.get_source_info_by_name(
|
||||
name,
|
||||
move |sink_list: ListResult<&introspect::SourceInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = device.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
|
||||
fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_volume_by_index(index, volume, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_volume_by_name(name, volume, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut dev_ref) = self.get_device_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = dev_ref.volume.increase(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_volume_by_index(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut dev_ref) = self.get_device_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = dev_ref.volume.decrease(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_volume_by_index(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppControl<ApplicationInfo> for SourceController {
|
||||
fn list_applications(&mut self) -> Result<Vec<ApplicationInfo>, ControllerError> {
|
||||
let list = Rc::new(RefCell::new(Some(Vec::new())));
|
||||
let list_ref = list.clone();
|
||||
|
||||
let op = self.handler.introspect.get_source_output_info_list(
|
||||
move |sink_list: ListResult<&introspect::SourceOutputInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
list_ref.borrow_mut().as_mut().unwrap().push(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = list.borrow_mut();
|
||||
result.take().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
|
||||
fn get_app_by_index(&mut self, index: u32) -> Result<ApplicationInfo, ControllerError> {
|
||||
let app = Rc::new(RefCell::new(Some(None)));
|
||||
let app_ref = app.clone();
|
||||
let op = self.handler.introspect.get_source_output_info(
|
||||
index,
|
||||
move |sink_list: ListResult<&introspect::SourceOutputInfo>| {
|
||||
if let ListResult::Item(item) = sink_list {
|
||||
app_ref.borrow_mut().as_mut().unwrap().replace(item.into());
|
||||
}
|
||||
},
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let mut result = app.borrow_mut();
|
||||
result.take().unwrap().ok_or(ControllerError::new(
|
||||
GetInfoError,
|
||||
"Error getting application list",
|
||||
))
|
||||
}
|
||||
|
||||
fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut app_ref) = self.get_app_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = app_ref.volume.increase(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_output_volume(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64) {
|
||||
if let Ok(mut app_ref) = self.get_app_by_index(index) {
|
||||
let new_vol = Volume::from(Volume(volume_from_percent(delta) as u32));
|
||||
if let Some(volumes) = app_ref.volume.decrease(new_vol) {
|
||||
let op = self
|
||||
.handler
|
||||
.introspect
|
||||
.set_source_output_volume(index, &volumes, None);
|
||||
self.handler.wait_for_operation(op).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn move_app_by_index(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_index: u32,
|
||||
) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.move_source_output_by_index(
|
||||
stream_index,
|
||||
device_index,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn move_app_by_name(
|
||||
&mut self,
|
||||
stream_index: u32,
|
||||
device_name: &str,
|
||||
) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.move_source_output_by_name(
|
||||
stream_index,
|
||||
device_name,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError> {
|
||||
let success = Rc::new(RefCell::new(false));
|
||||
let success_ref = success.clone();
|
||||
let op = self.handler.introspect.set_source_mute_by_index(
|
||||
index,
|
||||
mute,
|
||||
Some(Box::new(move |res| {
|
||||
success_ref.borrow_mut().clone_from(&res)
|
||||
})),
|
||||
);
|
||||
self.handler.wait_for_operation(op)?;
|
||||
let result = success.borrow_mut().clone();
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
354
libs/pulsectl/src/controllers/types.rs
Normal file
354
libs/pulsectl/src/controllers/types.rs
Normal file
@@ -0,0 +1,354 @@
|
||||
use pulse::{
|
||||
channelmap,
|
||||
context::introspect,
|
||||
def,
|
||||
def::PortAvailable,
|
||||
format,
|
||||
proplist::Proplist,
|
||||
sample,
|
||||
time::MicroSeconds,
|
||||
volume::{ChannelVolumes, Volume},
|
||||
};
|
||||
|
||||
/// These structs are direct representations of what libpulse_binding gives
|
||||
/// created to be copyable / cloneable for use in and out of callbacks
|
||||
|
||||
/// This is a wrapper around SinkPortInfo and SourcePortInfo as they have the same members
|
||||
#[derive(Clone)]
|
||||
pub struct DevicePortInfo {
|
||||
/// Name of the sink.
|
||||
pub name: Option<String>,
|
||||
/// Description of this sink.
|
||||
pub description: Option<String>,
|
||||
/// The higher this value is, the more useful this port is as a default.
|
||||
pub priority: u32,
|
||||
/// A flag indicating availability status of this port.
|
||||
pub available: PortAvailable,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Box<introspect::SinkPortInfo<'a>>> for DevicePortInfo {
|
||||
fn from(item: &'a Box<introspect::SinkPortInfo<'a>>) -> Self {
|
||||
DevicePortInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
priority: item.priority,
|
||||
available: item.available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SinkPortInfo<'a>> for DevicePortInfo {
|
||||
fn from(item: &'a introspect::SinkPortInfo<'a>) -> Self {
|
||||
DevicePortInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
priority: item.priority,
|
||||
available: item.available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Box<introspect::SourcePortInfo<'a>>> for DevicePortInfo {
|
||||
fn from(item: &'a Box<introspect::SourcePortInfo<'a>>) -> Self {
|
||||
DevicePortInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
priority: item.priority,
|
||||
available: item.available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SourcePortInfo<'a>> for DevicePortInfo {
|
||||
fn from(item: &'a introspect::SourcePortInfo<'a>) -> Self {
|
||||
DevicePortInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
priority: item.priority,
|
||||
available: item.available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a wrapper around SinkState and SourceState as they have the same values
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum DevState {
|
||||
/// This state is used when the server does not support sink state introspection.
|
||||
Invalid = -1,
|
||||
/// Running, sink is playing and used by at least one non-corked sink-input.
|
||||
Running = 0,
|
||||
/// When idle, the sink is playing but there is no non-corked sink-input attached to it.
|
||||
Idle = 1,
|
||||
/// When suspended, actual sink access can be closed, for instance.
|
||||
Suspended = 2,
|
||||
}
|
||||
|
||||
impl<'a> From<def::SourceState> for DevState {
|
||||
fn from(s: def::SourceState) -> Self {
|
||||
match s {
|
||||
def::SourceState::Idle => DevState::Idle,
|
||||
def::SourceState::Invalid => DevState::Invalid,
|
||||
def::SourceState::Running => DevState::Running,
|
||||
def::SourceState::Suspended => DevState::Suspended,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<def::SinkState> for DevState {
|
||||
fn from(s: def::SinkState) -> Self {
|
||||
match s {
|
||||
def::SinkState::Idle => DevState::Idle,
|
||||
def::SinkState::Invalid => DevState::Invalid,
|
||||
def::SinkState::Running => DevState::Running,
|
||||
def::SinkState::Suspended => DevState::Suspended,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Flags {
|
||||
SourceFLags(def::SourceFlagSet),
|
||||
SinkFlags(def::SinkFlagSet),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DeviceInfo {
|
||||
/// Index of the sink.
|
||||
pub index: u32,
|
||||
/// Name of the sink.
|
||||
pub name: Option<String>,
|
||||
/// Description of this sink.
|
||||
pub description: Option<String>,
|
||||
/// Sample spec of this sink.
|
||||
pub sample_spec: sample::Spec,
|
||||
/// Channel map.
|
||||
pub channel_map: channelmap::Map,
|
||||
/// Index of the owning module of this sink, or `None` if is invalid.
|
||||
pub owner_module: Option<u32>,
|
||||
/// Volume of the sink.
|
||||
pub volume: ChannelVolumes,
|
||||
/// Mute switch of the sink.
|
||||
pub mute: bool,
|
||||
/// Index of the monitor source connected to this sink.
|
||||
pub monitor: Option<u32>,
|
||||
/// The name of the monitor source.
|
||||
pub monitor_name: Option<String>,
|
||||
/// Length of queued audio in the output buffer.
|
||||
pub latency: MicroSeconds,
|
||||
/// Driver name.
|
||||
pub driver: Option<String>,
|
||||
/// Flags.
|
||||
pub flags: Flags,
|
||||
/// Property list.
|
||||
pub proplist: Proplist,
|
||||
/// The latency this device has been configured to.
|
||||
pub configured_latency: MicroSeconds,
|
||||
/// Some kind of “base” volume that refers to unamplified/unattenuated volume in the context of
|
||||
/// the output device.
|
||||
pub base_volume: Volume,
|
||||
/// State.
|
||||
pub state: DevState,
|
||||
/// Number of volume steps for sinks which do not support arbitrary volumes.
|
||||
pub n_volume_steps: u32,
|
||||
/// Card index, or `None` if invalid.
|
||||
pub card: Option<u32>,
|
||||
/// Set of available ports.
|
||||
pub ports: Vec<DevicePortInfo>,
|
||||
// Pointer to active port in the set, or None.
|
||||
pub active_port: Option<DevicePortInfo>,
|
||||
/// Set of formats supported by the sink.
|
||||
pub formats: Vec<format::Info>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SinkInfo<'a>> for DeviceInfo {
|
||||
fn from(item: &'a introspect::SinkInfo<'a>) -> Self {
|
||||
DeviceInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
index: item.index,
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
sample_spec: item.sample_spec,
|
||||
channel_map: item.channel_map,
|
||||
owner_module: item.owner_module,
|
||||
volume: item.volume,
|
||||
mute: item.mute,
|
||||
monitor: Some(item.monitor_source),
|
||||
monitor_name: item.monitor_source_name.as_ref().map(|cow| cow.to_string()),
|
||||
latency: item.latency,
|
||||
driver: item.driver.as_ref().map(|cow| cow.to_string()),
|
||||
flags: Flags::SinkFlags(item.flags),
|
||||
proplist: item.proplist.clone(),
|
||||
configured_latency: item.configured_latency,
|
||||
base_volume: item.base_volume,
|
||||
state: DevState::from(item.state),
|
||||
n_volume_steps: item.n_volume_steps,
|
||||
card: item.card,
|
||||
ports: item.ports.iter().map(From::from).collect(),
|
||||
active_port: item.active_port.as_ref().map(From::from),
|
||||
formats: item.formats.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SourceInfo<'a>> for DeviceInfo {
|
||||
fn from(item: &'a introspect::SourceInfo<'a>) -> Self {
|
||||
DeviceInfo {
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
index: item.index,
|
||||
description: item.description.as_ref().map(|cow| cow.to_string()),
|
||||
sample_spec: item.sample_spec,
|
||||
channel_map: item.channel_map,
|
||||
owner_module: item.owner_module,
|
||||
volume: item.volume,
|
||||
mute: item.mute,
|
||||
monitor: item.monitor_of_sink,
|
||||
monitor_name: item
|
||||
.monitor_of_sink_name
|
||||
.as_ref()
|
||||
.map(|cow| cow.to_string()),
|
||||
latency: item.latency,
|
||||
driver: item.driver.as_ref().map(|cow| cow.to_string()),
|
||||
flags: Flags::SourceFLags(item.flags),
|
||||
proplist: item.proplist.clone(),
|
||||
configured_latency: item.configured_latency,
|
||||
base_volume: item.base_volume,
|
||||
state: DevState::from(item.state),
|
||||
n_volume_steps: item.n_volume_steps,
|
||||
card: item.card,
|
||||
ports: item.ports.iter().map(From::from).collect(),
|
||||
active_port: item.active_port.as_ref().map(From::from),
|
||||
formats: item.formats.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApplicationInfo {
|
||||
/// Index of the sink input.
|
||||
pub index: u32,
|
||||
/// Name of the sink input.
|
||||
pub name: Option<String>,
|
||||
/// Index of the module this sink input belongs to, or `None` when it does not belong to any
|
||||
/// module.
|
||||
pub owner_module: Option<u32>,
|
||||
/// Index of the client this sink input belongs to, or invalid when it does not belong to any
|
||||
/// client.
|
||||
pub client: Option<u32>,
|
||||
/// Index of the connected sink/source.
|
||||
pub connection_id: u32,
|
||||
/// The sample specification of the sink input.
|
||||
pub sample_spec: sample::Spec,
|
||||
/// Channel map.
|
||||
pub channel_map: channelmap::Map,
|
||||
/// The volume of this sink input.
|
||||
pub volume: ChannelVolumes,
|
||||
/// Latency due to buffering in sink input, see
|
||||
/// [`def::TimingInfo`](../../def/struct.TimingInfo.html) for details.
|
||||
pub buffer_usec: MicroSeconds,
|
||||
/// Latency of the sink device, see
|
||||
/// [`def::TimingInfo`](../../def/struct.TimingInfo.html) for details.
|
||||
pub connection_usec: MicroSeconds,
|
||||
/// The resampling method used by this sink input.
|
||||
pub resample_method: Option<String>,
|
||||
/// Driver name.
|
||||
pub driver: Option<String>,
|
||||
/// Stream muted.
|
||||
pub mute: bool,
|
||||
/// Property list.
|
||||
pub proplist: Proplist,
|
||||
/// Stream corked.
|
||||
pub corked: bool,
|
||||
/// Stream has volume. If not set, then the meaning of this struct’s volume member is unspecified.
|
||||
pub has_volume: bool,
|
||||
/// The volume can be set. If not set, the volume can still change even though clients can’t
|
||||
/// control the volume.
|
||||
pub volume_writable: bool,
|
||||
/// Stream format information.
|
||||
pub format: format::Info,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SinkInputInfo<'a>> for ApplicationInfo {
|
||||
fn from(item: &'a introspect::SinkInputInfo<'a>) -> Self {
|
||||
ApplicationInfo {
|
||||
index: item.index,
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
owner_module: item.owner_module,
|
||||
client: item.client,
|
||||
connection_id: item.sink,
|
||||
sample_spec: item.sample_spec,
|
||||
channel_map: item.channel_map,
|
||||
volume: item.volume,
|
||||
buffer_usec: item.buffer_usec,
|
||||
connection_usec: item.sink_usec,
|
||||
resample_method: item.resample_method.as_ref().map(|cow| cow.to_string()),
|
||||
driver: item.driver.as_ref().map(|cow| cow.to_string()),
|
||||
mute: item.mute,
|
||||
proplist: item.proplist.clone(),
|
||||
corked: item.corked,
|
||||
has_volume: item.has_volume,
|
||||
volume_writable: item.volume_writable,
|
||||
format: item.format.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::SourceOutputInfo<'a>> for ApplicationInfo {
|
||||
fn from(item: &'a introspect::SourceOutputInfo<'a>) -> Self {
|
||||
ApplicationInfo {
|
||||
index: item.index,
|
||||
name: item.name.as_ref().map(|cow| cow.to_string()),
|
||||
owner_module: item.owner_module,
|
||||
client: item.client,
|
||||
connection_id: item.source,
|
||||
sample_spec: item.sample_spec,
|
||||
channel_map: item.channel_map,
|
||||
volume: item.volume,
|
||||
buffer_usec: item.buffer_usec,
|
||||
connection_usec: item.source_usec,
|
||||
resample_method: item.resample_method.as_ref().map(|cow| cow.to_string()),
|
||||
driver: item.driver.as_ref().map(|cow| cow.to_string()),
|
||||
mute: item.mute,
|
||||
proplist: item.proplist.clone(),
|
||||
corked: item.corked,
|
||||
has_volume: item.has_volume,
|
||||
volume_writable: item.volume_writable,
|
||||
format: item.format.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerInfo {
|
||||
/// User name of the daemon process.
|
||||
pub user_name: Option<String>,
|
||||
/// Host name the daemon is running on.
|
||||
pub host_name: Option<String>,
|
||||
/// Version string of the daemon.
|
||||
pub server_version: Option<String>,
|
||||
/// Server package name (usually “pulseaudio”).
|
||||
pub server_name: Option<String>,
|
||||
/// Default sample specification.
|
||||
pub sample_spec: sample::Spec,
|
||||
/// Name of default sink.
|
||||
pub default_sink_name: Option<String>,
|
||||
/// Name of default source.
|
||||
pub default_source_name: Option<String>,
|
||||
/// A random cookie for identifying this instance of PulseAudio.
|
||||
pub cookie: u32,
|
||||
/// Default channel map.
|
||||
pub channel_map: channelmap::Map,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a introspect::ServerInfo<'a>> for ServerInfo {
|
||||
fn from(info: &'a introspect::ServerInfo<'a>) -> Self {
|
||||
ServerInfo {
|
||||
user_name: info.user_name.as_ref().map(|cow| cow.to_string()),
|
||||
host_name: info.host_name.as_ref().map(|cow| cow.to_string()),
|
||||
server_version: info.server_version.as_ref().map(|cow| cow.to_string()),
|
||||
server_name: info.server_name.as_ref().map(|cow| cow.to_string()),
|
||||
sample_spec: info.sample_spec,
|
||||
default_sink_name: info.default_sink_name.as_ref().map(|cow| cow.to_string()),
|
||||
default_source_name: info.default_source_name.as_ref().map(|cow| cow.to_string()),
|
||||
cookie: info.cookie,
|
||||
channel_map: info.channel_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
54
libs/pulsectl/src/errors.rs
Normal file
54
libs/pulsectl/src/errors.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use std::fmt;
|
||||
|
||||
use pulse::error::{PAErr};
|
||||
|
||||
impl From<PAErr> for PulseCtlError {
|
||||
fn from(error: PAErr) -> Self {
|
||||
PulseCtlError {
|
||||
error: PulseCtlErrorType::PulseAudioError,
|
||||
message: format!("PulseAudio returned error: {}", error.to_string().unwrap_or("Unknown".to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PulseCtlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut error_string = String::new();
|
||||
match self.error {
|
||||
PulseCtlErrorType::ConnectError => {
|
||||
error_string.push_str("ConnectError");
|
||||
}
|
||||
PulseCtlErrorType::OperationError => {
|
||||
error_string.push_str("OperationError");
|
||||
}
|
||||
PulseCtlErrorType::PulseAudioError => {
|
||||
error_string.push_str("PulseAudioError");
|
||||
}
|
||||
}
|
||||
write!(f, "[{}]: {}", error_string, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum PulseCtlErrorType {
|
||||
ConnectError,
|
||||
OperationError,
|
||||
PulseAudioError,
|
||||
}
|
||||
|
||||
/// Error thrown when PulseAudio throws an error code, there are 3 variants
|
||||
/// `PulseCtlErrorType::ConnectError` when there's an error establishing a connection
|
||||
/// `PulseCtlErrorType::OperationError` when the requested operation quis unexpecdatly or is cancelled
|
||||
/// `PulseCtlErrorType::PulseAudioError` when PulseAudio returns an error code in any circumstance
|
||||
pub struct PulseCtlError {
|
||||
error: PulseCtlErrorType,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl PulseCtlError {
|
||||
pub(crate) fn new(err: PulseCtlErrorType, msg: &str) -> Self {
|
||||
PulseCtlError {
|
||||
error: err,
|
||||
message: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
166
libs/pulsectl/src/lib.rs
Normal file
166
libs/pulsectl/src/lib.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
/// `pulsectl` is a high level wrapper around the PulseAudio bindings supplied by
|
||||
/// `libpulse_binding`. It provides simple access to sinks, inputs, sources and outputs allowing
|
||||
/// one to write audio control programs with ease.
|
||||
///
|
||||
/// ## Quick Example
|
||||
///
|
||||
/// The following example demonstrates listing all of the playback devices currently connected
|
||||
///
|
||||
/// See examples/change_device_vol.rs for a more complete example
|
||||
/// ```no_run
|
||||
/// extern crate pulsectl;
|
||||
///
|
||||
/// use std::io;
|
||||
///
|
||||
/// use pulsectl::controllers::SinkController;
|
||||
/// use pulsectl::controllers::DeviceControl;
|
||||
/// fn main() {
|
||||
/// // create handler that calls functions on playback devices and apps
|
||||
/// let mut handler = SinkController::create().unwrap();
|
||||
/// let devices = handler
|
||||
/// .list_devices()
|
||||
/// .expect("Could not get list of playback devices");
|
||||
///
|
||||
/// println!("Playback Devices");
|
||||
/// for dev in devices.clone() {
|
||||
/// println!(
|
||||
/// "[{}] {}, Volume: {}",
|
||||
/// dev.index,
|
||||
/// dev.description.as_ref().unwrap(),
|
||||
/// dev.volume.print()
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
extern crate libpulse_binding as pulse;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
use pulse::{
|
||||
context::{introspect, Context},
|
||||
mainloop::standard::{IterateResult, Mainloop},
|
||||
operation::{Operation, State},
|
||||
proplist::Proplist,
|
||||
};
|
||||
|
||||
use crate::errors::{PulseCtlError, PulseCtlErrorType::*};
|
||||
|
||||
pub mod controllers;
|
||||
mod errors;
|
||||
|
||||
pub struct Handler {
|
||||
pub mainloop: Rc<RefCell<Mainloop>>,
|
||||
pub context: Rc<RefCell<Context>>,
|
||||
pub introspect: introspect::Introspector,
|
||||
}
|
||||
|
||||
fn connect_error(err: &str) -> PulseCtlError {
|
||||
PulseCtlError::new(ConnectError, err)
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn connect(name: &str) -> Result<Handler, PulseCtlError> {
|
||||
let mut proplist = Proplist::new().unwrap();
|
||||
proplist
|
||||
.set_str(pulse::proplist::properties::APPLICATION_NAME, name)
|
||||
.unwrap();
|
||||
|
||||
let mainloop;
|
||||
if let Some(m) = Mainloop::new() {
|
||||
mainloop = Rc::new(RefCell::new(m));
|
||||
} else {
|
||||
return Err(connect_error("Failed to create mainloop"));
|
||||
}
|
||||
|
||||
let context;
|
||||
if let Some(c) =
|
||||
Context::new_with_proplist(mainloop.borrow().deref(), "MainConn", &proplist)
|
||||
{
|
||||
context = Rc::new(RefCell::new(c));
|
||||
} else {
|
||||
return Err(connect_error("Failed to create new context"));
|
||||
}
|
||||
|
||||
context
|
||||
.borrow_mut()
|
||||
.connect(None, pulse::context::flags::NOFLAGS, None)
|
||||
.map_err(|_| connect_error("Failed to connect context"))?;
|
||||
|
||||
loop {
|
||||
match mainloop.borrow_mut().iterate(false) {
|
||||
IterateResult::Err(e) => {
|
||||
eprintln!("iterate state was not success, quitting...");
|
||||
return Err(e.into());
|
||||
}
|
||||
IterateResult::Success(_) => {}
|
||||
IterateResult::Quit(_) => {
|
||||
eprintln!("iterate state was not success, quitting...");
|
||||
return Err(PulseCtlError::new(
|
||||
ConnectError,
|
||||
"Iterate state quit without an error",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
match context.borrow().get_state() {
|
||||
pulse::context::State::Ready => break,
|
||||
pulse::context::State::Failed | pulse::context::State::Terminated => {
|
||||
eprintln!("context state failed/terminated, quitting...");
|
||||
return Err(PulseCtlError::new(
|
||||
ConnectError,
|
||||
"Context state failed/terminated without an error",
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let introspect = context.borrow_mut().introspect();
|
||||
Ok(Handler {
|
||||
mainloop,
|
||||
context,
|
||||
introspect,
|
||||
})
|
||||
}
|
||||
|
||||
// loop until the passed operation is completed
|
||||
pub fn wait_for_operation<G: ?Sized>(
|
||||
&mut self,
|
||||
op: Operation<G>,
|
||||
) -> Result<(), errors::PulseCtlError> {
|
||||
loop {
|
||||
match self.mainloop.borrow_mut().iterate(false) {
|
||||
IterateResult::Err(e) => return Err(e.into()),
|
||||
IterateResult::Success(_) => {}
|
||||
IterateResult::Quit(_) => {
|
||||
return Err(PulseCtlError::new(
|
||||
OperationError,
|
||||
"Iterate state quit without an error",
|
||||
));
|
||||
}
|
||||
}
|
||||
match op.get_state() {
|
||||
State::Done => {
|
||||
break;
|
||||
}
|
||||
State::Running => {}
|
||||
State::Cancelled => {
|
||||
return Err(PulseCtlError::new(
|
||||
OperationError,
|
||||
"Operation cancelled without an error",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Handler {
|
||||
fn drop(&mut self) {
|
||||
self.context.borrow_mut().disconnect();
|
||||
self.mainloop.borrow_mut().quit(pulse::def::Retval(0));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user