suckless/slstatus

components/volume.c in master
Repositories | Summary | Log | Files | README | LICENSE

volume.c (4319B) download


  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