Use the new sioctl_open(3) API to display current audio level.

Now the programm displays the current sndiod master volume level,
which is present on any device and always corresponds to the device
programs are playing on.
This commit is contained in:
ratchov 2020-03-16 14:54:45 +00:00
parent 7bf463e958
commit 9f385cbca0
7 changed files with 394 additions and 18 deletions

View File

@ -1,11 +1,11 @@
# $OpenBSD: Makefile,v 1.58 2019/09/27 20:33:22 jasper Exp $
# $OpenBSD: Makefile,v 1.59 2020/03/16 14:54:45 ratchov Exp $
ONLY_FOR_ARCHS= ${APM_ARCHS}
COMMENT= generate a statusbar for use with i3/xmobar/dzen2
DISTNAME= i3status-2.13
REVISION= 1
REVISION= 2
CATEGORIES= x11 sysutils
HOMEPAGE= https://i3wm.org/i3status/
@ -28,7 +28,9 @@ BUILD_DEPENDS= textproc/asciidoc>=8.6.8 \
LIB_DEPENDS= devel/libconfuse \
devel/libyajl
CONFIGURE_STYLE = gnu
AUTOCONF_VERSION = 2.69
AUTOMAKE_VERSION = 1.16
CONFIGURE_STYLE = autoreconf
SEPARATE_BUILD = Yes
FAKE_FLAGS += sysconfdir=${PREFIX}/share/examples/i3status/

View File

@ -0,0 +1,37 @@
$OpenBSD: patch-Makefile_am,v 1.1 2020/03/16 14:54:45 ratchov Exp $
Index: Makefile.am
--- Makefile.am.orig
+++ Makefile.am
@@ -1,5 +1,3 @@
-@CODE_COVERAGE_RULES@
-
echo-version:
@echo "@I3STATUS_VERSION@"
@@ -30,6 +28,7 @@ i3status_CFLAGS = \
$(PULSE_CFLAGS) \
$(NLGENL_CFLAGS) \
$(ALSA_CFLAGS) \
+ $(SNDIO_CFLAGS) \
$(PTHREAD_CFLAGS)
i3status_CPPFLAGS = \
@@ -42,6 +41,7 @@ i3status_LDADD = \
$(PULSE_LIBS) \
$(NLGENL_LIBS) \
$(ALSA_LIBS) \
+ $(SNDIO_LIBS) \
$(PTHREAD_LIBS)
i3status_SOURCES = \
@@ -69,7 +69,8 @@ i3status_SOURCES = \
src/print_wireless_info.c \
src/print_file_contents.c \
src/process_runs.c \
- src/pulse.c
+ src/pulse.c \
+ src/sndio.c
dist_sysconf_DATA = \
i3status.conf

View File

@ -1,15 +0,0 @@
$OpenBSD: patch-Makefile_in,v 1.1 2019/07/06 20:20:27 jasper Exp $
The CODE_COVERAGE_RULES fragment contains an unmatched "if" clause.
Index: Makefile.in
--- Makefile.in.orig
+++ Makefile.in
@@ -1851,7 +1851,6 @@ uninstall-man: uninstall-man1
.PRECIOUS: Makefile
-@CODE_COVERAGE_RULES@
echo-version:
@echo "@I3STATUS_VERSION@"

View File

@ -0,0 +1,19 @@
$OpenBSD: patch-configure_ac,v 1.1 2020/03/16 14:54:45 ratchov Exp $
Index: configure.ac
--- configure.ac.orig
+++ configure.ac
@@ -91,6 +91,13 @@ case $host_os in
;;
esac
+# if sndio is available, define USE_SNDIO
+AC_CHECK_HEADER(sndio.h,
+ [AC_CHECK_LIB([sndio], [sio_open], [
+ AC_SUBST(SNDIO_LIBS, "-lsndio")
+ AC_DEFINE([USE_SNDIO], [], [Use sndio])
+ ], [])], [])
+
dnl TODO: check for libbsd for GNU/kFreeBSD
# Checks for programs.

View File

