URI:
       volume.c - slstatus - status monitor
  HTML git clone git://git.suckless.org/slstatus
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       volume.c (4347B)
       ---
            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                 if (c == NULL)
           93                         return;
           94                 c->val = val;
           95         }
           96 
           97         static void
           98         cleanup(void)
           99         {
          100                 struct control *c;
          101 
          102                 if (hdl) {
          103                         sioctl_close(hdl);
          104                         hdl = NULL;
          105                 }
          106 
          107                 free(pfds);
          108                 pfds = NULL;
          109 
          110                 while (!LIST_EMPTY(&controls)) {
          111                         c = LIST_FIRST(&controls);
          112                         LIST_REMOVE(c, next);
          113                         free(c);
          114                 }
          115         }
          116 
          117         static int
          118         init(void)
          119         {
          120                 hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
          121                 if (hdl == NULL) {
          122                         warn("sndio: cannot open device");
          123                         goto failed;
          124                 }
          125 
          126                 if (!sioctl_ondesc(hdl, ondesc, NULL)) {
          127                         warn("sndio: cannot set control description call-back");
          128                         goto failed;
          129                 }
          130 
          131                 if (!sioctl_onval(hdl, onval, NULL)) {
          132                         warn("sndio: cannot set control values call-back");
          133                         goto failed;
          134                 }
          135 
          136                 pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
          137                 if (pfds == NULL) {
          138                         warn("sndio: cannot allocate pollfd structures");
          139                         goto failed;
          140                 }
          141 
          142                 return 1;
          143         failed:
          144                 cleanup();
          145                 return 0;
          146         }
          147 
          148         const char *
          149         vol_perc(const char *unused)
          150         {
          151                 struct control *c;
          152                 int n, v, value;
          153 
          154                 if (!initialized)
          155                         initialized = init();
          156 
          157                 if (hdl == NULL)
          158                         return NULL;
          159 
          160                 n = sioctl_pollfd(hdl, pfds, POLLIN);
          161                 if (n > 0) {
          162                         n = poll(pfds, n, 0);
          163                         if (n > 0) {
          164                                 if (sioctl_revents(hdl, pfds) & POLLHUP) {
          165                                         warn("sndio: disconnected");
          166                                         cleanup();
          167                                         initialized = 0;
          168                                         return NULL;
          169                                 }
          170                         }
          171                 }
          172 
          173                 value = 100;
          174                 LIST_FOREACH(c, &controls, next) {
          175                         if (c->type == CTRL_MUTE && c->val == 1)
          176                                 value = 0;
          177                         else if (c->type == CTRL_LEVEL) {
          178                                 v = (c->val * 100 + c->maxval / 2) / c->maxval;
          179                                 /* For multiple channels return the minimum. */
          180                                 if (v < value)
          181                                         value = v;
          182                         }
          183                 }
          184 
          185                 return bprintf("%d", value);
          186         }
          187 #else
          188         #include <sys/soundcard.h>
          189 
          190         const char *
          191         vol_perc(const char *card)
          192         {
          193                 size_t i;
          194                 int v, afd, devmask;
          195                 char *vnames[] = SOUND_DEVICE_NAMES;
          196 
          197                 if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) {
          198                         warn("open '%s':", card);
          199                         return NULL;
          200                 }
          201 
          202                 if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
          203                         warn("ioctl 'SOUND_MIXER_READ_DEVMASK':");
          204                         close(afd);
          205                         return NULL;
          206                 }
          207                 for (i = 0; i < LEN(vnames); i++) {
          208                         if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
          209                                 if (ioctl(afd, MIXER_READ(i), &v) < 0) {
          210                                         warn("ioctl 'MIXER_READ(%ld)':", i);
          211                                         close(afd);
          212                                         return NULL;
          213                                 }
          214                         }
          215                 }
          216 
          217                 close(afd);
          218 
          219                 return bprintf("%d", v & 0xff);
          220         }
          221 #endif