shepherd-launcher/crates/shepherd-hud/src/volume.rs

128 lines
4.1 KiB
Rust

//! Volume monitoring and control module
//!
//! Provides volume status and control via shepherdd.
//! The service handles actual volume control and enforces restrictions.
use shepherd_api::{Command, ResponsePayload, VolumeInfo};
use shepherd_ipc::IpcClient;
use shepherd_util::default_socket_path;
use tokio::runtime::Runtime;
/// Get current volume status from shepherdd
pub fn get_volume_status() -> Option<VolumeInfo> {
let socket_path = default_socket_path();
let rt = match Runtime::new() {
Ok(rt) => rt,
Err(e) => {
tracing::error!("Failed to create runtime: {}", e);
return None;
}
};
rt.block_on(async {
match IpcClient::connect(&socket_path).await {
Ok(mut client) => match client.send(Command::GetVolume).await {
Ok(response) => {
if let shepherd_api::ResponseResult::Ok(ResponsePayload::Volume(info)) =
response.result
{
Some(info)
} else {
None
}
}
Err(e) => {
tracing::error!("Failed to get volume: {}", e);
None
}
},
Err(e) => {
tracing::debug!("Failed to connect to shepherdd for volume: {}", e);
None
}
}
})
}
/// Toggle mute state via shepherdd
pub fn toggle_mute() -> anyhow::Result<()> {
let socket_path = default_socket_path();
let rt = Runtime::new()?;
rt.block_on(async {
let mut client = IpcClient::connect(&socket_path).await?;
let response = client.send(Command::ToggleMute).await?;
match response.result {
shepherd_api::ResponseResult::Ok(ResponsePayload::VolumeSet) => Ok(()),
shepherd_api::ResponseResult::Ok(ResponsePayload::VolumeDenied { reason }) => {
Err(anyhow::anyhow!("Volume denied: {}", reason))
}
shepherd_api::ResponseResult::Err(e) => {
Err(anyhow::anyhow!("Error: {}", e.message))
}
_ => Err(anyhow::anyhow!("Unexpected response")),
}
})
}
/// Set volume to a specific percentage via shepherdd
pub fn set_volume(percent: u8) -> anyhow::Result<()> {
let socket_path = default_socket_path();
let rt = Runtime::new()?;
rt.block_on(async {
let mut client = IpcClient::connect(&socket_path).await?;
let response = client.send(Command::SetVolume { percent }).await?;
match response.result {
shepherd_api::ResponseResult::Ok(ResponsePayload::VolumeSet) => Ok(()),
shepherd_api::ResponseResult::Ok(ResponsePayload::VolumeDenied { reason }) => {
Err(anyhow::anyhow!("Volume denied: {}", reason))
}
shepherd_api::ResponseResult::Err(e) => {
Err(anyhow::anyhow!("Error: {}", e.message))
}
_ => Err(anyhow::anyhow!("Unexpected response")),
}
})
}
#[cfg(test)]
mod tests {
use shepherd_api::VolumeRestrictions;
#[test]
fn test_volume_icon_names() {
// Test that VolumeInfo::icon_name works correctly
let info = shepherd_api::VolumeInfo {
percent: 0,
muted: false,
available: true,
backend: Some("test".into()),
restrictions: VolumeRestrictions::unrestricted(),
};
assert_eq!(info.icon_name(), "audio-volume-muted-symbolic");
let info = shepherd_api::VolumeInfo {
percent: 50,
muted: false,
available: true,
backend: Some("test".into()),
restrictions: VolumeRestrictions::unrestricted(),
};
assert_eq!(info.icon_name(), "audio-volume-medium-symbolic");
let info = shepherd_api::VolumeInfo {
percent: 100,
muted: true,
available: true,
backend: Some("test".into()),
restrictions: VolumeRestrictions::unrestricted(),
};
assert_eq!(info.icon_name(), "audio-volume-muted-symbolic");
}
}