@ -0,0 +1,13 @@
$OpenBSD: patch-include_i3status_h,v 1.10 2020/03/16 14:54:45 ratchov Exp $
Index: include/i3status.h
--- include/i3status.h.orig
+++ include/i3status.h
@@ -232,6 +232,7 @@ int volume_pulseaudio(uint32_t sink_idx, const char *s
bool description_pulseaudio(uint32_t sink_idx, const char *sink_name, char buffer[MAX_SINK_DESCRIPTION_LEN]);
bool pulse_initialize(void);
void print_file_contents(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_bad, const int max_chars);
+int volume_sndio(void);
/* socket file descriptor for general purposes */
extern int general_socket;

View File

@ -0,0 +1,144 @@
$OpenBSD: patch-src_print_volume_c,v 1.12 2020/03/16 14:54:45 ratchov Exp $
Index: src/print_volume.c
--- src/print_volume.c.orig
+++ src/print_volume.c
@@ -21,13 +21,6 @@
#include <sys/soundcard.h>
#endif
-#ifdef __OpenBSD__
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/audioio.h>
-#include <sys/ioctl.h>
-#endif
-
#include "i3status.h"
#include "queue.h"
@@ -145,9 +138,20 @@ void print_volume(yajl_gen json_gen, char *buffer, con
/* 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 USE_SNDIO
+ int vol;
+
+ vol = volume_sndio();
+ if (vol != -1) {
+ outwalk = apply_volume_format(fmt, outwalk, vol & 0x7f, "sndio");
+ goto out;
+ }
+/* If sndio is not detected, proceed to ALSA / OSS */
+#endif
+
#ifdef __linux__
const long MAX_LINEAR_DB_SCALE = 24;
int err;
@@ -248,7 +252,8 @@ void print_volume(yajl_gen json_gen, char *buffer, con
snd_mixer_selem_id_free(sid);
#endif
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
char *mixerpath;
char defaultmixer[] = "/dev/mixer";
int mixfd, vol, devmask = 0;
@@ -261,84 +266,13 @@ void print_volume(yajl_gen json_gen, char *buffer, con
mixerpath = defaultmixer;
if ((mixfd = open(mixerpath, O_RDWR)) < 0) {
-#if defined(__OpenBSD__)
- warn("audioio: Cannot open mixer");
-#else
warn("OSS: Cannot open mixer");
-#endif
goto out;
}
if (mixer_idx > 0)
free(mixerpath);
-#if defined(__OpenBSD__)
- int oclass_idx = -1, master_idx = -1, master_mute_idx = -1;
- int master_next = AUDIO_MIXER_LAST;
- mixer_devinfo_t devinfo, devinfo2;
- mixer_ctrl_t vinfo;
-
- devinfo.index = 0;
- while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo) >= 0) {
- if (devinfo.type != AUDIO_MIXER_CLASS) {
- devinfo.index++;
- continue;
- }
- if (strncmp(devinfo.label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN) == 0)
- oclass_idx = devinfo.index;
-
- devinfo.index++;
- }
-
- devinfo2.index = 0;
- while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo2) >= 0) {
- if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) == 0)) {
- master_idx = devinfo2.index;
- master_next = devinfo2.next;
- }
-
- if ((devinfo2.type == AUDIO_MIXER_ENUM) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmute, MAX_AUDIO_DEV_LEN) == 0))
- if (master_next == devinfo2.index)
- master_mute_idx = devinfo2.index;
-
- if (master_next != AUDIO_MIXER_LAST)
- master_next = devinfo2.next;
- devinfo2.index++;
- }
-
- if (master_idx == -1)
- goto out;
-
- devinfo.index = master_idx;
- if (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo) == -1)
- goto out;
-
- vinfo.dev = master_idx;
- vinfo.type = AUDIO_MIXER_VALUE;
- vinfo.un.value.num_channels = devinfo.un.v.num_channels;
- if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1)
- goto out;
-
- if (AUDIO_MAX_GAIN != 100) {
- float avgf = ((float)vinfo.un.value.level[AUDIO_MIXER_LEVEL_MONO] / AUDIO_MAX_GAIN) * 100;
- vol = (int)avgf;
- vol = (avgf - vol < 0.5 ? vol : (vol + 1));
- } else {
- vol = (int)vinfo.un.value.level[AUDIO_MIXER_LEVEL_MONO];
- }
-
- vinfo.dev = master_mute_idx;
- vinfo.type = AUDIO_MIXER_ENUM;
- if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1)
- goto out;
-
- if (master_mute_idx != -1 && vinfo.un.ord) {
- START_COLOR("color_degraded");
- fmt = fmt_muted;
- pbval = 0;
- }
-
-#else
if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
warn("OSS: Cannot read mixer information");
goto out;
@@ -353,7 +287,6 @@ void print_volume(yajl_gen json_gen, char *buffer, con
pbval = 0;
}
-#endif
outwalk = apply_volume_format(fmt, outwalk, vol & 0x7f, devicename);
close(mixfd);
#endif

View File

@ -0,0 +1,176 @@
$OpenBSD: patch-src_sndio_c,v 1.1 2020/03/16 14:54:45 ratchov Exp $
Add sndio volume backend.
Index: src/sndio.c
--- src/sndio.c.orig
+++ src/sndio.c
@@ -0,0 +1,168 @@
+#include <poll.h>
+#include <sndio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "i3status.h"
+
+struct control {
+ struct control *next;
+ unsigned int addr;
+ unsigned int max;
+ unsigned int value;
+};
+
+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;
+ }
+ }
+
+ /*
+ * 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;
+ }
+
+ i->addr = d->addr;
+ i->max = d->maxval;
+ i->value = val;
+ i->next = controls;
+ 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)
+ break;
+ }
+
+ c->value = value;
+}
+
+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(void)
+{
+ struct control *c;
+ int n, v, value;
+
+ 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
+ */
+ value = 100;
+ for (c = controls; c != NULL; c = c->next) {
+ v = (c->value * 100 + c->max / 2) / c->max;
+ if (v < value)
+ value = v;
+ }
+
+ return value;
+}