volume.c - slstatus - status monitor | |
git clone git://git.suckless.org/slstatus | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
volume.c (4319B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include <fcntl.h> | |
3 #include <stdio.h> | |
4 #include <string.h> | |
5 #include <sys/ioctl.h> | |
6 #include <unistd.h> | |
7 | |
8 #include "../slstatus.h" | |
9 #include "../util.h" | |
10 | |
11 #if defined(__OpenBSD__) | defined(__FreeBSD__) | |
12 #include <poll.h> | |
13 #include <sndio.h> | |
14 #include <stdlib.h> | |
15 #include <sys/queue.h> | |
16 | |
17 struct control { | |
18 LIST_ENTRY(control) next; | |
19 unsigned int addr; | |
20 #define CTRL_NONE 0 | |
21 #define CTRL_LEVEL 1 | |
22 #define CTRL_MUTE 2 | |
23 unsigned int type; | |
24 unsigned int maxval; | |
25 unsigned int val; | |
26 }; | |
27 | |
28 static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(con… | |
29 static struct pollfd *pfds; | |
30 static struct sioctl_hdl *hdl; | |
31 static int initialized; | |
32 | |
33 /* | |
34 * Call-back to obtain the description of all audio controls. | |
35 */ | |
36 static void | |
37 ondesc(void *unused, struct sioctl_desc *desc, int val) | |
38 { | |
39 struct control *c, *ctmp; | |
40 unsigned int type = CTRL_NONE; | |
41 | |
42 if (desc == NULL) | |
43 return; | |
44 | |
45 /* Delete existing audio control with the same address. … | |
46 LIST_FOREACH_SAFE(c, &controls, next, ctmp) { | |
47 if (desc->addr == c->addr) { | |
48 LIST_REMOVE(c, next); | |
49 free(c); | |
50 break; | |
51 } | |
52 } | |
53 | |
54 /* Only match output.level and output.mute audio control… | |
55 if (desc->group[0] != 0 || | |
56 strcmp(desc->node0.name, "output") != 0) | |
57 return; | |
58 if (desc->type == SIOCTL_NUM && | |
59 strcmp(desc->func, "level") == 0) | |
60 type = CTRL_LEVEL; | |
61 else if (desc->type == SIOCTL_SW && | |
62 strcmp(desc->func, "mute") == 0) | |
63 type = CTRL_MUTE; | |
64 else | |
65 return; | |
66 | |
67 c = malloc(sizeof(struct control)); | |
68 if (c == NULL) { | |
69 warn("sndio: failed to allocate audio control\n"… | |
70 return; | |
71 } | |
72 | |
73 c->addr = desc->addr; | |
74 c->type = type; | |
75 c->maxval = desc->maxval; | |
76 c->val = val; | |
77 LIST_INSERT_HEAD(&controls, c, next); | |
78 } | |
79 | |
80 /* | |
81 * Call-back invoked whenever an audio control changes. | |
82 */ | |
83 static void | |
84 onval(void *unused, unsigned int addr, unsigned int val) | |
85 { | |
86 struct control *c; | |
87 | |
88 LIST_FOREACH(c, &controls, next) { | |
89 if (c->addr == addr) | |
90 break; | |
91 } | |
92 c->val = val; | |
93 } | |
94 | |
95 static void | |
96 cleanup(void) | |
97 { | |
98 struct control *c; | |
99 | |
100 if (hdl) { | |
101 sioctl_close(hdl); | |
102 hdl = NULL; | |
103 } | |
104 | |
105 free(pfds); | |
106 pfds = NULL; | |
107 | |
108 while (!LIST_EMPTY(&controls)) { | |
109 c = LIST_FIRST(&controls); | |
110 LIST_REMOVE(c, next); | |
111 free(c); | |
112 } | |
113 } | |
114 | |
115 static int | |
116 init(void) | |
117 { | |
118 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); | |
119 if (hdl == NULL) { | |
120 warn("sndio: cannot open device"); | |
121 goto failed; | |
122 } | |
123 | |
124 if (!sioctl_ondesc(hdl, ondesc, NULL)) { | |
125 warn("sndio: cannot set control description call… | |
126 goto failed; | |
127 } | |
128 | |
129 if (!sioctl_onval(hdl, onval, NULL)) { | |
130 warn("sndio: cannot set control values call-back… | |
131 goto failed; | |
132 } | |
133 | |
134 pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd)); | |
135 if (pfds == NULL) { | |
136 warn("sndio: cannot allocate pollfd structures"); | |
137 goto failed; | |
138 } | |
139 | |
140 return 1; | |
141 failed: | |
142 cleanup(); | |
143 return 0; | |
144 } | |
145 | |
146 const char * | |
147 vol_perc(const char *unused) | |
148 { | |
149 struct control *c; | |
150 int n, v, value; | |
151 | |
152 if (!initialized) | |
153 initialized = init(); | |
154 | |
155 if (hdl == NULL) | |
156 return NULL; | |
157 | |
158 n = sioctl_pollfd(hdl, pfds, POLLIN); | |
159 if (n > 0) { | |
160 n = poll(pfds, n, 0); | |
161 if (n > 0) { | |
162 if (sioctl_revents(hdl, pfds) & POLLHUP)… | |
163 warn("sndio: disconnected"); | |
164 cleanup(); | |
165 initialized = 0; | |
166 return NULL; | |
167 } | |
168 } | |
169 } | |
170 | |
171 value = 100; | |
172 LIST_FOREACH(c, &controls, next) { | |
173 if (c->type == CTRL_MUTE && c->val == 1) | |
174 value = 0; | |
175 else if (c->type == CTRL_LEVEL) { | |
176 v = (c->val * 100 + c->maxval / 2) / c->… | |
177 /* For multiple channels return the mini… | |
178 if (v < value) | |
179 value = v; | |
180 } | |
181 } | |
182 | |
183 return bprintf("%d", value); | |
184 } | |
185 #else | |
186 #include <sys/soundcard.h> | |
187 | |
188 const char * | |
189 vol_perc(const char *card) | |
190 { | |
191 size_t i; | |
192 int v, afd, devmask; | |
193 char *vnames[] = SOUND_DEVICE_NAMES; | |
194 | |
195 if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) { | |
196 warn("open '%s':", card); | |
197 return NULL; | |
198 } | |
199 | |
200 if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) … | |
201 warn("ioctl 'SOUND_MIXER_READ_DEVMASK':"); | |
202 close(afd); | |
203 return NULL; | |
204 } | |
205 for (i = 0; i < LEN(vnames); i++) { | |
206 if (devmask & (1 << i) && !strcmp("vol", vnames[… | |
207 if (ioctl(afd, MIXER_READ(i), &v) < 0) { | |
208 warn("ioctl 'MIXER_READ(%ld)':",… | |
209 close(afd); | |
210 return NULL; | |
211 } | |
212 } | |
213 } | |
214 | |
215 close(afd); | |
216 | |
217 return bprintf("%d", v & 0xff); | |
218 } | |
219 #endif |