On Tue, Feb 25, 2020 at 01:02:06PM +0100, Alexandre Ratchov wrote:
> On Thu, Feb 13, 2020 at 04:52:12AM +0000, Raf Czlonka wrote:
> > 
> > Hi Alexandre,
> > 
> > I have to say that I also find the two ranges mildly confusing,
> > i.e. 0-255 in one place, and 0-127 in another. In terms of units,
> > personally, I'm used to, and quite like, the granularity of 0-255.
> > 
> > Again, not my place so others will certainly be more help here.
> > 
> > One more point regarding the interface, though.
> > 
> > This is the way mixerctl(1) currently behaves:
> > 
> >     $ mixerctl outputs.master     
> >     outputs.master=255,255
> >     $ mixerctl outputs.master=100 
> >     outputs.master: 255,255 -> 100,100
> >     $ mixerctl outputs.master=300 
> >     outputs.master: 100,100 -> 255,255
> > 
> > Should sndioctl(1) behave the same way?
> 
> Many thanks for the feedback.
> 
> After some thinking and experimenting, floats in the [0:1] seem the
> simplest option. It avoids discussions about preferences and allows
> arbitrary precision (if needed, later).
> 
> Furthermore, as all controls are in the [0:1] range, it makes sense to
> request the user to provide numbers between 0 and 1. Providing numbers
> outside this range indicates he is misunderstanding how the program
> works.
> 
> Below are all 3 base diffs combined (libsndio, sndiod, sndioctl,
> libossaudio), to ease testing.

And here's the ports part of the diff including audio/gqmpeg,
sysutils/tray-app and x11/i3status ports.

Index: sysutils/tray-app/Makefile
===================================================================
RCS file: /cvs/ports/sysutils/tray-app/Makefile,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 Makefile
--- sysutils/tray-app/Makefile  12 Jul 2019 20:49:53 -0000      1.8
+++ sysutils/tray-app/Makefile  22 Feb 2020 16:06:19 -0000
@@ -5,7 +5,7 @@ ONLY_FOR_ARCHS= ${APM_ARCHS}
 COMMENT=       small utilities for X11 system tray: eject, battery, mixer
 
 DISTNAME=      tray-app-0.3.1
-REVISION=      0
+REVISION=      1
 
 CATEGORIES=    sysutils x11
 
Index: sysutils/tray-app/patches/patch-sound_Makefile
===================================================================
RCS file: /cvs/ports/sysutils/tray-app/patches/patch-sound_Makefile,v
retrieving revision 1.1.1.1
diff -u -p -u -p -r1.1.1.1 patch-sound_Makefile
--- sysutils/tray-app/patches/patch-sound_Makefile      17 Sep 2013 11:21:50 
-0000      1.1.1.1
+++ sysutils/tray-app/patches/patch-sound_Makefile      22 Feb 2020 16:06:19 
-0000
@@ -1,6 +1,7 @@
 $OpenBSD: patch-sound_Makefile,v 1.1.1.1 2013/09/17 11:21:50 sthen Exp $
---- sound/Makefile.orig        Mon Mar 12 08:46:04 2012
-+++ sound/Makefile     Tue Sep 17 11:39:18 2013
+Index: sound/Makefile
+--- sound/Makefile.orig
++++ sound/Makefile
 @@ -8,13 +8,13 @@ MAN=
  
  gtk_CFLAGS!= pkg-config --cflags gtk+-2.0
@@ -8,7 +9,8 @@ $OpenBSD: patch-sound_Makefile,v 1.1.1.1
 -CFLAGS= -W -Wall -g -O0 -I../lib $(gtk_CFLAGS)
 +CFLAGS+= -W -Wall -I../lib $(gtk_CFLAGS)
  LDFLAGS= -L../lib $(gtk_LDFLAGS)
- LDADD= -ltrayapp
+-LDADD= -ltrayapp
++LDADD= -ltrayapp -lsndio
  
 -BINDIR=/usr/local/libexec/tray-app
 +BINDIR=${TRUEPREFIX}/libexec/tray-app
