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

Reply via email to