mix.c - spoon - set dwm status | |
git clone git://git.codemadness.org/spoon | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
mix.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_UNK… | |
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 |