Index: sysutils/tray-app/patches/patch-sound_sound_c
===================================================================
RCS file: sysutils/tray-app/patches/patch-sound_sound_c
diff -N sysutils/tray-app/patches/patch-sound_sound_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sysutils/tray-app/patches/patch-sound_sound_c       22 Feb 2020 16:06:19 
-0000
@@ -0,0 +1,516 @@
+$OpenBSD$
+
+Index: sound/sound.c
+--- sound/sound.c.orig
++++ sound/sound.c
+@@ -4,12 +4,13 @@
+  */
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+-#include <sys/audioio.h>
++#include <sndio.h>
+ 
+ #include <gtk/gtk.h>
+ 
+ #include <err.h>
+ #include <fcntl.h>
++#include <poll.h>
+ #include <stdlib.h>
+ #include <string.h>
+ 
+@@ -19,16 +20,20 @@
+ #include "sound-1.xpm"
+ #include "sound-2.xpm"
+ 
++struct control {
++      struct control *next;
++      unsigned int addr;
++      unsigned int value;
++      unsigned int max;
++      int ismute;
++};
++
+ static void   usage(const char *prog);
+-static int    get_mixer_index(int fd, mixer_devinfo_t *devinfo);
+-static int    get_volume(int fd, mixer_devinfo_t *devinfo, u_char *volume);
+-static int    set_volume(int fd, mixer_devinfo_t *devinfo, u_char volume);
+-static int    get_mute(int fd, mixer_devinfo_t *devinfo, int *mute);
+-static int    set_mute(int fd, mixer_devinfo_t *devinfo, int mute);
++static void   set_state(int ismute, int mute);
++static void   get_state(int *rvolume, int *rmute);
+ 
+ static void   prepare_tooltip(int mute, u_char volume, char *text, size_t sz);
+ 
+-static gboolean       cb_timer(GtkWidget *widget);
+ static void   cb_button_toggled(GtkWidget *widget, gpointer data);
+ static void   cb_scale_value_changed(GtkScale *scale, GtkAdjustment *adj);
+ static gboolean       cb_window_delete(GtkWidget *widget,GdkEvent *event,
+@@ -48,8 +53,8 @@ static void  hide_gui(void);
+ static int    init_tray(int doinvert);
+ static void   set_tray_icon(u_char volume);
+ 
+-static int            mixer_fd;
+-static mixer_devinfo_t        outputs_master_dev[2];  /* volume, mute */
++static struct sioctl_hdl *hdl;
++static struct control    *controls;
+ 
+ static GtkWidget      *gui_window = NULL;
+ static GtkObject      *gui_adj = NULL;
+@@ -63,9 +68,6 @@ static GdkPixbuf     *tray_pixbuf_0 = NULL;
+ static GdkPixbuf      *tray_pixbuf_1 = NULL;
+ static GdkPixbuf      *tray_pixbuf_2 = NULL;
+ 
+-static u_char         current_volume = 0;
+-static int            current_mute = 0;
+-
+ void
+ usage(const char *prog)
+ {
+@@ -76,104 +78,136 @@ usage(const char *prog)
+           prog);
+ }
+ 
+-static int
+-get_mixer_index(int fd, mixer_devinfo_t *devinfo)
++/*
++ * new control registered
++ */
++static void
++cb_control_desc(void *unused, struct sioctl_desc *d, int val)
+ {
+-      int error;
+-      int i, outputs_idx;
++      struct control *i, **pi;
++      int ismute;
+ 
+-      i = 0;
+-      outputs_idx = -1;
+-      devinfo[0].index = 0;
+-      for (;;) {
+-              error = ioctl(fd, AUDIO_MIXER_DEVINFO, devinfo + i);
+-              if (error == -1)
++      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;
++              }
++      }
+ 
+-              if (i == 0 && devinfo[0].type == AUDIO_MIXER_CLASS &&
+-                  strcmp(devinfo[0].label.name, "outputs") == 0)
+-                      outputs_idx = devinfo[0].index;
+-              else if (i == 0 && devinfo[0].type == AUDIO_MIXER_VALUE &&
+-                  strcmp(devinfo[0].label.name, "master") == 0 &&
+-                  outputs_idx != -1 &&
+-                  devinfo[0].mixer_class == outputs_idx) {
+-                      devinfo[1].index = devinfo[0].index;
+-                      i++;
+-              } else if (i == 1 && devinfo[1].prev == devinfo[0].index &&
+-                  devinfo[1].type == AUDIO_MIXER_ENUM &&
+-                  strcmp(devinfo[1].label.name, "mute") == 0 &&
+-                  devinfo[1].mixer_class == outputs_idx)
+-                      return (0);
++      /*
++       * SIOCTL_NONE means control was deleted from the device
++       */
++      if (d->type == SIOCTL_NONE)
++              return;
+ 
+-              devinfo[i].index++;
+-      }
+-      return (-1);
+-}
++      /*
++       * we're interested in top-level output.xxx controls only
++       */
++      if (d->group[0] != 0 || strcmp(d->node0.name, "output") != 0)
++              return;
+ 
+-static int
+-get_volume(int fd, mixer_devinfo_t *devinfo, u_char *volume)
+-{
+-      mixer_ctrl_t mctl;
+-      int error;
++      if (strcmp(d->func, "level") == 0)
++              ismute = 0;
++      else if (strcmp(d->func, "mute") == 0)
++              ismute = 1;
++      else
++              return;
+ 
+-      memset(&mctl, 0, sizeof(mctl));
+-      mctl.dev = devinfo->index;
+-      mctl.type = AUDIO_MIXER_VALUE;
+-      error = ioctl(fd, AUDIO_MIXER_READ, &mctl);
+-      if (error == -1)
+-              return (-1);
+-      *volume = mctl.un.value.level[0];
+-      return (0);
++      i = malloc(sizeof(struct control));
++      if (i == NULL)
++              err(1, "malloc");
++
++      i->addr = d->addr;
++      i->max = d->maxval;
++      i->value = val;
++      i->ismute = ismute;
++      i->next = controls;
++      controls = i;
+ }
+ 
+-static int
+-set_volume(int fd, mixer_devinfo_t *devinfo, u_char volume)
++/*
++ * control value changed
++ */
++static void
++cb_control_value(void *unused, unsigned int addr, unsigned int value)
+ {
+-      mixer_ctrl_t mctl;
+-      int i, error;
++      GtkWidget *widget = (gpointer)gui_window;
++      struct control *c;
++      int volume, mute;
+ 
+-      memset(&mctl, 0, sizeof(mctl));
+-      mctl.dev = devinfo->index;
+-      mctl.type = devinfo->type;
+-      mctl.un.value.num_channels = devinfo->un.v.num_channels;
+-      for (i = 0; i < devinfo->un.v.num_channels; i++)
+-              mctl.un.value.level[i] = volume;
+-      error = ioctl(fd, AUDIO_MIXER_WRITE, &mctl);
+-      if (error == -1)
+-              return (-1);
+-      return (0);
++      for (c = controls; ; c = c->next) {
++              if (c == NULL)
++                      return;
++              if (c->addr == addr)
++                      break;
++      }
++
++      c->value = value;
++      
++      /*
++       * refresh gui state
++       */
++
++      get_state(&volume, &mute);
++
++      set_tray_icon(mute ? 0 : volume);
++
++      /* Move slider. Change "check" button state. */
++      if (widget->window != NULL) {
++              gtk_adjustment_set_value(GTK_ADJUSTMENT(gui_adj),
++                  100 - volume);
++              gtk_toggle_button_set_active(
++                  GTK_TOGGLE_BUTTON(gui_check), mute);
++              gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !mute);
++      }
+ }
+ 
+-static int
+-get_mute(int fd, mixer_devinfo_t *devinfo, int *mute)
++/*
++ * Return current volume and mute states:
++ *   - the returned volume is the maximum volume of all channels.
++ *   - the returned mute set 1 if all channels are muted
++ */
++static void
++get_state(int *rvolume, int *rmute)
+ {
+-      mixer_ctrl_t mctl;
+-      int error;
+-
+-      memset(&mctl, 0, sizeof(mctl));
+-      mctl.dev = devinfo->index;
+-      mctl.type = AUDIO_MIXER_ENUM;
+-      error = ioctl(fd, AUDIO_MIXER_READ, &mctl);
+-      if (error == -1)
+-              return (-1);
+-      *mute = mctl.un.ord;
+-      return (0);
++      struct control *c;
++      int mute = 0;
++      int v, volume = 100;
++      
++      for (c = controls; c != NULL; c = c->next) {
++              if (c->ismute) {
++                      if (c->value)
++                              mute = 1;
++              } else {
++                      v = (c->value * 100 + c->max / 2) / c->max;
++                      if (v < volume)
++                              volume = v;
++              }
++      }
++      *rvolume = volume;
++      *rmute = mute;
+ }
+ 
+-static int
+-set_mute(int fd, mixer_devinfo_t *devinfo, int mute)
++static void
++set_state(int ismute, int value)
+ {
+-      mixer_ctrl_t mctl;
+-      int error;
++      struct control *c;
++      int v;
+ 
+-      memset(&mctl, 0, sizeof(mctl));
+-      mctl.dev = devinfo->index;
+-      mctl.type = devinfo->type;
+-      mctl.un.ord = mute;
+-      error = ioctl(fd, AUDIO_MIXER_WRITE, &mctl);
+-      if (error == -1)
+-              return (-1);
+-      return (0);
++      for (c = controls; c != NULL; c = c->next) {
++              v = (value * c->max + 50) / 100;
++              if (c->ismute == ismute && c->value == v)
++                      continue;
++              c->value = v;
++              sioctl_setval(hdl, c->addr, v);
++      }
+ }
+ 
+ static void
+@@ -182,63 +216,31 @@ prepare_tooltip(int mute, u_char volume, char *text, s
+       if (mute) {
+               strlcpy(text, "Audio is muted", sz);
+       } else {
+-              snprintf(text, sz, "Audio volume: %u%%",
+-                  100U * (u_int)volume / AUDIO_MAX_GAIN);
++              snprintf(text, sz, "Audio volume: %u%%", volume);
+       }
+ }
+ 
+-static gboolean
+-cb_timer(GtkWidget *widget)
+-{
+-      int mute;
+-      u_char volume;
+-
+-      if (get_mute(mixer_fd, outputs_master_dev + 1, &mute) == -1)
+-              return (TRUE);
+-      else if (get_volume(mixer_fd, outputs_master_dev, &volume) == -1)
+-              return (TRUE);
+-      if (mute != current_mute || volume != current_volume) {
+-              set_tray_icon(mute ? 0 : volume);
+-
+-              /* Move slider. Change "check" button state. */
+-              if (widget->window != NULL) {
+-                      gtk_adjustment_set_value(GTK_ADJUSTMENT(gui_adj),
+-                          AUDIO_MAX_GAIN - volume);
+-                      gtk_toggle_button_set_active(
+-                          GTK_TOGGLE_BUTTON(gui_check), mute);
+-                      gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !mute);
+-              }
+-
+-              current_volume = volume;
+-              current_mute = mute;
+-      }
+-
+-      return (TRUE);
+-}
+-
+ static void
+ cb_button_toggled(GtkWidget *widget, gpointer data)
+ {
+-      int mute;
++      int mute, volume;
+ 
+       mute = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+-      if (set_mute(mixer_fd, outputs_master_dev + 1, mute) == 0) {
+-              gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !mute);
+-              set_tray_icon(mute ? 0 : current_volume);
+-              current_mute = mute;
+-      }
++      set_state(1, mute);
++      get_state(&volume, &mute);
++      gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !mute);
++      set_tray_icon(mute ? 0 : volume);
+ }
+ 
+ static void
+ cb_scale_value_changed(GtkScale *scale, GtkAdjustment *adj)
+ {
+-      u_char volume;
++      int mute, volume;
+ 
+-      volume = AUDIO_MAX_GAIN - (u_char)gtk_adjustment_get_value(adj);
+-      if (set_volume(mixer_fd, outputs_master_dev, volume) == 0) {
+-              set_tray_icon(current_mute ? 0 : volume);
+-              current_volume = volume;
+-      }
++      volume = 100 - (int)gtk_adjustment_get_value(adj);
++      set_state(0, volume);
++      get_state(&volume, &mute);
++      set_tray_icon(mute ? 0 : volume);
+ }
+ 
+ static gboolean
+@@ -277,8 +279,10 @@ cb_tray_query_tooltip(GtkStatusIcon *icon, gint x, gin
+     gboolean keyboard_mode, GtkTooltip *tooltip, gpointer data)
+ {
+       char text[30];
++      int volume, mute;
+ 
+-      prepare_tooltip(current_mute, current_volume, text, sizeof(text));
++      get_state(&volume, &mute);
++      prepare_tooltip(mute, volume, text, sizeof(text));
+       gtk_tooltip_set_text(tooltip, text);
+       return (TRUE);
+ }
+@@ -304,8 +308,7 @@ init_gui(void)
+       gtk_window_set_deletable(GTK_WINDOW(gui_window), FALSE);
+       gtk_window_set_decorated(GTK_WINDOW(gui_window), FALSE);
+ 
+-      gui_adj = gtk_adjustment_new(0.0, AUDIO_MIN_GAIN,
+-          AUDIO_MAX_GAIN, 1.0, 10.0, 0.0);
++      gui_adj = gtk_adjustment_new(0.0, 0, 100, 1.0, 10.0, 0.0);
+       if (gui_adj == NULL)
+               return (-1);
+ 
+@@ -355,12 +358,15 @@ show_gui(void)
+       GdkRectangle area;
+       GtkOrientation orientation;
+       int width, height, x, y;
++      int volume, mute;
+ 
++      get_state(&volume, &mute);
++
+       gtk_adjustment_set_value(GTK_ADJUSTMENT(gui_adj),
+-          AUDIO_MAX_GAIN - current_volume);
++          100 - volume);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gui_check),
+-          current_mute);
+-      gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !current_mute);
++          mute);
++      gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), !mute);
+       gtk_widget_show_all(GTK_WIDGET(gui_window));
+ 
+       gtk_status_icon_get_geometry(tray_icon, NULL, &area, &orientation);
+@@ -395,8 +401,7 @@ static int
+ init_tray(int doinvert)
+ {
+       char tooltip[30];
+-      u_char volume;
+-      int mute;
++      int volume, mute;
+ 
+       tray_pixbuf = gdk_pixbuf_new_from_xpm_data(sound_xpm);
+       if (tray_pixbuf == NULL)
+@@ -421,13 +426,9 @@ init_tray(int doinvert)
+       tray_icon = gtk_status_icon_new();
+       if (tray_icon == NULL)
+               return (-1);
+-      volume = 0;
+-      mute = 0;
+-      get_volume(mixer_fd, outputs_master_dev, &volume);
+-      get_mute(mixer_fd, outputs_master_dev + 1, &mute);
++
++      get_state(&volume, &mute);
+       set_tray_icon(mute ? 0 : volume);
+-      current_volume = volume;
+-      current_mute = mute;
+ 
+       prepare_tooltip(mute, volume, tooltip, sizeof(tooltip));
+       gtk_status_icon_set_tooltip_text(tray_icon, tooltip);
+@@ -455,7 +456,43 @@ set_tray_icon(u_char volume)
+       gtk_status_icon_set_from_pixbuf(tray_icon, pb);
+ }
+ 
++/*
++ * Call poll(2), for both gtk and sndio descriptors.
++ */
+ int
++do_poll(GPollFD *gtk_pfds, guint gtk_nfds, gint timeout)
++{
++#define MAXFDS 64
++      struct pollfd pfds[MAXFDS], *sioctl_pfds;
++      unsigned int sioctl_nfds;
++      unsigned int i;
++      int revents;
++      int rc;
++
++      for (i = 0; i < gtk_nfds; i++) {
++              pfds[i].fd = gtk_pfds[i].fd;
++              pfds[i].events = gtk_pfds[i].events;
++      }
++      if (hdl != NULL) {
++              sioctl_pfds = pfds + gtk_nfds;
++              sioctl_nfds = sioctl_pollfd(hdl, sioctl_pfds, POLLIN);
++      } else
++              sioctl_nfds = 0;
++
++      rc = poll(pfds, gtk_nfds + sioctl_nfds, timeout);
++      if (rc > 0 && hdl != NULL) {
++              revents = sioctl_revents(hdl, sioctl_pfds);
++              if (revents & POLLHUP)
++                      errx(1, "Device disconnected");
++      }
++
++      for (i = 0; i < gtk_nfds; i++)
++              gtk_pfds[i].revents = pfds[i].revents;
++
++      return rc;
++}
++
++int
+ main(int argc, char **argv)
+ {
+       char *progname;
+@@ -481,30 +518,32 @@ main(int argc, char **argv)
+       }
+       argc -= optind;
+       argv += optind;
+-
+-      mixer_fd = open("/dev/mixer", O_RDWR);
+-      if (mixer_fd == -1)
++      
++      hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0);
++      if (hdl == NULL) {
+               errx(1, "Cannot open mixer device");
+-
+-      error = get_mixer_index(mixer_fd, outputs_master_dev);
+-      if (error == -1) {
+-              close(mixer_fd);
++      }
++      if (!sioctl_ondesc(hdl, cb_control_desc, NULL)) {
++              sioctl_close(hdl);
+               errx(1, "Cannot get mixer information");
+       }
+-
+       error = init_tray(invert_flag);
+       if (error == -1) {
+-              close(mixer_fd);
++              sioctl_close(hdl);
+               errx(1, "Cannot initialize notification area");
+       }
+       error = init_gui();
+       if (error == -1) {
+-              close(mixer_fd);
++              sioctl_close(hdl);
+               errx(1, "Cannot initialize program window");
+       }
+-      g_timeout_add(1000, (GSourceFunc)cb_timer, (gpointer)gui_window);
++
++      /* register call-back for external volume changes */
++      sioctl_onval(hdl, cb_control_value, NULL);
++
++      g_main_context_set_poll_func(g_main_context_default(), do_poll);
+       gtk_main();
+ 
+-      close(mixer_fd);
++      sioctl_close(hdl);
+       return (0);
+ }
Index: audio/gqmpeg/Makefile
===================================================================
RCS file: /cvs/ports/audio/gqmpeg/Makefile,v
retrieving revision 1.64
diff -u -p -u -p -r1.64 Makefile
--- audio/gqmpeg/Makefile       12 Jul 2019 20:43:33 -0000      1.64
+++ audio/gqmpeg/Makefile       22 Feb 2020 16:06:19 -0000
@@ -3,7 +3,7 @@
 COMMENT=               front-end to various audio players
 
 DISTNAME=              gqmpeg-0.91.1
