This makes i3status display sndiod master volume knob. This makes it
work by default with any audio device, including USB ones with no
volume controls.

OK?

Index: Makefile
===================================================================
RCS file: /cvs/ports/x11/i3status/Makefile,v
retrieving revision 1.58
diff -u -p -r1.58 Makefile
--- Makefile    27 Sep 2019 20:33:22 -0000      1.58
+++ Makefile    8 Mar 2020 15:33:22 -0000
@@ -5,7 +5,7 @@ ONLY_FOR_ARCHS=         ${APM_ARCHS}
 COMMENT=               generate a statusbar for use with i3/xmobar/dzen2
 
 DISTNAME=              i3status-2.13
-REVISION=              1
+REVISION=              2
 CATEGORIES=            x11 sysutils
 
 HOMEPAGE=              https://i3wm.org/i3status/
@@ -28,7 +28,9 @@ BUILD_DEPENDS=                textproc/asciidoc>=8.6.8
 LIB_DEPENDS=           devel/libconfuse \
                        devel/libyajl
 
-CONFIGURE_STYLE =      gnu
+AUTOCONF_VERSION =     2.69
+AUTOMAKE_VERSION =     1.16
+CONFIGURE_STYLE =      autoreconf
 SEPARATE_BUILD =       Yes
 
 FAKE_FLAGS +=          sysconfdir=${PREFIX}/share/examples/i3status/
