volume.c - slstatus - status monitor
HTML git clone git://git.suckless.org/slstatus
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
volume.c (4347B)
---
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(controls);
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 controls. */
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 if (c == NULL)
93 return;
94 c->val = val;
95 }
96
97 static void
98 cleanup(void)
99 {
100 struct control *c;
101
102 if (hdl) {
103 sioctl_close(hdl);
104 hdl = NULL;
105 }
106
107 free(pfds);
108 pfds = NULL;
109
110 while (!LIST_EMPTY(&controls)) {
111 c = LIST_FIRST(&controls);
112 LIST_REMOVE(c, next);
113 free(c);
114 }
115 }
116
117 static int
118 init(void)
119 {
120 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
121 if (hdl == NULL) {
122 warn("sndio: cannot open device");
123 goto failed;
124 }
125
126 if (!sioctl_ondesc(hdl, ondesc, NULL)) {
127 warn("sndio: cannot set control description call-back");
128 goto failed;
129 }
130
131 if (!sioctl_onval(hdl, onval, NULL)) {
132 warn("sndio: cannot set control values call-back");
133 goto failed;
134 }
135
136 pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
137 if (pfds == NULL) {
138 warn("sndio: cannot allocate pollfd structures");
139 goto failed;
140 }
141
142 return 1;
143 failed:
144 cleanup();
145 return 0;
146 }
147
148 const char *
149 vol_perc(const char *unused)
150 {
151 struct control *c;
152 int n, v, value;
153
154 if (!initialized)
155 initialized = init();
156
157 if (hdl == NULL)
158 return NULL;
159
160 n = sioctl_pollfd(hdl, pfds, POLLIN);
161 if (n > 0) {
162 n = poll(pfds, n, 0);
163 if (n > 0) {
164 if (sioctl_revents(hdl, pfds) & POLLHUP) {
165 warn("sndio: disconnected");
166 cleanup();
167 initialized = 0;
168 return NULL;
169 }
170 }
171 }
172
173 value = 100;
174 LIST_FOREACH(c, &controls, next) {
175 if (c->type == CTRL_MUTE && c->val == 1)
176 value = 0;
177 else if (c->type == CTRL_LEVEL) {
178 v = (c->val * 100 + c->maxval / 2) / c->maxval;
179 /* For multiple channels return the minimum. */
180 if (v < value)
181 value = v;
182 }
183 }
184
185 return bprintf("%d", value);
186 }
187 #else
188 #include <sys/soundcard.h>
189
190 const char *
191 vol_perc(const char *card)
192 {
193 size_t i;
194 int v, afd, devmask;
195 char *vnames[] = SOUND_DEVICE_NAMES;
196
197 if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) {
198 warn("open '%s':", card);
199 return NULL;
200 }
201
202 if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
203 warn("ioctl 'SOUND_MIXER_READ_DEVMASK':");
204 close(afd);
205 return NULL;
206 }
207 for (i = 0; i < LEN(vnames); i++) {
208 if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
209 if (ioctl(afd, MIXER_READ(i), &v) < 0) {
210 warn("ioctl 'MIXER_READ(%ld)':", i);
211 close(afd);
212 return NULL;
213 }
214 }
215 }
216
217 close(afd);
218
219 return bprintf("%d", v & 0xff);
220 }
221 #endif