-REVISION=              14
+REVISION=              15
 CATEGORIES=            audio
 
 HOMEPAGE=              http://gqmpeg.sourceforge.net/
Index: audio/gqmpeg/patches/patch-src_Makefile_in
===================================================================
RCS file: audio/gqmpeg/patches/patch-src_Makefile_in
diff -N audio/gqmpeg/patches/patch-src_Makefile_in
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ audio/gqmpeg/patches/patch-src_Makefile_in  22 Feb 2020 16:06:19 -0000
@@ -0,0 +1,14 @@
+$OpenBSD$
+
+Index: src/Makefile.in
+--- src/Makefile.in.orig
++++ src/Makefile.in
+@@ -342,7 +342,7 @@ gqmpeg_SOURCES = \
+       $(module_mpg123) $(module_xmp) $(module_ogg123) $(module_radio)
+ 
+ 
+-gqmpeg_LDADD = $(GTK_LIBS) $(LIBPNG)
++gqmpeg_LDADD = $(GTK_LIBS) $(LIBPNG) -lsndio
+ 
+ EXTRA_DIST = \
+       $(extra_SLIK)   \
Index: audio/gqmpeg/patches/patch-src_mixer_c
===================================================================
RCS file: /cvs/ports/audio/gqmpeg/patches/patch-src_mixer_c,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 patch-src_mixer_c
--- audio/gqmpeg/patches/patch-src_mixer_c      14 Oct 2007 14:12:42 -0000      
1.2
+++ audio/gqmpeg/patches/patch-src_mixer_c      22 Feb 2020 16:06:19 -0000
@@ -1,39 +1,288 @@
 $OpenBSD: patch-src_mixer_c,v 1.2 2007/10/14 14:12:42 jasper Exp $
