slstatus

status monitor
git clone git://git.suckless.org/slstatus
Log | Files | Refs | README | LICENSE

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