pulsectl/controllers/
mod.rs

1//! Helpful wrappers when working with applications and devices.
2//!
3//! Both of these controllers implement the same API, defined by the traits DeviceControl
4//! and AppControl.
5
6pub(crate) mod error;
7pub mod types;
8
9use pulse::{
10    callbacks::ListResult,
11    context::introspect,
12    volume::{ChannelVolumes, Volume},
13};
14
15use std::cell::RefCell;
16use std::clone::Clone;
17use std::rc::Rc;
18
19use self::error::ControllerError;
20use self::types::{ApplicationInfo, DeviceInfo, ServerInfo};
21use crate::Handler;
22
23pub trait DeviceControl<T> {
24    fn get_default_device(&mut self) -> Result<T, ControllerError>;
25    fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError>;
26
27    fn list_devices(&mut self) -> Result<Vec<T>, ControllerError>;
28    fn get_device_by_index(&mut self, index: u32) -> Result<T, ControllerError>;
29    fn get_device_by_name(&mut self, name: &str) -> Result<T, ControllerError>;
30    fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes);
31    fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes);
32    fn set_device_mute_by_index(&mut self, index: u32, mute: bool);
33    fn set_device_mute_by_name(&mut self, name: &str, mute: bool);
34    fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64);
35    fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64);
36}
37
38pub trait AppControl<T> {
39    fn list_applications(&mut self) -> Result<Vec<T>, ControllerError>;
40
41    fn get_app_by_index(&mut self, index: u32) -> Result<T, ControllerError>;
42    fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64);
43    fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64);
44
45    fn move_app_by_index(
46        &mut self,
47        stream_index: u32,
48        device_index: u32,
49    ) -> Result<bool, ControllerError>;
50    fn move_app_by_name(
51        &mut self,
52        stream_index: u32,
53        device_name: &str,
54    ) -> Result<bool, ControllerError>;
55    fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError>;
56}
57
58fn volume_from_percent(volume: f64) -> f64 {
59    (volume * 100.0) * (f64::from(pulse::volume::Volume::NORMAL.0) / 100.0)
60}
61
62/// This handles device that plays out audio (e.g., headphone), so this is appropriate when dealing
63/// with audio playback devices and applications.
64pub struct SinkController {
65    pub handler: Handler,
66}
67
68impl SinkController {
69    pub fn create() -> Result<Self, ControllerError> {
70        let handler = Handler::connect("SinkController")?;
71        Ok(SinkController { handler })
72    }
73
74    pub fn get_server_info(&mut self) -> Result<ServerInfo, ControllerError> {
75        let server = Rc::new(RefCell::new(Some(None)));
76        let server_ref = server.clone();
77
78        let op = self.handler.introspect.get_server_info(move |res| {
79            server_ref
80                .borrow_mut()
81                .as_mut()
82                .unwrap()
83                .replace(res.into());
84        });
85        self.handler.wait_for_operation(op)?;
86        let mut result = server.borrow_mut();
87        result.take().unwrap().ok_or_else(|| {
88            ControllerError::GetInfo("Error getting information about the server".to_string())
89        })
90    }
91}
92
93impl DeviceControl<DeviceInfo> for SinkController {
94    fn get_default_device(&mut self) -> Result<DeviceInfo, ControllerError> {
95        let server_info = self.get_server_info();
96        match server_info {
97            Ok(info) => self.get_device_by_name(info.default_sink_name.unwrap().as_ref()),
98            Err(e) => Err(e),
99        }
100    }
101    fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError> {
102        let success = Rc::new(RefCell::new(false));
103        let success_ref = success.clone();
104
105        let op = self
106            .handler
107            .context
108            .borrow_mut()
109            .set_default_sink(name, move |res| success_ref.borrow_mut().clone_from(&res));
110        self.handler.wait_for_operation(op)?;
111        let result = *success.borrow_mut();
112        Ok(result)
113    }
114
115    fn list_devices(&mut self) -> Result<Vec<DeviceInfo>, ControllerError> {
116        let list = Rc::new(RefCell::new(Some(Vec::new())));
117        let list_ref = list.clone();
118
119        let op = self.handler.introspect.get_sink_info_list(
120            move |sink_list: ListResult<&introspect::SinkInfo>| {
121                if let ListResult::Item(item) = sink_list {
122                    list_ref.borrow_mut().as_mut().unwrap().push(item.into());
123                }
124            },
125        );
126        self.handler.wait_for_operation(op)?;
127        let mut result = list.borrow_mut();
128        result
129            .take()
130            .ok_or_else(|| ControllerError::GetInfo("Error getting device list".to_string()))
131    }
132    fn get_device_by_index(&mut self, index: u32) -> Result<DeviceInfo, ControllerError> {
133        let device = Rc::new(RefCell::new(Some(None)));
134        let dev_ref = device.clone();
135        let op = self.handler.introspect.get_sink_info_by_index(
136            index,
137            move |sink_list: ListResult<&introspect::SinkInfo>| {
138                if let ListResult::Item(item) = sink_list {
139                    dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
140                }
141            },
142        );
143        self.handler.wait_for_operation(op)?;
144        let mut result = device.borrow_mut();
145        result
146            .take()
147            .unwrap()
148            .ok_or_else(|| ControllerError::GetInfo("Error getting requested device".to_string()))
149    }
150    fn get_device_by_name(&mut self, name: &str) -> Result<DeviceInfo, ControllerError> {
151        let device = Rc::new(RefCell::new(Some(None)));
152        let dev_ref = device.clone();
153        let op = self.handler.introspect.get_sink_info_by_name(
154            name,
155            move |sink_list: ListResult<&introspect::SinkInfo>| {
156                if let ListResult::Item(item) = sink_list {
157                    dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
158                }
159            },
160        );
161        self.handler.wait_for_operation(op)?;
162        let mut result = device.borrow_mut();
163        result
164            .take()
165            .unwrap()
166            .ok_or_else(|| ControllerError::GetInfo("Error getting requested device".to_string()))
167    }
168
169    fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes) {
170        let op = self
171            .handler
172            .introspect
173            .set_sink_volume_by_index(index, volume, None);
174        self.handler.wait_for_operation(op).ok();
175    }
176    fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
177        let op = self
178            .handler
179            .introspect
180            .set_sink_volume_by_name(name, volume, None);
181        self.handler.wait_for_operation(op).ok();
182    }
183    fn set_device_mute_by_index(&mut self, index: u32, mute: bool) {
184        let op = self
185            .handler
186            .introspect
187            .set_sink_mute_by_index(index, mute, None);
188        self.handler.wait_for_operation(op).ok();
189    }
190    fn set_device_mute_by_name(&mut self, name: &str, mute: bool) {
191        let op = self
192            .handler
193            .introspect
194            .set_sink_mute_by_name(name, mute, None);
195        self.handler.wait_for_operation(op).ok();
196    }
197    fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64) {
198        if let Ok(mut dev_ref) = self.get_device_by_index(index) {
199            let new_vol = Volume(volume_from_percent(delta) as u32);
200            if let Some(volumes) = dev_ref.volume.increase(new_vol) {
201                let op = self
202                    .handler
203                    .introspect
204                    .set_sink_volume_by_index(index, volumes, None);
205                self.handler.wait_for_operation(op).ok();
206            }
207        }
208    }
209    fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64) {
210        if let Ok(mut dev_ref) = self.get_device_by_index(index) {
211            let new_vol = Volume(volume_from_percent(delta) as u32);
212            if let Some(volumes) = dev_ref.volume.decrease(new_vol) {
213                let op = self
214                    .handler
215                    .introspect
216                    .set_sink_volume_by_index(index, volumes, None);
217                self.handler.wait_for_operation(op).ok();
218            }
219        }
220    }
221}
222
223impl AppControl<ApplicationInfo> for SinkController {
224    fn list_applications(&mut self) -> Result<Vec<ApplicationInfo>, ControllerError> {
225        let list = Rc::new(RefCell::new(Some(Vec::new())));
226        let list_ref = list.clone();
227
228        let op = self.handler.introspect.get_sink_input_info_list(
229            move |sink_list: ListResult<&introspect::SinkInputInfo>| {
230                if let ListResult::Item(item) = sink_list {
231                    list_ref.borrow_mut().as_mut().unwrap().push(item.into());
232                }
233            },
234        );
235        self.handler.wait_for_operation(op)?;
236        let mut result = list.borrow_mut();
237        result
238            .take()
239            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
240    }
241
242    fn get_app_by_index(&mut self, index: u32) -> Result<ApplicationInfo, ControllerError> {
243        let app = Rc::new(RefCell::new(Some(None)));
244        let app_ref = app.clone();
245        let op = self.handler.introspect.get_sink_input_info(
246            index,
247            move |sink_list: ListResult<&introspect::SinkInputInfo>| {
248                if let ListResult::Item(item) = sink_list {
249                    app_ref.borrow_mut().as_mut().unwrap().replace(item.into());
250                }
251            },
252        );
253        self.handler.wait_for_operation(op)?;
254        let mut result = app.borrow_mut();
255        result
256            .take()
257            .unwrap()
258            .ok_or_else(|| ControllerError::GetInfo("Error getting requested app".to_string()))
259    }
260
261    fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64) {
262        if let Ok(mut app_ref) = self.get_app_by_index(index) {
263            let new_vol = Volume(volume_from_percent(delta) as u32);
264            if let Some(volumes) = app_ref.volume.increase(new_vol) {
265                let op = self
266                    .handler
267                    .introspect
268                    .set_sink_input_volume(index, volumes, None);
269                self.handler.wait_for_operation(op).ok();
270            }
271        }
272    }
273
274    fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64) {
275        if let Ok(mut app_ref) = self.get_app_by_index(index) {
276            let new_vol = Volume(volume_from_percent(delta) as u32);
277            if let Some(volumes) = app_ref.volume.decrease(new_vol) {
278                let op = self
279                    .handler
280                    .introspect
281                    .set_sink_input_volume(index, volumes, None);
282                self.handler.wait_for_operation(op).ok();
283            }
284        }
285    }
286
287    fn move_app_by_index(
288        &mut self,
289        stream_index: u32,
290        device_index: u32,
291    ) -> Result<bool, ControllerError> {
292        let success = Rc::new(RefCell::new(false));
293        let success_ref = success.clone();
294        let op = self.handler.introspect.move_sink_input_by_index(
295            stream_index,
296            device_index,
297            Some(Box::new(move |res| {
298                success_ref.borrow_mut().clone_from(&res)
299            })),
300        );
301        self.handler.wait_for_operation(op)?;
302        let result = *success.borrow_mut();
303        Ok(result)
304    }
305
306    fn move_app_by_name(
307        &mut self,
308        stream_index: u32,
309        device_name: &str,
310    ) -> Result<bool, ControllerError> {
311        let success = Rc::new(RefCell::new(false));
312        let success_ref = success.clone();
313        let op = self.handler.introspect.move_sink_input_by_name(
314            stream_index,
315            device_name,
316            Some(Box::new(move |res| {
317                success_ref.borrow_mut().clone_from(&res)
318            })),
319        );
320        self.handler.wait_for_operation(op)?;
321        let result = *success.borrow_mut();
322        Ok(result)
323    }
324
325    fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError> {
326        let success = Rc::new(RefCell::new(false));
327        let success_ref = success.clone();
328        let op = self.handler.introspect.set_sink_input_mute(
329            index,
330            mute,
331            Some(Box::new(move |res| {
332                success_ref.borrow_mut().clone_from(&res)
333            })),
334        );
335        self.handler.wait_for_operation(op)?;
336        let result = *success.borrow_mut();
337        Ok(result)
338    }
339}
340
341/// This handles devices which takes in audio (e.g., microphone), so this is appropriate when
342/// manipulating recording devices such as microphone volume.
343pub struct SourceController {
344    pub handler: Handler,
345}
346
347impl SourceController {
348    pub fn create() -> Result<Self, ControllerError> {
349        let handler = Handler::connect("SourceController")?;
350        Ok(SourceController { handler })
351    }
352
353    pub fn get_server_info(&mut self) -> Result<ServerInfo, ControllerError> {
354        let server = Rc::new(RefCell::new(Some(None)));
355        let server_ref = server.clone();
356
357        let op = self.handler.introspect.get_server_info(move |res| {
358            server_ref
359                .borrow_mut()
360                .as_mut()
361                .unwrap()
362                .replace(res.into());
363        });
364        self.handler.wait_for_operation(op)?;
365        let mut result = server.borrow_mut();
366        result
367            .take()
368            .unwrap()
369            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
370    }
371}
372
373impl DeviceControl<DeviceInfo> for SourceController {
374    fn get_default_device(&mut self) -> Result<DeviceInfo, ControllerError> {
375        let server_info = self.get_server_info();
376        match server_info {
377            Ok(info) => self.get_device_by_name(info.default_sink_name.unwrap().as_ref()),
378            Err(e) => Err(e),
379        }
380    }
381    fn set_default_device(&mut self, name: &str) -> Result<bool, ControllerError> {
382        let success = Rc::new(RefCell::new(false));
383        let success_ref = success.clone();
384
385        let op = self
386            .handler
387            .context
388            .borrow_mut()
389            .set_default_source(name, move |res| success_ref.borrow_mut().clone_from(&res));
390        self.handler.wait_for_operation(op)?;
391        let result = *success.borrow_mut();
392        Ok(result)
393    }
394
395    fn list_devices(&mut self) -> Result<Vec<DeviceInfo>, ControllerError> {
396        let list = Rc::new(RefCell::new(Some(Vec::new())));
397        let list_ref = list.clone();
398
399        let op = self.handler.introspect.get_source_info_list(
400            move |sink_list: ListResult<&introspect::SourceInfo>| {
401                if let ListResult::Item(item) = sink_list {
402                    list_ref.borrow_mut().as_mut().unwrap().push(item.into());
403                }
404            },
405        );
406        self.handler.wait_for_operation(op)?;
407        let mut result = list.borrow_mut();
408        result
409            .take()
410            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
411    }
412    fn get_device_by_index(&mut self, index: u32) -> Result<DeviceInfo, ControllerError> {
413        let device = Rc::new(RefCell::new(Some(None)));
414        let dev_ref = device.clone();
415        let op = self.handler.introspect.get_source_info_by_index(
416            index,
417            move |sink_list: ListResult<&introspect::SourceInfo>| {
418                if let ListResult::Item(item) = sink_list {
419                    dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
420                }
421            },
422        );
423        self.handler.wait_for_operation(op)?;
424        let mut result = device.borrow_mut();
425        result
426            .take()
427            .unwrap()
428            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
429    }
430    fn get_device_by_name(&mut self, name: &str) -> Result<DeviceInfo, ControllerError> {
431        let device = Rc::new(RefCell::new(Some(None)));
432        let dev_ref = device.clone();
433        let op = self.handler.introspect.get_source_info_by_name(
434            name,
435            move |sink_list: ListResult<&introspect::SourceInfo>| {
436                if let ListResult::Item(item) = sink_list {
437                    dev_ref.borrow_mut().as_mut().unwrap().replace(item.into());
438                }
439            },
440        );
441        self.handler.wait_for_operation(op)?;
442        let mut result = device.borrow_mut();
443        result
444            .take()
445            .unwrap()
446            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
447    }
448
449    fn set_device_volume_by_index(&mut self, index: u32, volume: &ChannelVolumes) {
450        let op = self
451            .handler
452            .introspect
453            .set_source_volume_by_index(index, volume, None);
454        self.handler.wait_for_operation(op).ok();
455    }
456    fn set_device_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) {
457        let op = self
458            .handler
459            .introspect
460            .set_source_volume_by_name(name, volume, None);
461        self.handler.wait_for_operation(op).ok();
462    }
463    fn increase_device_volume_by_percent(&mut self, index: u32, delta: f64) {
464        if let Ok(mut dev_ref) = self.get_device_by_index(index) {
465            let new_vol = Volume(volume_from_percent(delta) as u32);
466            if let Some(volumes) = dev_ref.volume.increase(new_vol) {
467                let op = self
468                    .handler
469                    .introspect
470                    .set_source_volume_by_index(index, volumes, None);
471                self.handler.wait_for_operation(op).ok();
472            }
473        }
474    }
475    fn set_device_mute_by_index(&mut self, index: u32, mute: bool) {
476        let op = self
477            .handler
478            .introspect
479            .set_sink_mute_by_index(index, mute, None);
480        self.handler.wait_for_operation(op).ok();
481    }
482    fn set_device_mute_by_name(&mut self, name: &str, mute: bool) {
483        let op = self
484            .handler
485            .introspect
486            .set_sink_mute_by_name(name, mute, None);
487        self.handler.wait_for_operation(op).ok();
488    }
489    fn decrease_device_volume_by_percent(&mut self, index: u32, delta: f64) {
490        if let Ok(mut dev_ref) = self.get_device_by_index(index) {
491            let new_vol = Volume(volume_from_percent(delta) as u32);
492            if let Some(volumes) = dev_ref.volume.decrease(new_vol) {
493                let op = self
494                    .handler
495                    .introspect
496                    .set_source_volume_by_index(index, volumes, None);
497                self.handler.wait_for_operation(op).ok();
498            }
499        }
500    }
501}
502
503impl AppControl<ApplicationInfo> for SourceController {
504    fn list_applications(&mut self) -> Result<Vec<ApplicationInfo>, ControllerError> {
505        let list = Rc::new(RefCell::new(Some(Vec::new())));
506        let list_ref = list.clone();
507
508        let op = self.handler.introspect.get_source_output_info_list(
509            move |sink_list: ListResult<&introspect::SourceOutputInfo>| {
510                if let ListResult::Item(item) = sink_list {
511                    list_ref.borrow_mut().as_mut().unwrap().push(item.into());
512                }
513            },
514        );
515        self.handler.wait_for_operation(op)?;
516        let mut result = list.borrow_mut();
517        result
518            .take()
519            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
520    }
521
522    fn get_app_by_index(&mut self, index: u32) -> Result<ApplicationInfo, ControllerError> {
523        let app = Rc::new(RefCell::new(Some(None)));
524        let app_ref = app.clone();
525        let op = self.handler.introspect.get_source_output_info(
526            index,
527            move |sink_list: ListResult<&introspect::SourceOutputInfo>| {
528                if let ListResult::Item(item) = sink_list {
529                    app_ref.borrow_mut().as_mut().unwrap().replace(item.into());
530                }
531            },
532        );
533        self.handler.wait_for_operation(op)?;
534        let mut result = app.borrow_mut();
535        result
536            .take()
537            .unwrap()
538            .ok_or_else(|| ControllerError::GetInfo("Error getting application list".to_string()))
539    }
540
541    fn increase_app_volume_by_percent(&mut self, index: u32, delta: f64) {
542        if let Ok(mut app_ref) = self.get_app_by_index(index) {
543            let new_vol = Volume(volume_from_percent(delta) as u32);
544            if let Some(volumes) = app_ref.volume.increase(new_vol) {
545                let op = self
546                    .handler
547                    .introspect
548                    .set_source_output_volume(index, volumes, None);
549                self.handler.wait_for_operation(op).ok();
550            }
551        }
552    }
553
554    fn decrease_app_volume_by_percent(&mut self, index: u32, delta: f64) {
555        if let Ok(mut app_ref) = self.get_app_by_index(index) {
556            let new_vol = Volume(volume_from_percent(delta) as u32);
557            if let Some(volumes) = app_ref.volume.decrease(new_vol) {
558                let op = self
559                    .handler
560                    .introspect
561                    .set_source_output_volume(index, volumes, None);
562                self.handler.wait_for_operation(op).ok();
563            }
564        }
565    }
566
567    fn move_app_by_index(
568        &mut self,
569        stream_index: u32,
570        device_index: u32,
571    ) -> Result<bool, ControllerError> {
572        let success = Rc::new(RefCell::new(false));
573        let success_ref = success.clone();
574        let op = self.handler.introspect.move_source_output_by_index(
575            stream_index,
576            device_index,
577            Some(Box::new(move |res| {
578                success_ref.borrow_mut().clone_from(&res)
579            })),
580        );
581        self.handler.wait_for_operation(op)?;
582        let result = *success.borrow_mut();
583        Ok(result)
584    }
585
586    fn move_app_by_name(
587        &mut self,
588        stream_index: u32,
589        device_name: &str,
590    ) -> Result<bool, ControllerError> {
591        let success = Rc::new(RefCell::new(false));
592        let success_ref = success.clone();
593        let op = self.handler.introspect.move_source_output_by_name(
594            stream_index,
595            device_name,
596            Some(Box::new(move |res| {
597                success_ref.borrow_mut().clone_from(&res)
598            })),
599        );
600        self.handler.wait_for_operation(op)?;
601        let result = *success.borrow_mut();
602        Ok(result)
603    }
604
605    fn set_app_mute(&mut self, index: u32, mute: bool) -> Result<bool, ControllerError> {
606        let success = Rc::new(RefCell::new(false));
607        let success_ref = success.clone();
608        let op = self.handler.introspect.set_source_mute_by_index(
609            index,
610            mute,
611            Some(Box::new(move |res| {
612                success_ref.borrow_mut().clone_from(&res)
613            })),
614        );
615        self.handler.wait_for_operation(op)?;
616        let result = *success.borrow_mut();
617        Ok(result)
618    }
619}