From 22cc85cf0b0ca97835071ac4452f24fb56f0c729 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Mon, 6 Dec 2021 11:32:56 +0100 Subject: [PATCH 1/4] add sndio(7) support which is the only supported mixer backend on OpenBSD --- include/i3status.h | 1 + meson.build | 10 +++ meson_options.txt | 3 + src/print_volume.c | 29 +++++-- src/sndio.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 src/sndio.c diff --git a/include/i3status.h b/include/i3status.h index fe44780b..7e6b5282 100644 --- a/include/i3status.h +++ b/include/i3status.h @@ -425,6 +425,7 @@ void print_volume(volume_ctx_t *ctx); bool process_runs(const char *path); int volume_pulseaudio(uint32_t sink_idx, const char *sink_name); +int volume_sndio(int *vol, int *muted); bool description_pulseaudio(uint32_t sink_idx, const char *sink_name, char buffer[MAX_SINK_DESCRIPTION_LEN]); bool pulse_initialize(void); diff --git a/meson.build b/meson.build index 83b1e61a..c09983c9 100644 --- a/meson.build +++ b/meson.build @@ -50,6 +50,10 @@ if get_option('pulseaudio') cdata.set('HAS_PULSEAUDIO', 1) endif +if get_option('sndio') + cdata.set('HAS_SNDIO', 1) +endif + # Instead of generating config.h directly, make vcs_tag generate it so that # @VCS_TAG@ is replaced. config_h_in = configure_file( @@ -191,6 +195,12 @@ if get_option('pulseaudio') i3status_srcs += ['src/pulse.c'] endif +if get_option('sndio') + sndio_dep = cc.find_library('sndio') + i3status_deps += [sndio_dep] + i3status_srcs += ['src/sndio.c'] +endif + host_os = host_machine.system() if host_os == 'linux' nlgenl_dep = dependency('libnl-genl-3.0', method: 'pkg-config') diff --git a/meson_options.txt b/meson_options.txt index 33e0937b..0e11007a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,3 +11,6 @@ option('docdir', type: 'string', value: '', option('pulseaudio', type: 'boolean', value: true, description: 'Build with pulseaudio support') + +option('sndio', type: 'boolean', value: true, + description: 'Build with sndio support') diff --git a/src/print_volume.c b/src/print_volume.c index dfe7f511..900f6f38 100644 --- a/src/print_volume.c +++ b/src/print_volume.c @@ -21,7 +21,7 @@ #include #endif -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) #include #include #include @@ -137,7 +137,26 @@ void print_volume(volume_ctx_t *ctx) { /* 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 HAS_SNDIO + int vol, mute; + + if (volume_sndio(&vol, &mute) == 0) { + if (mute) { + START_COLOR("color_degraded"); + pbval = 0; + } + char *formatted = apply_volume_format(mute ? ctx->fmt_muted : ctx->fmt, + vol & 0x7f, "sndio"); + OUTPUT_FORMATTED; + free(formatted); + goto out_with_format; + } + + goto out; +/* If sndio is not detected, proceed to ALSA / OSS */ #endif #ifdef __linux__ @@ -243,7 +262,7 @@ void print_volume(volume_ctx_t *ctx) { goto out_with_format; #endif -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) char *mixerpath; char defaultmixer[] = "/dev/mixer"; int mixfd, vol, devmask = 0; @@ -256,7 +275,7 @@ void print_volume(volume_ctx_t *ctx) { mixerpath = defaultmixer; if ((mixfd = open(mixerpath, O_RDWR)) < 0) { -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) warn("audioio: Cannot open mixer"); #else warn("OSS: Cannot open mixer"); @@ -267,7 +286,7 @@ void print_volume(volume_ctx_t *ctx) { if (ctx->mixer_idx > 0) free(mixerpath); -#if defined(__NetBSD__) || defined(__OpenBSD__) +#if defined(__NetBSD__) int oclass_idx = -1, master_idx = -1, master_mute_idx = -1; int master_next = AUDIO_MIXER_LAST; mixer_devinfo_t devinfo, devinfo2; diff --git a/src/sndio.c b/src/sndio.c new file mode 100644 index 00000000..3381e5ff --- /dev/null +++ b/src/sndio.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include "i3status.h" + +struct control { + struct control *next; + char name[32]; + unsigned int addr; + unsigned int max; + unsigned int value; + unsigned int muted; + unsigned int muteaddr; +}; + +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; + } + } + + /* + * if we find an output.mute, associate it with its output.level + */ + if (d->type == SIOCTL_SW && + d->group[0] == 0 && + strcmp(d->node0.name, "output") == 0 && + strcmp(d->func, "mute") == 0) { + char name[32]; + snprintf(name, sizeof(name), "%s%d", d->node0.name, d->node0.unit); + for (pi = &controls; (i = *pi) != NULL; pi = &i->next) { + if (strcmp(name, i->name) == 0) { + i->muted = val; + i->muteaddr = d->addr; + break; + } + } + return; + } + + /* + * 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; + } + + snprintf(i->name, sizeof(i->name), "%s%d", d->node0.name, d->node0.unit); + i->addr = d->addr; + i->max = d->maxval; + i->value = val; + i->next = controls; + i->muted = 0; + i->muteaddr = -1; + 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) { + c->value = value; + return; + } + if (c->muteaddr == addr) { + c->muted = value; + return; + } + } +} + +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(int *vol, int *muted) +{ + struct control *c; + int n, v; + + 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 + */ + *vol = 100; + *muted = 0; + for (c = controls; c != NULL; c = c->next) { + v = (c->value * 100 + c->max / 2) / c->max; + if (v < *vol) { + *vol = v; + *muted = c->muted; + + } + } + + return 0; +} From 0ae695f8826cac03c396d8f3cbf7b8c8593f6437 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Mon, 6 Dec 2021 14:43:29 +0100 Subject: [PATCH 2/4] apply clang-format --- src/print_volume.c | 6 +++--- src/sndio.c | 22 ++++++++-------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/print_volume.c b/src/print_volume.c index 900f6f38..04da9d6b 100644 --- a/src/print_volume.c +++ b/src/print_volume.c @@ -143,10 +143,10 @@ void print_volume(volume_ctx_t *ctx) { #ifdef HAS_SNDIO int vol, mute; - if (volume_sndio(&vol, &mute) == 0) { + if (volume_sndio(&vol, &mute) == 0) { if (mute) { - START_COLOR("color_degraded"); - pbval = 0; + START_COLOR("color_degraded"); + pbval = 0; } char *formatted = apply_volume_format(mute ? ctx->fmt_muted : ctx->fmt, vol & 0x7f, "sndio"); diff --git a/src/sndio.c b/src/sndio.c index 3381e5ff..40f0b786 100644 --- a/src/sndio.c +++ b/src/sndio.c @@ -23,8 +23,7 @@ static struct pollfd *pfds; /* * new control registered or control changed */ -static void ondesc(void *unused, struct sioctl_desc *d, int val) -{ +static void ondesc(void *unused, struct sioctl_desc *d, int val) { struct control *i, **pi; if (d == NULL) @@ -50,14 +49,14 @@ static void ondesc(void *unused, struct sioctl_desc *d, int val) strcmp(d->func, "mute") == 0) { char name[32]; snprintf(name, sizeof(name), "%s%d", d->node0.name, d->node0.unit); - for (pi = &controls; (i = *pi) != NULL; pi = &i->next) { + for (pi = &controls; (i = *pi) != NULL; pi = &i->next) { if (strcmp(name, i->name) == 0) { i->muted = val; i->muteaddr = d->addr; break; } } - return; + return; } /* @@ -88,11 +87,10 @@ static void ondesc(void *unused, struct sioctl_desc *d, int val) /* * control value changed */ -static void onval(void *unused, unsigned int addr, unsigned int value) -{ +static void onval(void *unused, unsigned int addr, unsigned int value) { struct control *c; - for (c = controls; ; c = c->next) { + for (c = controls;; c = c->next) { if (c == NULL) return; if (c->addr == addr) { @@ -106,8 +104,7 @@ static void onval(void *unused, unsigned int addr, unsigned int value) } } -static void cleanup(void) -{ +static void cleanup(void) { struct control *c; if (hdl) { @@ -124,8 +121,7 @@ static void cleanup(void) } } -static int init(void) -{ +static int init(void) { /* open device */ hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0); if (hdl == NULL) { @@ -157,8 +153,7 @@ static int init(void) return 0; } -int volume_sndio(int *vol, int *muted) -{ +int volume_sndio(int *vol, int *muted) { struct control *c; int n, v; @@ -193,7 +188,6 @@ int volume_sndio(int *vol, int *muted) if (v < *vol) { *vol = v; *muted = c->muted; - } } From 70edc0d050b3b8201f1fb151b6517e30e2aaf70f Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sun, 19 Dec 2021 09:34:59 +0100 Subject: [PATCH 3/4] style changes --- src/sndio.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sndio.c b/src/sndio.c index 40f0b786..188bbad8 100644 --- a/src/sndio.c +++ b/src/sndio.c @@ -26,8 +26,9 @@ static struct pollfd *pfds; static void ondesc(void *unused, struct sioctl_desc *d, int val) { struct control *i, **pi; - if (d == NULL) + if (d == NULL) { return; + } /* * delete existing control with the same address @@ -90,9 +91,7 @@ static void ondesc(void *unused, struct sioctl_desc *d, int val) { static void onval(void *unused, unsigned int addr, unsigned int value) { struct control *c; - for (c = controls;; c = c->next) { - if (c == NULL) - return; + for (c = controls; c != NULL; c = c->next) { if (c->addr == addr) { c->value = value; return; From 3eaa510ba5dd58d92aef075072651aaf5b7baf91 Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Sun, 19 Dec 2021 10:42:15 +0100 Subject: [PATCH 4/4] set default value of sndio to false --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 0e11007a..99988ad4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,5 +12,5 @@ option('docdir', type: 'string', value: '', option('pulseaudio', type: 'boolean', value: true, description: 'Build with pulseaudio support') -option('sndio', type: 'boolean', value: true, +option('sndio', type: 'boolean', value: false, description: 'Build with sndio support')