--- a/configure.ac 2019-06-30 19:53:24.000000000 +0200 +++ b/configure.ac 2021-07-19 13:39:39.769190529 +0200 @@ -80,11 +80,24 @@ PKG_CHECK_MODULES([CONFUSE], [libconfuse]) PKG_CHECK_MODULES([YAJL], [yajl]) +AC_ARG_ENABLE(pulseaudio, + AS_HELP_STRING( + [--disable-pulseaudio], + [build without pulseaudio support]), + [ax_pulse=$enableval], + [ax_pulse=yes]) +AM_CONDITIONAL([PULSE], [test x$ax_pulse = xyes]) +AS_IF([test x"$ax_pulse" = x"yes"], + [PKG_CHECK_MODULES([PULSE], [libpulse])]) +pulse_def=0 +AS_IF([test x"$ax_pulse" = x"yes"], + [pulse_def=1]) +AC_DEFINE_UNQUOTED([HAS_PULSEAUDIO], [$pulse_def], [Build with pulseaudio]) + case $host_os in linux*) PKG_CHECK_MODULES([NLGENL], [libnl-genl-3.0]) PKG_CHECK_MODULES([ALSA], [alsa]) - PKG_CHECK_MODULES([PULSE], [libpulse]) ;; netbsd*) AC_SEARCH_LIBS([prop_string_create], [prop]) @@ -101,12 +114,20 @@ AC_PROG_RANLIB AC_PROG_LN_S -AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc], [no]) -AS_IF([test x"$PATH_ASCIIDOC" = x"no"], - [AC_MSG_ERROR([asciidoc is required for generating man pages])]) -AC_PATH_PROG([PATH_XMLTO], [xmlto], [no]) -AS_IF([test x"$PATH_XMLTO" = x"no"], - [AC_MSG_ERROR([xmlto is required for generating man pages])]) +AC_ARG_ENABLE(mans, + AS_HELP_STRING( + [--disable-mans], + [disable building manual pages]), + [ax_mans=$enableval], + [ax_mans=yes]) +AS_IF([test x$ax_mans = xyes], [ + AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc]) +]) +AS_IF([test x$ax_mans = xyes], [ + AC_PATH_PROG([PATH_XMLTO], [xmlto]) + AC_PATH_PROG([PATH_POD2MAN], [pod2man]) +]) +AM_CONDITIONAL([BUILD_MANS], [test x$ax_mans = xyes && test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x]) AM_PROG_AR @@ -131,15 +152,12 @@ AC_OUTPUT -in_git_worktree=`git rev-parse --is-inside-work-tree 2>/dev/null` -if [ "$in_git_worktree" = "true" ]; then - git_dir=`git rev-parse --git-dir 2>/dev/null` - srcdir=`dirname "$git_dir"` - exclude_dir=`pwd | sed "s,^$srcdir,,g"` - if ! grep -q "^$exclude_dir" "$git_dir/info/exclude"; then - echo "$exclude_dir" >> "$git_dir/info/exclude" - fi -fi +AS_IF([test -d ${srcdir}/.git], [ + srcdir_abs=`readlink -f "$srcdir"` + exclude_dir=`pwd | sed "s,^$srcdir_abs/*,,g"` + AS_IF([! grep -q "^$exclude_dir" "${srcdir}/.git/info/exclude"], + [echo "$exclude_dir" >> "${srcdir}/.git/info/exclude"])]) + echo \ "-------------------------------------------------------------------------------- @@ -151,6 +169,7 @@ AS_HELP_STRING([enable debug flags:], [${ax_enable_debug}]) AS_HELP_STRING([code coverage:], [${CODE_COVERAGE_ENABLED}]) AS_HELP_STRING([enabled sanitizers:], [${ax_enabled_sanitizers}]) +AS_HELP_STRING([pulseaudio support:], [${ax_pulse}]) To compile, run: --- a/Makefile.am 2019-02-21 15:35:12.000000000 +0100 +++ b/Makefile.am 2021-07-19 13:47:05.320995008 +0200 @@ -5,6 +5,7 @@ bin_PROGRAMS = i3status +if BUILD_MANS dist_man1_MANS = \ $(asciidoc_MANS) @@ -17,6 +18,9 @@ man/%.xml: man/%.man man/asciidoc.conf man/$(dirstamp) $(AM_V_GEN) @PATH_ASCIIDOC@ -d manpage -b docbook -f $(top_builddir)/man/asciidoc.conf -o $@ $< +else +asciidoc_MANS = +endif AM_CPPFLAGS = \ -DSYSCONFDIR="\"$(sysconfdir)\"" \ @@ -50,6 +54,7 @@ i3status.c \ src/auto_detect_format.c \ src/first_network_device.c \ + src/format_placeholders.c \ src/general.c \ src/output.c \ src/print_battery_info.c \ @@ -68,8 +73,11 @@ src/print_volume.c \ src/print_wireless_info.c \ src/print_file_contents.c \ - src/process_runs.c \ - src/pulse.c + src/process_runs.c + +if PULSE +i3status_SOURCES += src/pulse.c +endif dist_sysconf_DATA = \ i3status.conf --- a/src/print_volume.c 2019-01-23 09:03:56.000000000 +0100 +++ b/src/print_volume.c 2021-07-19 13:53:19.409302195 +0200 @@ -21,7 +21,7 @@ #include #endif -#ifdef __OpenBSD__ +#if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #include @@ -31,6 +31,8 @@ #include "i3status.h" #include "queue.h" +#define STRING_SIZE 10 + #define ALSA_VOLUME(channel) \ err = snd_mixer_selem_get_##channel##_dB_range(elem, &min, &max) || \ snd_mixer_selem_get_##channel##_dB(elem, 0, &val); \ @@ -48,30 +50,20 @@ fmt = fmt_muted; \ } -static char *apply_volume_format(const char *fmt, char *outwalk, int ivolume, const char *devicename) { - const char *walk = fmt; +static char *apply_volume_format(const char *fmt, char *buffer, int ivolume, const char *devicename) { + char string_volume[STRING_SIZE]; - for (; *walk != '\0'; walk++) { - if (*walk != '%') { - *(outwalk++) = *walk; - - } else if (BEGINS_WITH(walk + 1, "%")) { - outwalk += sprintf(outwalk, "%s", pct_mark); - walk += strlen("%"); - - } else if (BEGINS_WITH(walk + 1, "volume")) { - outwalk += sprintf(outwalk, "%d%s", ivolume, pct_mark); - walk += strlen("volume"); - - } else if (BEGINS_WITH(walk + 1, "devicename")) { - outwalk += sprintf(outwalk, "%s", devicename); - walk += strlen("devicename"); + snprintf(string_volume, STRING_SIZE, "%d%s", ivolume, pct_mark); - } else { - *(outwalk++) = '%'; - } - } - return outwalk; + placeholder_t placeholders[] = { + {.name = "%%", .value = pct_mark}, + {.name = "%volume", .value = string_volume}, + {.name = "%devicename", .value = devicename}}; + + const size_t num = sizeof(placeholders) / sizeof(placeholder_t); + buffer = format_placeholders(fmt, &placeholders[0], num); + + return buffer; } void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx) { @@ -86,7 +78,7 @@ free(instance); } -#if !defined(__DragonFly__) && !defined(__OpenBSD__) +#if HAS_PULSEAUDIO /* Try PulseAudio first */ /* If the device name has the format "pulse[:N]" where N is the @@ -119,11 +111,11 @@ /* negative result means error, stick to 0 */ if (ivolume < 0) ivolume = 0; - outwalk = apply_volume_format(muted ? fmt_muted : fmt, - outwalk, - ivolume, - description); - goto out; + buffer = apply_volume_format(muted ? fmt_muted : fmt, + buffer, + ivolume, + description); + goto out_with_format; } else if (!strcasecmp(device, "default") && pulse_initialize()) { /* no device specified or "default" set */ char description[MAX_SINK_DESCRIPTION_LEN]; @@ -136,11 +128,11 @@ START_COLOR("color_degraded"); pbval = 0; } - outwalk = apply_volume_format(muted ? fmt_muted : fmt, - outwalk, - ivolume, - description); - goto out; + buffer = apply_volume_format(muted ? fmt_muted : fmt, + buffer, + ivolume, + description); + goto out_with_format; } /* negative result or NULL description means error, fail PulseAudio attempt */ } @@ -242,13 +234,14 @@ ALSA_MUTE_SWITCH(capture) } - outwalk = apply_volume_format(fmt, outwalk, avg, mixer_name); + buffer = apply_volume_format(fmt, buffer, avg, mixer_name); snd_mixer_close(m); snd_mixer_selem_id_free(sid); + goto out_with_format; #endif -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) char *mixerpath; char defaultmixer[] = "/dev/mixer"; int mixfd, vol, devmask = 0; @@ -261,7 +254,7 @@ mixerpath = defaultmixer; if ((mixfd = open(mixerpath, O_RDWR)) < 0) { -#if defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) warn("audioio: Cannot open mixer"); #else warn("OSS: Cannot open mixer"); @@ -272,7 +265,7 @@ if (mixer_idx > 0) free(mixerpath); -#if defined(__OpenBSD__) +#if defined(__NetBSD__) || defined(__OpenBSD__) int oclass_idx = -1, master_idx = -1, master_mute_idx = -1; int master_next = AUDIO_MIXER_LAST; mixer_devinfo_t devinfo, devinfo2; @@ -327,15 +320,17 @@ 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.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; + if (vinfo.un.ord) { + START_COLOR("color_degraded"); + fmt = fmt_muted; + pbval = 0; + } } #else @@ -354,13 +349,20 @@ } #endif - outwalk = apply_volume_format(fmt, outwalk, vol & 0x7f, devicename); + buffer = apply_volume_format(fmt, buffer, vol & 0x7f, devicename); close(mixfd); + goto out_with_format; #endif out: - *outwalk = '\0'; if (!pbval) END_COLOR; OUTPUT_FULL_TEXT(buffer); + return; + +out_with_format: + if (!pbval) + END_COLOR; + OUTPUT_FULL_TEXT(buffer); + free(buffer); } --- /dev/null 2021-07-19 09:18:28.360000528 +0200 +++ b/src/format_placeholders.c 2021-07-19 13:57:16.830921169 +0200 @@ -0,0 +1,70 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + */ +// copied from i3:libi3/format_placeholders.c +#include +#include +#include +#include +#include + +#include "i3status.h" + +#ifndef CS_STARTS_WITH +#define CS_STARTS_WITH(string, needle) (strncmp((string), (needle), strlen((needle))) == 0) +#endif + +/* + * Replaces occurrences of the defined placeholders in the format string. + * + */ +char *format_placeholders(const char *format, placeholder_t *placeholders, int num) { + if (format == NULL) + return NULL; + + /* We have to first iterate over the string to see how much buffer space + * we need to allocate. */ + int buffer_len = strlen(format) + 1; + for (const char *walk = format; *walk != '\0'; walk++) { + for (int i = 0; i < num; i++) { + if (!CS_STARTS_WITH(walk, placeholders[i].name)) + continue; + + buffer_len = buffer_len - strlen(placeholders[i].name) + strlen(placeholders[i].value); + walk += strlen(placeholders[i].name) - 1; + break; + } + } + + /* Now we can parse the format string. */ + char buffer[buffer_len]; + char *outwalk = buffer; + for (const char *walk = format; *walk != '\0'; walk++) { + if (*walk != '%') { + *(outwalk++) = *walk; + continue; + } + + bool matched = false; + for (int i = 0; i < num; i++) { + if (!CS_STARTS_WITH(walk, placeholders[i].name)) { + continue; + } + + matched = true; + outwalk += sprintf(outwalk, "%s", placeholders[i].value); + walk += strlen(placeholders[i].name) - 1; + break; + } + + if (!matched) + *(outwalk++) = *walk; + } + + *outwalk = '\0'; + return sstrdup(buffer); +} --- a/include/i3status.h 2019-02-21 15:35:12.000000000 +0100 +++ b/include/i3status.h 2021-07-19 14:06:39.618462946 +0200 @@ -198,6 +199,16 @@ void reset_cursor(void); void maybe_escape_markup(char *text, char **buffer); +// copied from i3:libi3/format_placeholders.c +/* src/format_placeholders.c */ +typedef struct { + /* The placeholder to be replaced, e.g., "%title". */ + const char *name; + /* The value this placeholder should be replaced with. */ + const char *value; +} placeholder_t; +char *format_placeholders(const char *format, placeholder_t *placeholders, int num); + /* src/auto_detect_format.c */ char *auto_detect_format();