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