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: 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`
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: 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]
137fn 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]
150fn 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