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