Hi,

thanks for the great work! These changes are upstream now.

Regards,
Aaron

On 20-05-09 Sat, Ingo Feinerer wrote:
Starting with OpenBSD 6.7 regular users cannot access raw audio devices
anymore, for improved security. Instead use the sioctl_open(3) API to
access and manipulate audio controls exposed by sndiod(8). On the first
call a permanent connection is established with the running sndiod
daemon, and call-back functions are registered which are triggered when
audio controls are changed (e.g., a USB headset is attached) or when the
volume is modified. On subsequent calls we poll for changes; if there
are no volume changes this costs virtually nothing.

Joint work with Alexandre Ratchov.
---
LICENSE             |   3 +-
components/volume.c | 210 +++++++++++++++++++++++++++++++++-----------
config.def.h        |   1 +
config.mk           |   1 +
4 files changed, 163 insertions(+), 52 deletions(-)

diff --git a/LICENSE b/LICENSE
index 0eec587..c61489f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -19,7 +19,8 @@ Copyright 2018 David Demelier <mark...@malikania.fr>
Copyright 2018-2019 Michael Buch <michaelbuc...@gmail.com>
Copyright 2018 Ian Remmler <i...@remmler.org>
Copyright 2016-2019 Joerg Jung <j...@openbsd.org>
-Copyright 2019 Ingo Feinerer <feine...@logic.at>
+Copyright 2019-2020 Ingo Feinerer <feine...@logic.at>
+Copyright 2020 Alexandre Ratchov <a...@caoua.org>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/components/volume.c b/components/volume.c
index 61cec90..b6665da 100644
--- a/components/volume.c
+++ b/components/volume.c
@@ -8,69 +8,177 @@
#include "../util.h"

#if defined(__OpenBSD__)
-       #include <sys/audioio.h>
+       #include <sys/queue.h>
+       #include <poll.h>
+       #include <sndio.h>
+       #include <stdlib.h>
+
+       struct control {
+               LIST_ENTRY(control)     next;
+               unsigned int            addr;
+       #define CTRL_NONE       0
+       #define CTRL_LEVEL      1
+       #define CTRL_MUTE       2
+               unsigned int            type;
+               unsigned int            maxval;
+               unsigned int            val;
+       };
+
+       static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls);
+       static struct pollfd *pfds;
+       static struct sioctl_hdl *hdl;
+       static int initialized;
+
+       /*
+        * Call-back to obtain the description of all audio controls.
+        */
+       static void
+       ondesc(void *unused, struct sioctl_desc *desc, int val)
+       {
+               struct control *c, *ctmp;
+               unsigned int type = CTRL_NONE;
+
+               if (desc == NULL)
+                       return;
+
+               /* Delete existing audio control with the same address. */
+               LIST_FOREACH_SAFE(c, &controls, next, ctmp) {
+                       if (desc->addr == c->addr) {
+                               LIST_REMOVE(c, next);
+                               free(c);
+                               break;
+                       }
+               }
+
+               /* Only match output.level and output.mute audio controls. */
+               if (desc->group[0] != 0 ||
+                   strcmp(desc->node0.name, "output") != 0)
+                       return;
+               if (desc->type == SIOCTL_NUM &&
+                   strcmp(desc->func, "level") == 0)
+                       type = CTRL_LEVEL;
+               else if (desc->type == SIOCTL_SW &&
+                        strcmp(desc->func, "mute") == 0)
+                       type = CTRL_MUTE;
+               else
+                       return;
+
+               c = malloc(sizeof(struct control));
+               if (c == NULL) {
+                       warn("sndio: failed to allocate audio control\n");
+                       return;
+               }
+
+               c->addr = desc->addr;
+               c->type = type;
+               c->maxval = desc->maxval;
+               c->val = val;
+               LIST_INSERT_HEAD(&controls, c, next);
+       }
+
+       /*
+        * Call-back invoked whenever an audio control changes.
+        */
+       static void
+       onval(void *unused, unsigned int addr, unsigned int val)
+       {
+               struct control *c;
+
+               LIST_FOREACH(c, &controls, next) {
+                       if (c->addr == addr)
+                               break;
+               }
+               c->val = val;
+       }
+
+       static void
+       cleanup(void)
+       {
+               struct control *c;
+
+               if (hdl) {
+                       sioctl_close(hdl);
+                       hdl = NULL;
+               }
+
+               free(pfds);
+               pfds = NULL;
+
+               while (!LIST_EMPTY(&controls)) {
+                       c = LIST_FIRST(&controls);
+                       LIST_REMOVE(c, next);
+                       free(c);
+               }
+       }
+
+       static int
+       init(void)
+       {
+               hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
+               if (hdl == NULL) {
+                       warn("sndio: cannot open device");
+                       goto failed;
+               }
+
+               if (!sioctl_ondesc(hdl, ondesc, NULL)) {
+                       warn("sndio: cannot set control description call-back");
+                       goto failed;
+               }
+
+               if (!sioctl_onval(hdl, onval, NULL)) {
+                       warn("sndio: cannot set control values call-back");
+                       goto failed;
+               }
+
+               pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
+               if (pfds == NULL) {
+                       warn("sndio: cannot allocate pollfd structures");
+                       goto failed;
+               }
+
+               return 1;
+       failed:
+               cleanup();
+               return 0;
+       }

        const char *
