On Sun Mar 08, 2020 at 04:38:31PM +0100, Alexandre Ratchov wrote:
> 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?

OK rsadowski@, works fine here and helps me for my daily use.

> 
> 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