spoon

set dwm status
git clone git@git.2f30.org/spoon.git
Log | Files | Refs | LICENSE

mix.c (5735B)


      1 #include <err.h>
      2 #include <stdio.h>
      3 
      4 #include "util.h"
      5 
      6 #ifdef __OpenBSD__
      7 #include <errno.h>
      8 #include <poll.h>
      9 #include <sndio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 
     13 #define MAX_CHANNELS 16
     14 
     15 static struct sioctl_hdl *hdl = NULL;
     16 static struct pollfd *pfds = NULL;
     17 
     18 static struct {
     19 	unsigned int addr;
     20 	int val;
     21 } channel[MAX_CHANNELS];
     22 static size_t nchannels = 0;
     23 
     24 static int
     25 get_output(void)
     26 {
     27 	size_t i;
     28 	int val;
     29 
     30 	if (nchannels == 0)
     31 		return 0;
     32 
     33 	val = 0;
     34 	for (i = 0; i < nchannels; i++)
     35 		val += channel[i].val;
     36 	return 100 * ((val / (double)nchannels) / 255.0);
     37 }
     38 
     39 static void
     40 ondesc(void *arg, struct sioctl_desc *desc, int val)
     41 {
     42 	size_t i;
     43 
     44 	if (desc == NULL)
     45 		return;
     46 
     47 	if (desc->type != SIOCTL_NUM ||
     48 	    strcmp(desc->func, "level") != 0 ||
     49 	    strcmp(desc->node0.name, "output") != 0)
     50 		return;
     51 
     52 	for (i = 0; i < nchannels; i++)
     53 		if (channel[i].addr == desc->addr)
     54 			break;
     55 
     56 	if (i < nchannels) {
     57 		channel[i].val = val;
     58 		return;
     59 	}
     60 
     61 	if (nchannels >= MAX_CHANNELS) {
     62 		warnx("too many channels");
     63 		return;
     64 	}
     65 	channel[i].addr = desc->addr;
     66 	channel[i].val = val;
     67 	nchannels++;
     68 }
     69 
     70 static void
     71 onval(void *arg, unsigned int addr, unsigned int val)
     72 {
     73 	size_t i;
     74 
     75 	for (i = 0; i < nchannels; i++)
     76 		if (channel[i].addr == addr) {
     77 			channel[i].val = val;
     78 			break;
     79 		}
     80 }
     81 
     82 static int
     83 do_init(void)
     84 {
     85 	hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
     86 	if (hdl == NULL) {
     87 		warnx("sioctl_open %s", SIO_DEVANY);
     88 		return 0;
     89 	}
     90 	if (!sioctl_ondesc(hdl, ondesc, NULL)) {
     91 		warnx("sioctl_ondesc");
     92 		sioctl_close(hdl);
     93 		return 0;
     94 	}
     95 	sioctl_onval(hdl, onval, NULL);
     96 
     97 	return 1;
     98 }
     99 
    100 static int
    101 poll_peek(void)
    102 {
    103 	int nfds, revents;
    104 
    105 	if (pfds == NULL) {
    106 		pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
    107 		if (pfds == NULL) {
    108 			warnx("out of memory");
    109 			goto out;
    110 		}
    111 	}
    112 
    113 	nfds = sioctl_pollfd(hdl, pfds, POLLIN);
    114 	if (nfds == 0)
    115 		return 1;
    116 	while (poll(pfds, nfds, 0) == -1)
    117 		if (errno != EINTR) {
    118 			warn("sioctl poll");
    119 			goto out;
    120 		}
    121 	revents = sioctl_revents(hdl, pfds);
    122 	if (revents & POLLHUP) {
    123 		warnx("sioctl disconnected");
    124 		goto out;
    125 	}
    126 
    127 	return 1;
    128 
    129 out:
    130 	free(pfds);
    131 	pfds = NULL;
    132 	sioctl_close(hdl);
    133 
    134 	return 0;
    135 }
    136 
    137 int
    138 mixread(void *arg, char *buf, size_t len)
    139 {
    140 	static int init_done = 0;
    141 	struct pollfd *pfds;
    142 	int nfds;
    143 
    144 	if (!init_done) {
    145 		if (!do_init())
    146 			return -1;
    147 		init_done = 1;
    148 	}
    149 
    150 	init_done = poll_peek();
    151 	snprintf(buf, len, "%d%%", get_output());
    152 
    153 	return 0;
    154 }
    155 #elif __linux__ && !USE_TINYALSA
    156 #include <alsa/asoundlib.h>
    157 
    158 static int active;
    159 static int master;
    160 
    161 int
    162 mixer_elem_cb(snd_mixer_elem_t *elem, unsigned int mask)
    163 {
    164 	long min, max, vol;
    165 	int r;
    166 
    167 	r = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_UNKNOWN, &active);
    168 	if (r < 0) {
    169 		warnx("snd_mixer_selem_get_playback_switch: %s",
    170 		      snd_strerror(r));
    171 		return -1;
    172 	}
    173 	DPRINTF_D(active);
    174 	r = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
    175 	if (r < 0) {
    176 		warnx("snd_mixer_selem_get_playback_volume_range: %s",
    177 		      snd_strerror(r));
    178 		return -1;
    179 	}
    180 	r = snd_mixer_selem_get_playback_volume(elem,
    181 	    SND_MIXER_SCHN_UNKNOWN, &vol);
    182 	if (r < 0) {
    183 		warnx("snd_mixer_selem_get_playback_volume: %s",
    184 		      snd_strerror(r));
    185 		return -1;
    186 	}
    187 	/* compute percentage */
    188 	vol -= min;
    189 	max -= min;
    190 	if (max == 0)
    191 		master = 0;
    192 	else
    193 		master = 100 * vol / max;
    194 	DPRINTF_D(master);
    195 	return 0;
    196 }
    197 
    198 int
    199 mixread(void *arg, char *buf, size_t len)
    200 {
    201 	snd_mixer_selem_id_t *id;
    202 	snd_mixer_elem_t *elem;
    203 	static snd_mixer_t *mixerp;
    204 	struct pollfd pfd[1];
    205 	int r;
    206 
    207 	snd_mixer_selem_id_alloca(&id);
    208 	snd_mixer_selem_id_set_name(id, "Master");
    209 	snd_mixer_selem_id_set_index(id, 0);
    210 
    211 	if (mixerp != NULL)
    212 		goto readvol;
    213 
    214 	r = snd_mixer_open(&mixerp, O_RDONLY);
    215 	if (r < 0) {
    216 		warnx("snd_mixer_open: %s", snd_strerror(r));
    217 		return -1;
    218 	}
    219 	r = snd_mixer_attach(mixerp, "default");
    220 	if (r < 0) {
    221 		warnx("snd_mixer_attach: %s", snd_strerror(r));
    222 		goto out;
    223 	}
    224 	r = snd_mixer_selem_register(mixerp, NULL, NULL);
    225 	if (r < 0) {
    226 		warnx("snd_mixer_selem_register: %s", snd_strerror(r));
    227 		goto out;
    228 	}
    229 	r = snd_mixer_load(mixerp);
    230 	if (r < 0) {
    231 		warnx("snd_mixer_load: %s", snd_strerror(r));
    232 		goto out;
    233 	}
    234 	elem = snd_mixer_find_selem(mixerp, id);
    235 	if (elem == NULL) {
    236 		warnx("could not find mixer element");
    237 		goto out;
    238 	}
    239 	snd_mixer_elem_set_callback(elem, mixer_elem_cb);
    240 	/* force the callback the first time around */
    241 	r = mixer_elem_cb(elem, 0);
    242 	if (r < 0)
    243 		goto out;
    244 readvol:
    245 	r = snd_mixer_poll_descriptors(mixerp, pfd, LEN(pfd));
    246 	if (r < 0) {
    247 		warnx("snd_mixer_poll_descriptors: %s", snd_strerror(r));
    248 		goto out;
    249 	}
    250 	r = snd_mixer_handle_events(mixerp);
    251 	if (r < 0) {
    252 		warnx("snd_mixer_handle_events: %s", snd_strerror(r));
    253 		goto out;
    254 	}
    255 	if (active)
    256 		snprintf(buf, len, "%d%%", master);
    257 	else
    258 		snprintf(buf, len, "!%d%%", master);
    259 	return 0;
    260 out:
    261 	snd_mixer_free(mixerp);
    262 	snd_mixer_close(mixerp);
    263 	mixerp = NULL;
    264 	return -1;
    265 }
    266 #elif __linux__ && USE_TINYALSA
    267 #include <tinyalsa/asoundlib.h>
    268 
    269 int
    270 mixread(void *arg, char *buf, size_t len)
    271 {
    272 	static struct mixer	*mixer;
    273 	struct mixer_ctl	*ctl;
    274 	int			 cur, max;
    275 
    276 	if (mixer == NULL && (mixer = mixer_open(0)) == NULL) {
    277 		warnx("mixer_open() failed");
    278 		return -1;
    279 	}
    280 
    281 	if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Switch"))
    282 	    == NULL) {
    283 		warnx("mixer_get_ctl_by_name() failed");
    284 		goto out;
    285 	}
    286 	if (!mixer_ctl_get_value(ctl, 0)) {
    287 		snprintf(buf, len, "mute");
    288 		return 0;
    289 	}
    290 
    291 	if ((ctl = mixer_get_ctl_by_name(mixer, "Master Playback Volume"))
    292 	    == NULL) {
    293 		warnx("mixer_get_ctl_by_name() failed");
    294 		goto out;
    295 	}
    296 
    297 	cur = mixer_ctl_get_value(ctl, 0);
    298 	max = mixer_ctl_get_range_max(ctl);
    299 	snprintf(buf, len, "%d%%", cur * 100 / max);
    300 	return 0;
    301 
    302 out:
    303 	mixer_close(mixer);
    304 	mixer = NULL;
    305 	return -1;
    306 }
    307 #endif