slstatus

status monitor
git clone git://git.suckless.org/slstatus
Log | Files | Refs | README | 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