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 | |
28 | use crate::{alsa, Card}; |
29 | use std::ffi::{CStr, CString}; |
30 | use super::error::*; |
31 | use std::ptr; |
32 | use super::{ctl_int, poll}; |
33 | use 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 |
37 | pub struct HCtl(*mut alsa::snd_hctl_t); |
38 | |
39 | unsafe impl Send for HCtl {} |
40 | |
41 | impl Drop for HCtl { |
42 | fn drop(&mut self) { unsafe { alsa::snd_hctl_close(self.0) }; } |
43 | } |
44 | |
45 | impl 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 | |
83 | impl 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: i32 = unsafe { alsa::snd_hctl_poll_descriptors(self.0, pfds:p.as_mut_ptr(), space: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: u16 = 0; |
93 | let z: i32 = unsafe { alsa::snd_hctl_poll_descriptors_revents(self.0, pfds:p.as_ptr() as *mut pollfd, nfds:p.len() as c_uint, &mut r) }; |
94 | from_code("snd_hctl_poll_descriptors_revents" , z).map(|_| poll::Flags::from_bits_truncate(bits:r as c_short)) |
95 | } |
96 | } |
97 | |
98 | /// Iterates over elements for a `HCtl` |
99 | pub struct ElemIter<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t); |
100 | |
101 | impl<'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 |
113 | pub struct Elem<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t); |
114 | |
115 | impl<'a> Elem<'a> { |
116 | pub fn get_id(&self) -> Result<ctl_int::ElemId> { |
117 | let v: ElemId = ctl_int::elem_id_new()?; |
118 | unsafe { alsa::snd_hctl_elem_get_id(self.1, ptr:ctl_int::elem_id_ptr(&v)) }; |
119 | Ok(v) |
120 | } |
121 | pub fn info(&self) -> Result<ctl_int::ElemInfo> { |
122 | let v: ElemInfo = 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: ElemInfo = self.info()?; |
127 | let v: ElemValue = ctl_int::elem_value_new(t: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: i32| e > 0) |
133 | } |
134 | } |
135 | |
136 | #[test ] |
137 | fn print_hctls() { |
138 | for a: Card in super::card::Iter::new().map(|x: Result| x.unwrap()) { |
139 | use std::ffi::CString; |
140 | let h: HCtl = HCtl::open(&CString::new(format!("hw: {}" , a.get_index())).unwrap(), nonblock:false).unwrap(); |
141 | h.load().unwrap(); |
142 | println!("Card {}:" , a.get_name().unwrap()); |
143 | for b: Elem<'_> in h.elem_iter() { |
144 | println!(" {:?} - {:?}" , b.get_id().unwrap(), b.read().unwrap()); |
145 | } |
146 | } |
147 | } |
148 | |
149 | #[test ] |
150 | fn print_jacks() { |
151 | for a: Card in super::card::Iter::new().map(|x: Result| x.unwrap()) { |
152 | use std::ffi::CString; |
153 | let h: HCtl = HCtl::open(&CString::new(format!("hw: {}" , a.get_index())).unwrap(), nonblock:false).unwrap(); |
154 | h.load().unwrap(); |
155 | for b: Elem<'_> in h.elem_iter() { |
156 | let id: ElemId = b.get_id().unwrap(); |
157 | if id.get_interface() != super::ctl_int::ElemIface::Card { continue; } |
158 | let name: &str = 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 | } |
169 | |