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; ++}