mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-17 10:11:01 +03: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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user