Index: src/mixer.c
--- src/mixer.c.orig
+++ src/mixer.c
@@ -39,10 +39,16 @@
 #include <sys/soundcard.h>
 #endif
 
-#if defined(__NetBSD__) || defined(__OpenBSD__)
+#if defined(__NetBSD__)
 #include <sys/audioio.h>
 #endif
 
+#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
 
