alsa/
hctl.rs

1//! HCtl API - for mixer control and jack detection
2//!
3//! # Example
4//! Print all jacks and their status
5//!
6//! ```
7//! for a in ::alsa::card::Iter::new().map(|x| x.unwrap()) {
8//!     use std::ffi::CString;
9//!     use alsa::hctl::HCtl;
10//!     let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
11//!     h.load().unwrap();
12//!     for b in h.elem_iter() {
13//!         use alsa::ctl::ElemIface;
14//!         let id = b.get_id().unwrap();
15//!         if id.get_interface() != ElemIface::Card { continue; }
16//!         let name = id.get_name().unwrap();
17//!         if !name.ends_with(" Jack") { continue; }
18//!         if name.ends_with(" Phantom Jack") {
19//!             println!("{} is always present", &name[..name.len()-13])
20//!         }
21//!         else { println!("{} is {}", &name[..name.len()-5],
22//!             if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
23//!         }
24//!     }
25//! }
26//! ```
27
28use crate::{alsa, Card};
29use std::ffi::{CStr, CString};
30use super::error::*;
31use std::ptr;
32use super::{ctl_int, poll};
33use libc::{c_short, c_uint, c_int, pollfd};
34
35
36/// [snd_hctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
37pub struct HCtl(*mut alsa::snd_hctl_t);
38
39unsafe impl Send for HCtl {}
40
41impl Drop for HCtl {
42    fn drop(&mut self) { unsafe { alsa::snd_hctl_close(self.0) }; }
43}
44
45impl HCtl {
46    /// Wrapper around open that takes a &str instead of a &CStr
47    pub fn new(c: &str, nonblock: bool) -> Result<HCtl> {
48        Self::open(&CString::new(c).unwrap(), nonblock)
49    }
50
51    /// Open does not support async mode (it's not very Rustic anyway)
52    /// Note: You probably want to call `load` afterwards.
53    pub fn open(c: &CStr, nonblock: bool) -> Result<HCtl> {
54        let mut r = ptr::null_mut();
55        let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
56        acheck!(snd_hctl_open(&mut r, c.as_ptr(), flags))
57            .map(|_| HCtl(r))
58    }
59
60    /// Wrapper around open. You probably want to call `load` afterwards.
61    pub fn from_card(c: &Card, nonblock: bool) -> Result<HCtl> {
62        let s = format!("hw:{}", c.get_index());
63        HCtl::new(&s, nonblock)
64    }
65
66    pub fn load(&self) -> Result<()> { acheck!(snd_hctl_load(self.0)).map(|_| ()) }
67
68    pub fn elem_iter(&self) -> ElemIter { ElemIter(self, ptr::null_mut()) }
69
70    pub fn find_elem(&self, id: &ctl_int::ElemId) -> Option<Elem> {
71        let p = unsafe { alsa::snd_hctl_find_elem(self.0, ctl_int::elem_id_ptr(id)) };
72        if p.is_null() { None } else { Some(Elem(self, p)) }
73    }
74
75    pub fn handle_events(&self) -> Result<u32> {
76        acheck!(snd_hctl_handle_events(self.0)).map(|x| x as u32)
77    }
78
79    pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> {
80        acheck!(snd_hctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) }
81}
82
83impl poll::Descriptors for HCtl {
84    fn count(&self) -> usize {
85        unsafe { alsa::snd_hctl_poll_descriptors_count(self.0) as usize }
86    }
87    fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
88        let z = unsafe { alsa::snd_hctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
89        from_code("snd_hctl_poll_descriptors", z).map(|_| z as usize)
90    }
91    fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
92        let mut r = 0;
93        let z = unsafe { alsa::snd_hctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
94        from_code("snd_hctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
95    }
96}
97
98/// Iterates over elements for a `HCtl`
99pub struct ElemIter<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
100
101impl<'a> Iterator for ElemIter<'a> {
102    type Item = Elem<'a>;
103    fn next(&mut self) -> Option<Elem<'a>> {
104        self.1 = if self.1.is_null() { unsafe { alsa::snd_hctl_first_elem((self.0).0) }}
105            else { unsafe { alsa::snd_hctl_elem_next(self.1) }};
106        if self.1.is_null() { None }
107        else { Some(Elem(self.0, self.1)) }
108    }
109}
110
111
112/// [snd_hctl_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
113pub struct Elem<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
114
115impl<'a> Elem<'a> {
116    pub fn get_id(&self) -> Result<ctl_int::ElemId> {
117        let v = ctl_int::elem_id_new()?;
118        unsafe { alsa::snd_hctl_elem_get_id(self.1, ctl_int::elem_id_ptr(&v)) };
119        Ok(v)
120    }
121    pub fn info(&self) -> Result<ctl_int::ElemInfo> {
122        let v = ctl_int::elem_info_new()?;
123        acheck!(snd_hctl_elem_info(self.1, ctl_int::elem_info_ptr(&v))).map(|_| v)
124    }
125    pub fn read(&self) -> Result<ctl_int::ElemValue> {
126        let i = self.info()?;
127        let v = ctl_int::elem_value_new(i.get_type(), i.get_count())?;
128        acheck!(snd_hctl_elem_read(self.1, ctl_int::elem_value_ptr(&v))).map(|_| v)
129    }
130
131    pub fn write(&self, v: &ctl_int::ElemValue) -> Result<bool> {
132        acheck!(snd_hctl_elem_write(self.1, ctl_int::elem_value_ptr(v))).map(|e| e > 0)
133    }
134}
135
136#[test]
137fn print_hctls() {
138    for a in super::card::Iter::new().map(|x| x.unwrap()) {
139        use std::ffi::CString;
140        let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
141        h.load().unwrap();
142        println!("Card {}:", a.get_name().unwrap());
143        for b in h.elem_iter() {
144            println!("  {:?} - {:?}", b.get_id().unwrap(), b.read().unwrap());
145        }
146    }
147}
148
149#[test]
150fn print_jacks() {
151    for a in super::card::Iter::new().map(|x| x.unwrap()) {
152        use std::ffi::CString;
153        let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
154        h.load().unwrap();
155        for b in h.elem_iter() {
156            let id = b.get_id().unwrap();
157            if id.get_interface() != super::ctl_int::ElemIface::Card { continue; }
158            let name = id.get_name().unwrap();
159            if !name.ends_with(" Jack") { continue; }
160            if name.ends_with(" Phantom Jack") {
161                println!("{} is always present", &name[..name.len()-13])
162            }
163            else { println!("{} is {}", &name[..name.len()-5],
164                if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
165            }
166        }
167    }
168}