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(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 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-back"); 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->maxval; 177 /* For multiple channels return the minimum. */ 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) < 0) { 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[i])) { 207 if (ioctl(afd, MIXER_READ(i), &v) < 0) { 208 warn("ioctl 'MIXER_READ(%ld)':", i); 209 close(afd); 210 return NULL; 211 } 212 } 213 } 214 215 close(afd); 216 217 return bprintf("%d", v & 0xff); 218 } 219 #endif