---- src/mixer.c.orig   Tue Sep 10 16:16:26 2002
-+++ src/mixer.c        Sun Oct 14 15:47:27 2007
-@@ -285,7 +285,11 @@ void mixer_init(gint init_device_id)
- 
-   mixer_device = getenv("MIXERDEVICE");
-   if (mixer_device == NULL)
-+#ifdef __OpenBSD__
-+    mixer_device = "/dev/mixer";
-+#else
-     mixer_device = "/dev/mixer0";
-+#endif
+Index: src/mixer.c
+--- src/mixer.c.orig
++++ src/mixer.c
+@@ -39,10 +39,16 @@
+ #include <sys/soundcard.h>
+ #endif
  
-   if ((fd = open(mixer_device, O_RDWR)) == -1) {
-     perror(mixer_device);
-@@ -362,7 +366,11 @@ static void mixer_set_vol(DeviceData *device, gint vol
- 
-   mixer_device = getenv("MIXERDEVICE");
-   if (mixer_device == NULL)
-+#ifdef __OpenBSD__
-+    mixer_device = "/dev/mixer";
-+#else
-     mixer_device = "/dev/mixer0";
-+#endif
+-#if defined(__NetBSD__) || defined(__OpenBSD__)
++#if defined(__NetBSD__)
+ #include <sys/audioio.h>
+ #endif
  
-   if ((fd = open(mixer_device, O_RDWR)) == -1) {
-     perror(mixer_device);
-@@ -406,7 +414,11 @@ static gint mixer_get_vol(DeviceData *device)
- 
-   mixer_device = getenv("MIXERDEVICE");
-   if (mixer_device == NULL)
-+#ifdef __OpenBSD__
-+    mixer_device = "/dev/mixer";
-+#else
-     mixer_device = "/dev/mixer0";
++#if defined(__OpenBSD__)
++#include <poll.h>
++#include <sndio.h>
++#include "display.h"
 +#endif
++
+ #if defined(sun) && defined(__svr4__)
+ #include <sys/audioio.h>
+ #endif
+@@ -267,11 +273,11 @@ static gint mixer_get_vol(DeviceData *device)
+ 
+ /*
+  *--------------------------------------------------------------------
+- * NetBSD and OpenBSD
++ * NetBSD
+  *--------------------------------------------------------------------
+  */
+ 
+-#elif defined(__NetBSD__) || defined(__OpenBSD__)
++#elif defined(__NetBSD__)
+ 
+ mixer_devinfo_t *infos;
+ mixer_ctrl_t *values;
+@@ -442,6 +448,242 @@ static gint mixer_get_vol(DeviceData *device)
+ 
+ /*
+  *--------------------------------------------------------------------
++ * OpenBSD
++ *--------------------------------------------------------------------
++ */
++
++#elif defined(__OpenBSD__)
++
++struct control {
++      struct control *next;
++      unsigned int addr;
++      unsigned int max, value;
++};
++
++static struct control *controls;
++static struct sioctl_hdl *hdl;
++static struct pollfd *pfds;
++static int initialized;
++
++/*
++ * new control registered
++ */
++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;
++              }
++      }
++
++      /*
++       * SIOCTL_NONE means control was deleted from the device
++       */
++      if (d->type == SIOCTL_NONE)
++              return;
++
++      /*
++       * we're interested in top-level output.level controls only
++       */
++      if (d->group[0] != 0 ||
++          strcmp(d->node0.name, "output") != 0 ||
++          strcmp(d->func, "level") != 0)
++              return;
++
++      i = malloc(sizeof(struct control));
++      if (i == NULL) {
++              perror("malloc");
++              return;
++      }
++
++      i->addr = d->addr;
++      i->max = d->maxval;
++      i->value = val;
++      i->next = controls;
++      controls = i;
++
++      if (debug_mode)
++              fprintf(stderr, "found output.level at %d\n", i->addr);
++}
++
++/*
++ * control value changed
++ */
++static void onval(void *unused, unsigned int addr, unsigned int value)
++{
++      struct control *c;
++
++      if (debug_mode)
++              fprintf(stderr, "control %d changed to %d\n", addr, value);
++
++      for (c = controls; ; c = c->next) {
++              if (c == NULL)
++                      return;
++              if (c->addr == addr)
++                      break;
++      }
++
++      c->value = value;
++
++      if (debug_mode)
++              fprintf(stderr, "refreshing\n");
++      display_set_volume();
++}
++
++/*
++ * Call poll(2), for both gtk and sndio descriptors.
++ */
++int
++do_poll(GPollFD *gtk_pfds, guint gtk_nfds, gint timeout)
++{
++#define MAXFDS 64
++      struct pollfd pfds[MAXFDS], *sioctl_pfds;
++      unsigned int sioctl_nfds;
++      unsigned int i;
++      int revents;
++      int rc;
++
++      for (i = 0; i < gtk_nfds; i++) {
++              pfds[i].fd = gtk_pfds[i].fd;
++              pfds[i].events = gtk_pfds[i].events;
++      }
++      if (hdl != NULL) {
++              sioctl_pfds = pfds + gtk_nfds;
++              sioctl_nfds = sioctl_pollfd(hdl, sioctl_pfds, POLLIN);
++      } else
++              sioctl_nfds = 0;
++
++      rc = poll(pfds, gtk_nfds + sioctl_nfds, timeout);
++      if (rc > 0 && hdl != NULL) {
++              revents = sioctl_revents(hdl, sioctl_pfds);
++              if (revents & POLLHUP) {
++                      fprintf(stderr, "Device disconnected\n");
++                      sioctl_close(hdl);
++                      hdl = NULL;
++              }
++      }
++
++      for (i = 0; i < gtk_nfds; i++)
++              gtk_pfds[i].revents = pfds[i].revents;
++
++      return rc;
++}
++
++void mixer_init(gint init_device_id)
++{
++      if (debug_mode)
++              fprintf(stderr, "mixer, initializing...\n");
++
++      if (initialized) {
++              fprintf(stderr, "mixer, already initialized\n");
++              return;
++      }
++
++      initialized = 1;
++      
++      hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ | SIOCTL_WRITE, 0);
++      if (hdl == NULL) {
++              fprintf(stderr, "Cannot open audio control device\n");
++              mixer_enabled = FALSE;
++              return;
++      }
++      if (!sioctl_ondesc(hdl, ondesc, NULL)) {
++              sioctl_close(hdl);
++              fprintf(stderr, "Cannot get mixer information\n");
++              mixer_enabled = FALSE;
++              return;
++      }
++
++      /* register call-back for external volume changes */
++      if (!sioctl_onval(hdl, onval, NULL)) {
++              sioctl_close(hdl);
++              fprintf(stderr, "Cannot get mixer values\n");
++              mixer_enabled = FALSE;
++              return;
++      }
++
++      pfds = malloc(sizeof(struct pollfd) * sioctl_nfds(hdl));
++      if (pfds == NULL) {
++              sioctl_close(hdl);
++              fprintf(stderr, "Cannot allocate pollfd structures\n");
++              mixer_enabled = FALSE;
++              return;
++      }
++
++      if (controls != NULL) {
++              DeviceData *device = g_new0(DeviceData, 1);
++              device->device_id = 0;
++              device->device_name = "output.level";
++              device->stereo = (controls->next != NULL);
++              device->recordable = 0;
++              device_list = g_list_append(device_list, device);
++              current_device = device_list->data;
++              current_vol = mixer_get_vol(current_device);
++              mixer_enabled = TRUE;
++      } else
++              mixer_enabled = FALSE;
++
++      if (debug_mode)
++              fprintf(stderr, "setting gtk poll function\n");
++      g_main_context_set_poll_func(g_main_context_default(), do_poll);
++
++}
++
++static void mixer_poll(void)
++{
++      int n, nfds;
++
++      nfds = sioctl_pollfd(hdl, pfds, 0);
++      if (nfds > 0) {
++              n = poll(pfds, nfds, 0);
++              if (n >= 0)
++                      sioctl_revents(hdl, pfds);
++      }
++}
++
++static void mixer_set_vol(DeviceData *device, gint vol)
++{
++      struct control *c;
++
++      if (hdl == NULL)
++              return;
++
++      for (c = controls; c != NULL; c = c->next) {
++              sioctl_setval(hdl, c->addr, (vol * c->max + 50) / 100);
++              if (debug_mode)
++                      fprintf(stderr, "setting %d to %d%%\n", c->addr, vol);
++      }
++}
++
++static gint mixer_get_vol(DeviceData *device)
++{
++      struct control *c;
++      int vol, minvol = 100;
++
++      for (c = controls; c != NULL; c = c->next) {
++              vol = (c->value * 100 + c->max / 2) / c->max;
++              if (vol < minvol)
++                      minvol = vol;
++      }
++
++      if (debug_mode)
++              fprintf(stderr, "get volume: %d\n", minvol);
++
++      return minvol;
++}
++
++/*
++ *--------------------------------------------------------------------
+  * Sun (svr4)
+  *--------------------------------------------------------------------
+  */
+@@ -1266,7 +1508,7 @@ gint get_volume(void)
+  * but some platforms did not have it update the volume (mixer_get_vol),
+  * and I am not going to mess with it.
+  */
+-#if defined (linux) || defined (__FreeBSD__)
++#if defined (linux) || defined (__FreeBSD__) || defined (__OpenBSD__)
+       current_vol = mixer_get_vol(current_device);
+ #endif
  
