Skip to main content

pulsectl/
lib.rs

1/// `pulsectl` is a high level wrapper around the PulseAudio bindings supplied by
2/// `libpulse_binding`. It provides simple access to sinks, inputs, sources and outputs allowing
3/// one to write audio control programs with ease.
4///
5/// ## Quick Example
6///
7/// The following example demonstrates listing all of the playback devices currently connected
8///
9/// See examples/change_device_vol.rs for a more complete example
10/// ```no_run
11/// extern crate pulsectl;
12///
13/// use std::io;
14///
15/// use pulsectl::controllers::SinkController;
16/// use pulsectl::controllers::DeviceControl;
17/// fn main() {
18///     // create handler that calls functions on playback devices and apps
19///     let mut handler = SinkController::create().unwrap();
20///     let devices = handler
21///         .list_devices()
22///        .expect("Could not get list of playback devices");
23///
24///     println!("Playback Devices");
25///     for dev in devices.clone() {
26///         println!(
27///             "[{}] {}, Volume: {}",
28///             dev.index,
29///             dev.description.as_ref().unwrap(),
30///             dev.volume.print()
31///         );
32///     }
33/// }
34/// ```
35extern crate libpulse_binding as pulse;
36
37use std::cell::RefCell;
38use std::ops::Deref;
39use std::rc::Rc;
40
41use pulse::{
42    context::{introspect, Context},
43    mainloop::standard::{IterateResult, Mainloop},
44    operation::{Operation, State},
45    proplist::Proplist,
46};
47
48use crate::errors::{PulseCtlError, PulseCtlErrorType::*};
49
50pub mod controllers;
51mod errors;
52
53pub struct Handler {
54    pub mainloop: Rc<RefCell<Mainloop>>,
55    pub context: Rc<RefCell<Context>>,
56    pub introspect: introspect::Introspector,
57}
58
59fn connect_error(err: &str) -> PulseCtlError {
60    PulseCtlError::new(ConnectError, err)
61}
62
63impl Handler {
64    pub fn connect(name: &str) -> Result<Handler, PulseCtlError> {
65        let mut proplist = Proplist::new().unwrap();
66        proplist
67            .set_str(pulse::proplist::properties::APPLICATION_NAME, name)
68            .unwrap();
69
70        let mainloop;
71        if let Some(m) = Mainloop::new() {
72            mainloop = Rc::new(RefCell::new(m));
73        } else {
74            return Err(connect_error("Failed to create mainloop"));
75        }
76
77        let context;
78        if let Some(c) =
79            Context::new_with_proplist(mainloop.borrow().deref(), "MainConn", &proplist)
80        {
81            context = Rc::new(RefCell::new(c));
82        } else {
83            return Err(connect_error("Failed to create new context"));
84        }
85
86        context
87            .borrow_mut()
88            .connect(None, pulse::context::FlagSet::NOFLAGS, None)
89            .map_err(|_| connect_error("Failed to connect context"))?;
90
91        loop {
92            match mainloop.borrow_mut().iterate(false) {
93                IterateResult::Err(e) => {
94                    eprintln!("iterate state was not success, quitting...");
95                    return Err(e.into());
96                }
97                IterateResult::Success(_) => {}
98                IterateResult::Quit(_) => {
99                    eprintln!("iterate state was not success, quitting...");
100                    return Err(PulseCtlError::new(
101                        ConnectError,
102                        "Iterate state quit without an error",
103                    ));
104                }
105            }
106
107            match context.borrow().get_state() {
108                pulse::context::State::Ready => break,
109                pulse::context::State::Failed | pulse::context::State::Terminated => {
110                    eprintln!("context state failed/terminated, quitting...");
111                    return Err(PulseCtlError::new(
112                        ConnectError,
113                        "Context state failed/terminated without an error",
114                    ));
115                }
116                _ => {}
117            }
118        }
119
120        let introspect = context.borrow_mut().introspect();
121        Ok(Handler {
122            mainloop,
123            context,
124            introspect,
125        })
126    }
127
128    // loop until the passed operation is completed
129    pub fn wait_for_operation<G: ?Sized>(
130        &mut self,
131        op: Operation<G>,
132    ) -> Result<(), errors::PulseCtlError> {
133        loop {
134            match self.mainloop.borrow_mut().iterate(false) {
135                IterateResult::Err(e) => return Err(e.into()),
136                IterateResult::Success(_) => {}
137                IterateResult::Quit(_) => {
138                    return Err(PulseCtlError::new(
139                        OperationError,
140                        "Iterate state quit without an error",
141                    ));
142                }
143            }
144            match op.get_state() {
145                State::Done => {
146                    break;
147                }
148                State::Running => {}
149                State::Cancelled => {
150                    return Err(PulseCtlError::new(
151                        OperationError,
152                        "Operation cancelled without an error",
153                    ));
154                }
155            }
156        }
157        Ok(())
158    }
159}
160
161impl Drop for Handler {
162    fn drop(&mut self) {
163        self.context.borrow_mut().disconnect();
164        self.mainloop.borrow_mut().quit(pulse::def::Retval(0));
165    }
166}