i3status_rs/blocks/sound/
pipewire.rs1use tokio::sync::mpsc::{UnboundedReceiver, unbounded_channel};
2
3use super::*;
4use crate::pipewire::{CLIENT, CommandKind, EventKind, PIPEWIRE_CONNECTION_ERROR_MSG, PwSender};
5
6pub(super) struct Device {
7 device_kind: DeviceKind,
8 match_name: Option<String>,
9 id: Option<u32>,
10 name: String,
11 description: Option<String>,
12 active_port: Option<String>,
13 form_factor: Option<String>,
14 volume: Vec<f32>,
15 volume_avg: u32,
16 muted: bool,
17 updates: UnboundedReceiver<EventKind>,
18 command_sender: PwSender<CommandKind>,
19}
20
21impl Device {
22 pub(super) async fn new(device_kind: DeviceKind, match_name: Option<String>) -> Result<Self> {
23 let client = CLIENT.as_ref().error("Could not get client")?;
24
25 let (tx, rx) = unbounded_channel();
26 client.add_event_listener(tx);
27 let command_sender = client.add_command_listener();
28 let mut s = Self {
29 device_kind,
30 match_name,
31 id: None,
32 name: "".into(),
33 description: None,
34 active_port: None,
35 form_factor: None,
36 volume: Vec::new(),
37 volume_avg: 0,
38 muted: false,
39 updates: rx,
40 command_sender,
41 };
42 s.get_info().await?;
43 Ok(s)
44 }
45}
46
47#[async_trait]
48impl SoundDevice for Device {
49 fn volume(&self) -> u32 {
50 self.volume_avg
51 }
52
53 fn muted(&self) -> bool {
54 self.muted
55 }
56
57 fn output_name(&self) -> String {
58 self.name.clone()
59 }
60
61 fn output_description(&self) -> Option<String> {
62 self.description.clone()
63 }
64
65 fn active_port(&self) -> Option<String> {
66 self.active_port.clone()
67 }
68
69 fn form_factor(&self) -> Option<&str> {
70 self.form_factor.as_deref()
71 }
72
73 async fn get_info(&mut self) -> Result<()> {
74 let client = CLIENT.as_ref().error("Could not get client")?;
75 if client.is_terminated() {
76 return Err(Error::new(PIPEWIRE_CONNECTION_ERROR_MSG));
77 }
78
79 let data = client.data.lock().unwrap();
80
81 let name = if self.match_name.is_some() {
82 &self.match_name
84 } else {
85 match self.device_kind {
87 DeviceKind::Sink => &data.default_metadata.sink,
88 DeviceKind::Source => &data.default_metadata.source,
89 }
90 };
91
92 let Some(name) = name else {
93 return Ok(());
95 };
96
97 if let Some((id, node)) = data.nodes.iter().find(|(_, node)| node.name == *name) {
98 self.id = Some(*id);
99 if let Some(volume) = &node.volume {
100 self.volume = volume.clone();
101 self.volume_avg = (volume.iter().sum::<f32>() / volume.len() as f32).round() as u32;
102 }
103 if let Some(muted) = node.muted {
104 self.muted = muted;
105 }
106 self.name = node.name.clone();
107 self.description = node.description.clone();
108 self.form_factor = node.form_factor.clone();
109
110 if let Some(device_id) = node.device_id
111 && let Some(direction) = node.direction
112 && let Some(directed_routes) = data.directed_routes.get(&device_id)
113 && let Some(route) = directed_routes.get_route(direction)
114 {
115 self.active_port = Some(route.name.clone());
116 }
117 } else {
118 self.id = None;
119 }
120
121 Ok(())
122 }
123
124 async fn set_volume(&mut self, step: i32, max_vol: Option<u32>) -> Result<()> {
125 if let Some(id) = self.id {
126 let volume = self
127 .volume
128 .iter()
129 .map(|&vol| {
130 let uncapped_vol = 0_f32.max(vol + step as f32);
131 if let Some(vol_cap) = max_vol {
132 uncapped_vol.min(vol_cap as f32)
133 } else {
134 uncapped_vol
135 }
136 })
137 .collect();
138
139 self.command_sender
140 .send(CommandKind::SetVolume(id, volume))
141 .map_err(|_| Error::new("Could not set volume"))?;
142 }
143 Ok(())
144 }
145
146 async fn toggle(&mut self) -> Result<()> {
147 if let Some(id) = self.id {
148 self.command_sender
149 .send(CommandKind::Mute(id, !self.muted))
150 .map_err(|_| Error::new("Could not toggle mute"))?;
151 }
152 Ok(())
153 }
154
155 async fn wait_for_update(&mut self) -> Result<()> {
156 while let Some(event) = self.updates.recv().await {
157 if event.intersects(
158 EventKind::DEFAULT_META_DATA_UPDATED
159 | EventKind::DEVICE_ADDED
160 | EventKind::DEVICE_PARAM_UPDATE
161 | EventKind::DEVICE_REMOVED
162 | EventKind::NODE_PARAM_UPDATE
163 | EventKind::NODE_STATE_UPDATE
164 | EventKind::PORT_ADDED
165 | EventKind::PORT_REMOVED,
166 ) {
167 break;
168 } else if event.contains(EventKind::PIPEWIRE_CONNECTION_ERROR) {
169 return Err(Error::new(PIPEWIRE_CONNECTION_ERROR_MSG));
170 }
171 }
172 Ok(())
173 }
174}