There are two ports only using the kernel-based API, this diff updates
them to use new libsndio-based API:
  - sysutils/tray-app
  - audio/gqmpeg

As a side effect, this fixes gqmpeg crashes, makes these programs use
the right device, and make them work on many uaudio(4) and envy(4)
devices with no hardware master level knob. Also when one program
changes the volume, slider of other programs are properly updated.

enjoy,

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       4 Feb 2020 22:28:34 -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  4 Feb 2020 22:28:34 -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      4 Feb 2020 22:28:34 -0000
@@ -1,39 +1,287 @@
 $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,241 @@ static gint mixer_get_vol(DeviceData *device)
+ 
+ /*
+  *--------------------------------------------------------------------
++ * OpenBSD
++ *--------------------------------------------------------------------
++ */
++
++#elif defined(__OpenBSD__)
++
++struct control {
++      struct control *next;
++      unsigned int addr;
++      unsigned int 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->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(SIOCTL_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 * SIOCTL_VALMAX + 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 + SIOCTL_VALMAX / 2) / SIOCTL_VALMAX;
++              if (vol < minvol)
++                      minvol = vol;
++      }
++
++      if (debug_mode)
++              fprintf(stderr, "get volume: %d\n", minvol);
++
++      return minvol;
++}
++
++/*
++ *--------------------------------------------------------------------
+  * Sun (svr4)
+  *--------------------------------------------------------------------
+  */
+@@ -1266,7 +1507,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: 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  4 Feb 2020 22:28:34 -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      4 Feb 2020 22:28:34 
-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       4 Feb 2020 22:28:34 
-0000
@@ -0,0 +1,511 @@
+$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,19 @@
+ #include "sound-1.xpm"
+ #include "sound-2.xpm"
+ 
++struct control {
++      struct control *next;
++      unsigned int addr;
++      unsigned int value;
++      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 +52,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 +67,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 +77,132 @@ 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->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),
++                  SIOCTL_VALMAX - 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 volume = SIOCTL_VALMAX;
++      
++      for (c = controls; c != NULL; c = c->next) {
++              if (c->ismute) {
++                      if (c->value)
++                              mute = 1;
++              } else {
++                      if (c->value < volume)
++                              volume = c->value;
++              }
++      }
++      *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;
+ 
+-      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) {
++              if (c->ismute != ismute || c->value == value)
++                      continue;
++              c->value = value;
++              sioctl_setval(hdl, c->addr, value);
++      }
+ }
+ 
+ static void
+@@ -183,62 +212,31 @@ prepare_tooltip(int mute, u_char volume, char *text, s
+               strlcpy(text, "Audio is muted", sz);
+       } else {
+               snprintf(text, sz, "Audio volume: %u%%",
+-                  100U * (u_int)volume / AUDIO_MAX_GAIN);
++                  100U * (u_int)volume / SIOCTL_VALMAX);
+       }
+ }
+ 
+-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 = SIOCTL_VALMAX - (int)gtk_adjustment_get_value(adj);
++      set_state(0, volume);
++      get_state(&volume, &mute);
++      set_tray_icon(mute ? 0 : volume);
+ }
+ 
+ static gboolean
+@@ -277,8 +275,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 +304,8 @@ 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,
++          SIOCTL_VALMAX, 1.0, 10.0, 0.0);
+       if (gui_adj == NULL)
+               return (-1);
+ 
+@@ -355,12 +355,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);
++          SIOCTL_VALMAX - 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 +398,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 +423,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 +453,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\n");
++      }
++
++      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 +515,32 @@ main(int argc, char **argv)
+       }
+       argc -= optind;
+       argv += optind;
+-
+-      mixer_fd = open("/dev/mixer", O_RDWR);
+-      if (mixer_fd == -1)
++      
++      hdl = sioctl_open(SIOCTL_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);
+ }

Reply via email to