-   if ((fd = open(mixer_device, O_RDWR)) == -1) {
-     perror(mixer_device);
Index: x11/i3status/Makefile
===================================================================
RCS file: /cvs/ports/x11/i3status/Makefile,v
retrieving revision 1.58
diff -u -p -u -p -r1.58 Makefile
--- x11/i3status/Makefile       27 Sep 2019 20:33:22 -0000      1.58
+++ x11/i3status/Makefile       22 Feb 2020 16:06:19 -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: x11/i3status/patches/patch-Makefile_am
===================================================================
RCS file: x11/i3status/patches/patch-Makefile_am
diff -N x11/i3status/patches/patch-Makefile_am
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ x11/i3status/patches/patch-Makefile_am      22 Feb 2020 16:06:19 -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: x11/i3status/patches/patch-Makefile_in
===================================================================
RCS file: x11/i3status/patches/patch-Makefile_in
diff -N x11/i3status/patches/patch-Makefile_in
--- x11/i3status/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: x11/i3status/patches/patch-configure_ac
===================================================================
RCS file: x11/i3status/patches/patch-configure_ac
diff -N x11/i3status/patches/patch-configure_ac
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ x11/i3status/patches/patch-configure_ac     22 Feb 2020 16:06:19 -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: x11/i3status/patches/patch-include_i3status_h
===================================================================
RCS file: x11/i3status/patches/patch-include_i3status_h
diff -N x11/i3status/patches/patch-include_i3status_h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ x11/i3status/patches/patch-include_i3status_h       22 Feb 2020 16:06:19 
-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: x11/i3status/patches/patch-src_print_volume_c
===================================================================
RCS file: x11/i3status/patches/patch-src_print_volume_c
diff -N x11/i3status/patches/patch-src_print_volume_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ x11/i3status/patches/patch-src_print_volume_c       22 Feb 2020 16:06:19 
-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: x11/i3status/patches/patch-src_sndio_c
===================================================================
RCS file: x11/i3status/patches/patch-src_sndio_c
diff -N x11/i3status/patches/patch-src_sndio_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ x11/i3status/patches/patch-src_sndio_c      22 Feb 2020 16:06:20 -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