volume.c - slstatus - status monitor
 (HTM) git clone git://git.suckless.org/slstatus
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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