On Sun Mar 08, 2020 at 04:32:49PM +0100, Alexandre Ratchov wrote: > This diff switches sysutils/tray-app to the new audio control API. Now > the programm displays and controls the sndiod master volume knob. This > has two advantages: > > - tray-app now works on any device, including those with no volume > controls. > > - in case there are multiple audio devices, tray-app won't pick the > wrong one anymore > > OK?
OK rsadowski@, works fine here. Thanks > > Index: Makefile > =================================================================== > RCS file: /cvs/ports/sysutils/tray-app/Makefile,v > retrieving revision 1.8 > diff -u -p -u -p -r1.8 Makefile > --- Makefile 12 Jul 2019 20:49:53 -0000 1.8 > +++ Makefile 8 Mar 2020 15:19:47 -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: 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 > --- patches/patch-sound_Makefile 17 Sep 2013 11:21:50 -0000 1.1.1.1 > +++ patches/patch-sound_Makefile 8 Mar 2020 15:19:47 -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: patches/patch-sound_sound_c > =================================================================== > RCS file: patches/patch-sound_sound_c > diff -N patches/patch-sound_sound_c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ patches/patch-sound_sound_c 8 Mar 2020 15:19:47 -0000 > @@ -0,0 +1,539 @@ > +$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, > +@@ -44,12 +49,13 @@ static gboolean cb_tray_query_tooltip(GtkStatusIcon *i > + static int init_gui(void); > + static void show_gui(void); > + static void hide_gui(void); > ++static void refresh_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 +69,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 +79,134 @@ 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 *c, **pc; > ++ int has_volume, has_mute; > ++ 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) { > ++ /* > ++ * NULL indicates that we got all changes and its time > ++ * to rebuild the GUI > ++ */ > ++ has_volume = has_mute = 0; > ++ for (c = controls; c != NULL; c = c->next) { > ++ if (c->ismute) > ++ has_mute = 1; > ++ else > ++ has_volume = 1; > ++ } > ++ gtk_widget_set_sensitive(GTK_WIDGET(gui_scale), has_volume); > ++ gtk_widget_set_sensitive(GTK_WIDGET(gui_check), has_mute); > ++ return; > ++ } > ++ > ++ /* > ++ * delete existing control with the same address > ++ */ > ++ for (pc = &controls; (c = *pc) != NULL; pc = &c->next) { > ++ if (d->addr == c->addr) { > ++ *pc = c->next; > ++ free(c); > + 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 > ++ */ > ++ if (d->type == SIOCTL_NONE) > ++ return; > + > +- devinfo[i].index++; > +- } > +- return (-1); > +-} > ++ /* > ++ * we're interested in top-level output.xxx controls only > ++ */ > ++ if (strcmp(d->group, "") != 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); > ++ c = malloc(sizeof(struct control)); > ++ if (c == NULL) > ++ err(1, "malloc"); > ++ > ++ c->addr = d->addr; > ++ c->max = d->maxval; > ++ c->value = val; > ++ c->ismute = ismute; > ++ c->next = controls; > ++ controls = c; > + } > + > +-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; > ++ struct control *c; > + > +- 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; > ++ } > ++ if (c->value == value) > ++ return; > ++ > ++ c->value = value; > ++ refresh_gui(); > + } > + > +-static int > +-get_mute(int fd, mixer_devinfo_t *devinfo, int *mute) > ++/* > ++ * Return current volume and mute states: > ++ * - the returned volume is the minimum 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) { > ++ mute |= !!c->value; > ++ } 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; > + > +- 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) > ++ continue; > ++ c->value = c->ismute ? !!value : (value * c->max + 50) / 100; > ++ sioctl_setval(hdl, c->addr, c->value); > ++ } > ++ > ++ refresh_gui(); > + } > + > + static void > +@@ -182,63 +215,20 @@ 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; > +- > +- 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, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); > + } > + > + static void > + cb_scale_value_changed(GtkScale *scale, GtkAdjustment *adj) > + { > +- u_char 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; > +- } > ++ set_state(0, 100 - (int)gtk_adjustment_get_value(adj)); > + } > + > + static gboolean > +@@ -277,8 +267,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 +296,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 +346,14 @@ 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_show_all(GTK_WIDGET(gui_window)); > + > + gtk_status_icon_get_geometry(tray_icon, NULL, &area, &orientation); > +@@ -391,12 +384,33 @@ hide_gui(void) > + gtk_widget_hide(GTK_WIDGET(gui_window)); > + } > + > ++/* > ++ * refresh gui state > ++ */ > ++static void > ++refresh_gui(void) > ++{ > ++ GtkWidget *widget = (gpointer)gui_window; > ++ int volume, mute; > ++ > ++ 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); > ++ } > ++} > ++ > + 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 +435,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 +465,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 +527,34 @@ 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); > +- 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); > ++ > ++ if (!sioctl_ondesc(hdl, cb_control_desc, NULL)) { > ++ sioctl_close(hdl); > ++ errx(1, "Cannot get mixer information"); > ++ } > ++ if (!sioctl_onval(hdl, cb_control_value, NULL)) { > ++ sioctl_close(hdl); > ++ errx(1, "Cannot get mixer values"); > ++ } > ++ > ++ g_main_context_set_poll_func(g_main_context_default(), do_poll); > + gtk_main(); > + > +- close(mixer_fd); > ++ sioctl_close(hdl); > + return (0); > + } >