tmix.c - spoon - [fork] customized build of spoon, the dwm status utility
(HTM) git clone git://src.adamsgaard.dk/spoon
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
tmix.c (5735B)
---
1 #include <err.h>
2 #include <stdio.h>
3
4 #include "util.h"
5
6 #ifdef __OpenBSD__
7 #include <errno.h>
8 #include <poll.h>
9 #include <sndio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #define MAX_CHANNELS 16
14
15 static struct sioctl_hdl *hdl = NULL;
16 static struct pollfd *pfds = NULL;
17
18 static struct {
19 unsigned int addr;
20 int val;
21 } channel[MAX_CHANNELS];
22 static size_t nchannels = 0;
23
24 static int
25 get_output(void)
26 {
27 size_t i;
28 int val;
29
30 if (nchannels == 0)
31 return 0;
32
33 val = 0;
34 for (i = 0; i < nchannels; i++)
35 val += channel[i].val;
36 return 100 * ((val / (double)nchannels) / 255.0);
37 }
38
39 static void
40 ondesc(void *arg, struct sioctl_desc *desc, int val)
41 {
42 size_t i;
43
44 if (desc == NULL)
45 return;
46
47 if (desc->type != SIOCTL_NUM ||
48 strcmp(desc->func, "level") != 0 ||
49 strcmp(desc->node0.name, "output") != 0)
50 return;
51
52 for (i = 0; i < nchannels; i++)
53 if (channel[i].addr == desc->addr)
54 break;
55
56 if (i < nchannels) {
57 channel[i].val = val;
58 return;
59 }
60
61 if (nchannels >= MAX_CHANNELS) {
62 warnx("too many channels");
63 return;
64 }
65 channel[i].addr = desc->addr;
66 channel[i].val = val;
67 nchannels++;
68 }
69
70 static void
71 onval(void *arg, unsigned int addr, unsigned int val)
72 {
73 size_t i;
74
75 for (i = 0; i < nchannels; i++)
76 if (channel[i].addr == addr) {
77 channel[i].val = val;
78 break;
79 }
80 }
81
82 static int
83 do_init(void)
84 {
85 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
86 if (hdl == NULL) {
87 warnx("sioctl_open %s", SIO_DEVANY);
88 return 0;
89 }
90 if (!sioctl_ondesc(hdl, ondesc, NULL)) {
91 warnx("sioctl_ondesc");
92 sioctl_close(hdl);
93 return 0;
94 }
95 sioctl_onval(hdl, onval, NULL);
96
97 return 1;
98 }
99
100 static int
101 poll_peek(void)
102 {
103 int nfds, revents;
104
105 if (pfds == NULL) {
106 pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
107 if (pfds == NULL) {
108 warnx("out of memory");
109 goto out;
110 }
111 }
112
113 nfds = sioctl_pollfd(hdl, pfds, POLLIN);
114 if (nfds == 0)
115 return 1;
116 while (poll(pfds, nfds, 0) == -1)
117 if (errno != EINTR) {
118 warn("sioctl poll");
119 goto out;
120 }
121 revents = sioctl_revents(hdl, pfds);
122 if (revents & POLLHUP) {
123 warnx("sioctl disconnected");
124 goto out;
125 }
126
127 return 1;
128
129 out:
130 free(pfds);
131 pfds = NULL;
132 sioctl_close(hdl);
133
134 return 0;
135 }
136
137 int
138 mixread(void *arg, char *buf, size_t len)
139 {
140 static int init_done = 0;
141 struct pollfd *pfds;
142 int nfds;
143
144 if (!init_done) {
145 if (!do_init())
146 return -1;
147 init_done = 1;
148 }
149
150 init_done = poll_peek();
151 snprintf(buf, len, "%d%%", get_output());
152
153 return 0;
154 }
155 #elif __linux__ && !USE_TINYALSA
156 #include <alsa/asoundlib.h>
157
158 static int active;
159 static int master;
160
161 int
162 mixer_elem_cb(snd_mixer_elem_t *elem, unsigned int mask)
163 {
164 long min, max, vol;
165 int r;
166
167 r = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_UNKNOWN, &active);
168 if (r < 0) {
169 warnx("snd_mixer_selem_get_playback_switch: %s",
170 snd_strerror(r));
171 return -1;
172 }
173 DPRINTF_D(active);
174 r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
175 if (r < 0) {
176 warnx("snd_mixer_selem_get_playback_volume_range: %s",
177 snd_strerror(r));
178 return -1;
179 }
180 r = snd_mixer_selem_get_playback_volume(elem,
181 SND_MIXER_SCHN_UNKNOWN, &vol);
182 if (r < 0) {
183 warnx("snd_mixer_selem_get_playback_volume: %s",
184 snd_strerror(r));
185 return -1;
186 }
187 /* compute percentage */
188 vol -= min;
189 max -= min;
190 if (max == 0)
191 master = 0;
192 else
193 master = 100 * vol / max;
194 DPRINTF_D(master);
195 return 0;
196 }
197
198 int
199 mixread(void *arg, char *buf, size_t len)
200 {
201 snd_mixer_selem_id_t *id;
202 snd_mixer_elem_t *elem;
203 static snd_mixer_t *mixerp;
204 struct pollfd pfd[1];
205 int r;
206
207 snd_mixer_selem_id_alloca(&id);
208 snd_mixer_selem_id_set_name(id, "Master");
209 snd_mixer_selem_id_set_index(id, 0);
210
211 if (mixerp != NULL)
212 goto readvol;
213
214 r = snd_mixer_open(&mixerp, O_RDONLY);
215 if (r < 0) {
216 warnx("snd_mixer_open: %s", snd_strerror(r));
217 return -1;
218 }
219 r = snd_mixer_attach(mixerp, "default");
220 if (r < 0) {
221 warnx("snd_mixer_attach: %s", snd_strerror(r));
222 goto out;
223 }
224 r = snd_mixer_selem_register(mixerp, NULL, NULL);
225 if (r < 0) {
226 warnx("snd_mixer_selem_register: %s", snd_strerror(r));
227 goto out;
228 }
229 r = snd_mixer_load(mixerp);
230 if (r < 0) {
231 warnx("snd_mixer_load: %s", snd_strerror(r));
232 goto out;
233 }
234 elem = snd_mixer_find_selem(mixerp, id);
235 if (elem == NULL) {
236 warnx("could not find mixer element");
237 goto out;
238 }
239 snd_mixer_elem_set_callback(elem, mixer_elem_cb);
240 /* force the callback the first time around */
241 r = mixer_elem_cb(elem, 0);
242 if (r < 0)
243 goto out;
244 readvol:
245 r = snd_mixer_poll_descriptors(mixerp, pfd, LEN(pfd));
246 if (r < 0) {
247 warnx("snd_mixer_poll_descriptors: %s", snd_strerror(r));
248 goto out;
249 }
250 r = snd_mixer_handle_events(mixerp);
251 if (r < 0) {
252 warnx("snd_mixer_handle_events: %s", snd_strerror(r));
253 goto out;
254 }
255 if (active)
256 snprintf(buf, len, "%d%%", master);
257 else
258 snprintf(buf, len, "!%d%%", master);
259 return 0;
260 out:
261 snd_mixer_free(mixerp);
262 snd_mixer_close(mixerp);
263 mixerp = NULL;
264 return -1;
265 }
266 #elif __linux__ && USE_TINYALSA
267 #include <tinyalsa/asoundlib.h>
268
269 int
270 mixread(void *arg, char *buf, size_t len)
271 {
272 static struct mixer *mixer;
273 struct mixer_ctl *ctl;
274 int cur, max;
275
276 if (mixer == NULL && (mixer = mixer_open(0)) == NULL) {
277 warnx("mixer_open() failed");
278 return -1;
279 }
280
281 if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Switch"))
282 == NULL) {
283 warnx("mixer_get_ctl_by_name() failed");
284 goto out;
285 }
286 if (!mixer_ctl_get_value(ctl, 0)) {
287 snprintf(buf, len, "mute");
288 return 0;
289 }
290
291 if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Volume"))
292 == NULL) {
293 warnx("mixer_get_ctl_by_name() failed");
294 goto out;
295 }
296
297 cur = mixer_ctl_get_value(ctl, 0);
298 max = mixer_ctl_get_range_max(ctl);
299 snprintf(buf, len, "%d%%", cur * 100 / max);
300 return 0;
301
302 out:
303 mixer_close(mixer);
304 mixer = NULL;
305 return -1;
306 }
307 #endif