tmix.c - spoon - [fork] customized build of spoon, the dwm status utility
 (HTM) git clone git://src.adamsgaard.dk/spoon
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       tmix.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