Index: patches/patch-Makefile_am
===================================================================
RCS file: patches/patch-Makefile_am
diff -N patches/patch-Makefile_am
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-Makefile_am   8 Mar 2020 15:33:22 -0000
@@ -0,0 +1,37 @@
+$OpenBSD$
+
+Index: Makefile.am
+--- Makefile.am.orig
++++ Makefile.am
+@@ -1,5 +1,3 @@
+-@CODE_COVERAGE_RULES@
+-
+ echo-version:
+       @echo "@I3STATUS_VERSION@"
+ 
+@@ -30,6 +28,7 @@ i3status_CFLAGS = \
+       $(PULSE_CFLAGS) \
+       $(NLGENL_CFLAGS) \
+       $(ALSA_CFLAGS) \
++      $(SNDIO_CFLAGS) \
+       $(PTHREAD_CFLAGS)
+ 
+ i3status_CPPFLAGS = \
+@@ -42,6 +41,7 @@ i3status_LDADD = \
+       $(PULSE_LIBS) \
+       $(NLGENL_LIBS) \
+       $(ALSA_LIBS) \
++      $(SNDIO_LIBS) \
+       $(PTHREAD_LIBS)
+ 
+ i3status_SOURCES = \
+@@ -69,7 +69,8 @@ i3status_SOURCES = \
+       src/print_wireless_info.c \
+       src/print_file_contents.c \
+       src/process_runs.c \
+-      src/pulse.c
++      src/pulse.c \
++      src/sndio.c
+ 
+ dist_sysconf_DATA = \
+       i3status.conf
Index: patches/patch-Makefile_in
===================================================================
RCS file: patches/patch-Makefile_in
diff -N patches/patch-Makefile_in
--- patches/patch-Makefile_in   6 Jul 2019 20:20:27 -0000       1.1
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,15 +0,0 @@
-$OpenBSD: patch-Makefile_in,v 1.1 2019/07/06 20:20:27 jasper Exp $
-
-The CODE_COVERAGE_RULES fragment contains an unmatched "if" clause.
-
-Index: Makefile.in
---- Makefile.in.orig
-+++ Makefile.in
-@@ -1851,7 +1851,6 @@ uninstall-man: uninstall-man1
- 
- .PRECIOUS: Makefile
- 
--@CODE_COVERAGE_RULES@
- 
- echo-version:
-       @echo "@I3STATUS_VERSION@"
Index: patches/patch-configure_ac
===================================================================
RCS file: patches/patch-configure_ac
diff -N patches/patch-configure_ac
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-configure_ac  8 Mar 2020 15:33:22 -0000
@@ -0,0 +1,19 @@
+$OpenBSD$
+
+Index: configure.ac
+--- configure.ac.orig
++++ configure.ac
+@@ -91,6 +91,13 @@ case $host_os in
+       ;;
+ esac
+ 
++# if sndio is available, define USE_SNDIO
++AC_CHECK_HEADER(sndio.h,
++      [AC_CHECK_LIB([sndio], [sio_open], [
++              AC_SUBST(SNDIO_LIBS, "-lsndio")
++              AC_DEFINE([USE_SNDIO], [], [Use sndio])
++      ], [])], [])
++
+ dnl TODO: check for libbsd for GNU/kFreeBSD
+ 
+ # Checks for programs.
Index: patches/patch-include_i3status_h
===================================================================
RCS file: patches/patch-include_i3status_h
diff -N patches/patch-include_i3status_h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-include_i3status_h    8 Mar 2020 15:33:22 -0000
@@ -0,0 +1,13 @@
+$OpenBSD$
+
+Index: include/i3status.h
+--- include/i3status.h.orig
++++ include/i3status.h
+@@ -232,6 +232,7 @@ int volume_pulseaudio(uint32_t sink_idx, const char *s
+ bool description_pulseaudio(uint32_t sink_idx, const char *sink_name, char 
buffer[MAX_SINK_DESCRIPTION_LEN]);
+ bool pulse_initialize(void);
+ void print_file_contents(yajl_gen json_gen, char *buffer, const char *title, 
const char *path, const char *format, const char *format_bad, const int 
max_chars);
++int volume_sndio(void);
+ 
+ /* socket file descriptor for general purposes */
+ extern int general_socket;
Index: patches/patch-src_print_volume_c
===================================================================
RCS file: patches/patch-src_print_volume_c
diff -N patches/patch-src_print_volume_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_print_volume_c    8 Mar 2020 15:33:22 -0000
@@ -0,0 +1,144 @@
+$OpenBSD$
+
+Index: src/print_volume.c
+--- src/print_volume.c.orig
++++ src/print_volume.c
+@@ -21,13 +21,6 @@
+ #include <sys/soundcard.h>
+ #endif
+ 
+-#ifdef __OpenBSD__
+-#include <fcntl.h>
+-#include <unistd.h>
+-#include <sys/audioio.h>
+-#include <sys/ioctl.h>
+-#endif
+-
+ #include "i3status.h"
+ #include "queue.h"
+ 
+@@ -145,9 +138,20 @@ void print_volume(yajl_gen json_gen, char *buffer, con
+         /* negative result or NULL description means error, fail PulseAudio 
attempt */
+     }
+ /* If some other device was specified or PulseAudio is not detected,
+- * proceed to ALSA / OSS */
++ * proceed to sndio / ALSA / OSS */
+ #endif
+ 
++#ifdef USE_SNDIO
++    int vol;
++    
++      vol = volume_sndio();
++      if (vol != -1) {
++        outwalk = apply_volume_format(fmt, outwalk, vol & 0x7f, "sndio");
++        goto out;
++    }
++/* If sndio is not detected, proceed to ALSA / OSS */
++#endif
++
+ #ifdef __linux__
+     const long MAX_LINEAR_DB_SCALE = 24;
+     int err;
+@@ -248,7 +252,8 @@ void print_volume(yajl_gen json_gen, char *buffer, con
+     snd_mixer_selem_id_free(sid);
+ 
+ #endif
+-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
++
++#if defined(__FreeBSD__) || defined(__DragonFly__)
+     char *mixerpath;
+     char defaultmixer[] = "/dev/mixer";
+     int mixfd, vol, devmask = 0;
+@@ -261,84 +266,13 @@ void print_volume(yajl_gen json_gen, char *buffer, con
+         mixerpath = defaultmixer;
+ 
+     if ((mixfd = open(mixerpath, O_RDWR)) < 0) {
+-#if defined(__OpenBSD__)
+-        warn("audioio: Cannot open mixer");
+-#else
+         warn("OSS: Cannot open mixer");
+-#endif
+         goto out;
+     }
+ 
+     if (mixer_idx > 0)
+         free(mixerpath);
+ 
+-#if defined(__OpenBSD__)
+-    int oclass_idx = -1, master_idx = -1, master_mute_idx = -1;
+-    int master_next = AUDIO_MIXER_LAST;
+-    mixer_devinfo_t devinfo, devinfo2;
+-    mixer_ctrl_t vinfo;
+-
+-    devinfo.index = 0;
+-    while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo) >= 0) {
+-        if (devinfo.type != AUDIO_MIXER_CLASS) {
+-            devinfo.index++;
+-            continue;
+-        }
+-        if (strncmp(devinfo.label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN) == 
0)
+-            oclass_idx = devinfo.index;
+-
+-        devinfo.index++;
+-    }
+-
+-    devinfo2.index = 0;
+-    while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo2) >= 0) {
+-        if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == 
oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) 
== 0)) {
+-            master_idx = devinfo2.index;
+-            master_next = devinfo2.next;
+-        }
+-
+-        if ((devinfo2.type == AUDIO_MIXER_ENUM) && (devinfo2.mixer_class == 
oclass_idx) && (strncmp(devinfo2.label.name, AudioNmute, MAX_AUDIO_DEV_LEN) == 
0))
+-            if (master_next == devinfo2.index)
+-                master_mute_idx = devinfo2.index;
+-
+-        if (master_next != AUDIO_MIXER_LAST)
+-            master_next = devinfo2.next;
+-        devinfo2.index++;
+-    }
+-
+-    if (master_idx == -1)
+-        goto out;
+-
+-    devinfo.index = master_idx;
+-    if (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo) == -1)
+-        goto out;
+-
+-    vinfo.dev = master_idx;
+-    vinfo.type = AUDIO_MIXER_VALUE;
+-    vinfo.un.value.num_channels = devinfo.un.v.num_channels;
+-    if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1)
+-        goto out;
+-
+-    if (AUDIO_MAX_GAIN != 100) {
+-        float avgf = ((float)vinfo.un.value.level[AUDIO_MIXER_LEVEL_MONO] / 
AUDIO_MAX_GAIN) * 100;
+-        vol = (int)avgf;
+-        vol = (avgf - vol < 0.5 ? vol : (vol + 1));
+-    } else {
+-        vol = (int)vinfo.un.value.level[AUDIO_MIXER_LEVEL_MONO];
+-    }
+-
+-    vinfo.dev = master_mute_idx;
+-    vinfo.type = AUDIO_MIXER_ENUM;
+-    if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1)
+-        goto out;
+-
+-    if (master_mute_idx != -1 && vinfo.un.ord) {
+-        START_COLOR("color_degraded");
+-        fmt = fmt_muted;
+-        pbval = 0;
+-    }
+-
+-#else
+     if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
+         warn("OSS: Cannot read mixer information");
+         goto out;
+@@ -353,7 +287,6 @@ void print_volume(yajl_gen json_gen, char *buffer, con
+         pbval = 0;
+     }
+ 
+-#endif
+     outwalk = apply_volume_format(fmt, outwalk, vol & 0x7f, devicename);
+     close(mixfd);
+ #endif
Index: patches/patch-src_sndio_c
===================================================================
RCS file: patches/patch-src_sndio_c
diff -N patches/patch-src_sndio_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_sndio_c   8 Mar 2020 15:33:22 -0000
@@ -0,0 +1,176 @@
+$OpenBSD$
+
+Add sndio volume backend.
+
+Index: src/sndio.c
+--- src/sndio.c.orig
++++ src/sndio.c
+@@ -0,0 +1,168 @@
++#include <poll.h>
++#include <sndio.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include "i3status.h"
++
++struct control {
++    struct control *next;
++    unsigned int addr;
++    unsigned int max;
++    unsigned int value;
++};
++
++static int initialized;
++static struct sioctl_hdl *hdl;
++static struct control *controls;
++static struct pollfd *pfds;
++
++/*
++ * new control registered or control changed
++ */
++static void ondesc(void *unused, struct sioctl_desc *d, int val)
++{
++    struct control *i, **pi;
++
++    if (d == NULL)
++        return;
++
++    /*
++     * delete existing control with the same address
++     */
++    for (pi = &controls; (i = *pi) != NULL; pi = &i->next) {
++        if (d->addr == i->addr) {
++            *pi = i->next;
++            free(i);
++            break;
++        }
++    }
++
++    /*
++     * we're interested in top-level output.level controls only
++     */
++    if (d->type != SIOCTL_NUM ||
++        d->group[0] != 0 ||
++        strcmp(d->node0.name, "output") != 0 ||
++        strcmp(d->func, "level") != 0)
++        return;
++
++    i = malloc(sizeof(struct control));
++    if (i == NULL) {
++        fprintf(stderr, "sndio: failed to allocate control\n");
++        return;
++    }
++
++    i->addr = d->addr;
++    i->max = d->maxval;
++    i->value = val;
++    i->next = controls;
++    controls = i;
++}
++
++/*
++ * control value changed
++ */
++static void onval(void *unused, unsigned int addr, unsigned int value)
++{
++    struct control *c;
++
++    for (c = controls; ; c = c->next) {
++        if (c == NULL)
++            return;
++        if (c->addr == addr)
++            break;
++    }
++
++    c->value = value;
++}
++
++static void cleanup(void)
++{
++    struct control *c;
++
++    if (hdl) {
++        sioctl_close(hdl);
++        hdl = NULL;
++    }
++    if (pfds) {
++        free(pfds);
++        pfds = NULL;
++    }
++    while ((c = controls) != NULL) {
++        controls = c->next;
++        free(c);
++    }
++}
++
++static int init(void)
++{
++    /* open device */
++    hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
++    if (hdl == NULL) {
++        fprintf(stderr, "sndio: cannot open device\n");
++        goto failed;
++    }
++
++    /* register call-back for control description changes */
++    if (!sioctl_ondesc(hdl, ondesc, NULL)) {
++        fprintf(stderr, "sndio: cannot get description\n");
++        goto failed;
++    }
++
++    /* register call-back for volume changes */
++    if (!sioctl_onval(hdl, onval, NULL)) {
++        fprintf(stderr, "sndio: cannot get values\n");
++        goto failed;
++    }
++
++    /* allocate structures for poll() syscall */
++    pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
++    if (pfds == NULL) {
++        fprintf(stderr, "sndio: cannot allocate pollfd structures\n");
++        goto failed;
++    }
++    return 1;
++failed:
++    cleanup();
++    return 0;
++}
++
++int volume_sndio(void)
++{
++    struct control *c;
++    int n, v, value;
++
++    if (!initialized) {
++        initialized = 1;
++        init();
++    }
++    if (hdl == NULL)
++        return -1;
++
++    /* check if controls changed */
++    n = sioctl_pollfd(hdl, pfds, POLLIN);
++    if (n > 0) {
++        n = poll(pfds, n, 0);
++        if (n > 0) {
++            if (sioctl_revents(hdl, pfds) & POLLHUP) {
++                fprintf(stderr, "sndio: disconnected\n");
++                cleanup();
++                return -1;
++            }
++        }
++    }
++
++    /*
++     * get control value: as there may be multiple
++     * channels, return the minimum
++     */
++    value = 100;
++    for (c = controls; c != NULL; c = c->next) {
++        v = (c->value * 100 + c->max / 2) / c->max;
++        if (v < value)
++            value = v;
++    }
++
++    return value;
++}

Reply via email to