-       vol_perc(const char *card)
+       vol_perc(const char *unused)
        {
-               static int cls = -1;
-               mixer_devinfo_t mdi;
-               mixer_ctrl_t mc;
-               int afd = -1, m = -1, v = -1;
+               struct control *c;
+               int n, v, value;

-               if ((afd = open(card, O_RDONLY)) < 0) {
-                       warn("open '%s':", card);
+               if (!initialized)
+                       initialized = init();
+
+               if (hdl == NULL)
                        return NULL;
-               }

-               for (mdi.index = 0; cls == -1; mdi.index++) {
-                       if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) {
-                               warn("ioctl 'AUDIO_MIXER_DEVINFO':");
-                               close(afd);
-                               return NULL;
-                       }
-                       if (mdi.type == AUDIO_MIXER_CLASS &&
-                           !strncmp(mdi.label.name,
-                                    AudioCoutputs,
-                                    MAX_AUDIO_DEV_LEN))
-                               cls = mdi.index;
-                       }
-               for (mdi.index = 0; v == -1 || m == -1; mdi.index++) {
-                       if (ioctl(afd, AUDIO_MIXER_DEVINFO, &mdi) < 0) {
-                               warn("ioctl 'AUDIO_MIXER_DEVINFO':");
-                               close(afd);
-                               return NULL;
-                       }
-                       if (mdi.mixer_class == cls &&
-                           ((mdi.type == AUDIO_MIXER_VALUE &&
-                             !strncmp(mdi.label.name,
-                                      AudioNmaster,
-                                      MAX_AUDIO_DEV_LEN)) ||
-                            (mdi.type == AUDIO_MIXER_ENUM &&
-                             !strncmp(mdi.label.name,
-                                     AudioNmute,
-                                     MAX_AUDIO_DEV_LEN)))) {
-                               mc.dev = mdi.index, mc.type = mdi.type;
-                               if (ioctl(afd, AUDIO_MIXER_READ, &mc) < 0) {
-                                       warn("ioctl 'AUDIO_MIXER_READ':");
-                                       close(afd);
+               n = sioctl_pollfd(hdl, pfds, POLLIN);
+               if (n > 0) {
+                       n = poll(pfds, n, 0);
+                       if (n > 0) {
+                               if (sioctl_revents(hdl, pfds) & POLLHUP) {
+                                       warn("sndio: disconnected");
+                                       cleanup();
                                        return NULL;
                                }
-                               if (mc.type == AUDIO_MIXER_VALUE)
-                                       v = mc.un.value.num_channels == 1 ?
-                                           
mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] :
-                                           
(mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] >
-                                            
mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] ?
-                                            
mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] :
-                                            
mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]);
-                               else if (mc.type == AUDIO_MIXER_ENUM)
-                                       m = mc.un.ord;
                        }
                }

-               close(afd);
+               value = 100;
+               LIST_FOREACH(c, &controls, next) {
+                       if (c->type == CTRL_MUTE && c->val == 1)
+                               value = 0;
+                       else if (c->type == CTRL_LEVEL) {
+                               v = (c->val * 100 + c->maxval / 2) / c->maxval;
+                               /* For multiple channels return the minimum. */
+                               if (v < value)
+                                       value = v;
+                       }
+               }

-               return bprintf("%d", m ? 0 : v * 100 / 255);
+               return bprintf("%d", value);
        }
#else
        #include <sys/soundcard.h>
diff --git a/config.def.h b/config.def.h
index e06be66..8b392cf 100644
--- a/config.def.h
+++ b/config.def.h
@@ -58,6 +58,7 @@ static const char unknown_str[] = "n/a";
 * uptime              system uptime                   NULL
 * username            username of current user        NULL
 * vol_perc            OSS/ALSA volume in percent      mixer file (/dev/mixer)
+ *                                                     NULL on OpenBSD
 * wifi_perc           WiFi signal in percent          interface name (wlan0)
 * wifi_essid          WiFi ESSID                      interface name (wlan0)
 */
diff --git a/config.mk b/config.mk
index 3b32b7c..d88695c 100644
--- a/config.mk
+++ b/config.mk
@@ -14,6 +14,7 @@ X11LIB = /usr/X11R6/lib
CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE
CFLAGS   = -std=c99 -pedantic -Wall -Wextra -Os
LDFLAGS  = -L$(X11LIB) -s
+# OpenBSD: add -lsndio
LDLIBS   = -lX11

# compiler and linker
--
2.26.2



--
Web: https://drkhsh.at/ or http://drkhsh5rv6pnahas.onion/
Gopher: gopher://drkhsh.at or gopher://drkhsh5rv6pnahas.onion
GPG: 0x7A65E38D55BE96FE
Fingerprint: 4688 907C 8720 3318 0D9F AFDE 7A65 E38D 55BE 96FE

Reply via email to