summaryrefslogtreecommitdiff
path: root/src/pulse.c
diff options
context:
space:
mode:
authorDenton Liu <liu.denton+github@gmail.com>2019-01-22 23:45:51 -0800
committerMichael Stapelberg <stapelberg@users.noreply.github.com>2019-01-23 08:45:51 +0100
commit7efbeeaf6ce9232f7479f76c1c79ff73c0db49e4 (patch)
tree6782b5540b224b2fb1fc5e7387276f92215adf81 /src/pulse.c
parentbe0be599d9a5ebe5685c4a0e83ae4c70ebd0cb96 (diff)
Implement %devicename specifier for volume module (#325)
This commit implements the %devicename specifier for the volume module for both PulseAudio and ALSA. This way, i3status will be able to display the specific device that corresponds to the volume indicator. Note that this is not implemented for the OSS API but is left in a state where someone can pick it up for the future.
Diffstat (limited to 'src/pulse.c')
-rw-r--r--src/pulse.c112
1 files changed, 84 insertions, 28 deletions
diff --git a/src/pulse.c b/src/pulse.c
index 66e6a3d..9e278c5 100644
--- a/src/pulse.c
+++ b/src/pulse.c
@@ -10,13 +10,14 @@
#define APP_NAME "i3status"
#define APP_ID "org.i3wm"
-typedef struct indexed_volume_s {
+typedef struct index_info_s {
char *name;
uint32_t idx;
int volume;
- TAILQ_ENTRY(indexed_volume_s)
+ char description[MAX_SINK_DESCRIPTION_LEN];
+ TAILQ_ENTRY(index_info_s)
entries;
-} indexed_volume_t;
+} index_info_t;
static pa_threaded_mainloop *main_loop = NULL;
static pa_context *context = NULL;
@@ -24,9 +25,9 @@ static pa_mainloop_api *api = NULL;
static bool context_ready = false;
static bool mainloop_thread_running = false;
static uint32_t default_sink_idx = DEFAULT_SINK_INDEX;
-TAILQ_HEAD(tailhead, indexed_volume_s)
-cached_volume =
- TAILQ_HEAD_INITIALIZER(cached_volume);
+TAILQ_HEAD(tailhead, index_info_s)
+cached_info =
+ TAILQ_HEAD_INITIALIZER(cached_info);
static pthread_mutex_t pulse_mutex = PTHREAD_MUTEX_INITIALIZER;
static void pulseaudio_error_log(pa_context *c) {
@@ -45,13 +46,20 @@ static bool pulseaudio_free_operation(pa_context *c, pa_operation *o) {
}
/*
- * save the volume for the specified sink index
+ * save the info for the specified sink index
* returning true if the value was changed
*/
-static bool save_volume(uint32_t sink_idx, int new_volume, const char *name) {
+static bool save_info(uint32_t sink_idx, int new_volume, const char *new_description, const char *name) {
pthread_mutex_lock(&pulse_mutex);
- indexed_volume_t *entry;
- TAILQ_FOREACH(entry, &cached_volume, entries) {
+ index_info_t *entry;
+
+ /* if this is NULL, gracefully handle and replace with empty-string */
+ if (!new_description) {
+ new_description = "";
+ fprintf(stderr, "i3status: PulseAudio: NULL new_description provided\n");
+ }
+
+ TAILQ_FOREACH(entry, &cached_info, entries) {
if (name) {
if (!entry->name || strcmp(entry->name, name)) {
continue;
@@ -61,16 +69,30 @@ static bool save_volume(uint32_t sink_idx, int new_volume, const char *name) {
continue;
}
}
- const bool changed = (new_volume != entry->volume);
- entry->volume = new_volume;
+
+ bool changed = false;
+
+ if (new_volume != entry->volume) {
+ entry->volume = new_volume;
+ changed = true;
+ }
+
+ if (strncmp(entry->description, new_description, sizeof(entry->description))) {
+ strncpy(entry->description, new_description, sizeof(entry->description) - 1);
+ entry->description[sizeof(entry->description) - 1] = '\0';
+ changed = true;
+ }
+
pthread_mutex_unlock(&pulse_mutex);
return changed;
}
/* index not found, store it */
entry = malloc(sizeof(*entry));
- TAILQ_INSERT_HEAD(&cached_volume, entry, entries);
+ TAILQ_INSERT_HEAD(&cached_info, entry, entries);
entry->idx = sink_idx;
entry->volume = new_volume;
+ strncpy(entry->description, new_description, sizeof(entry->description) - 1);
+ entry->description[sizeof(entry->description) - 1] = '\0';
if (name) {
entry->name = malloc(strlen(name) + 1);
strcpy(entry->name, name);
@@ -81,10 +103,10 @@ static bool save_volume(uint32_t sink_idx, int new_volume, const char *name) {
return true;
}
-static void store_volume_from_sink_cb(pa_context *c,
- const pa_sink_info *info,
- int eol,
- void *userdata) {
+static void store_info_from_sink_cb(pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata) {
if (eol < 0) {
if (pa_context_errno(c) == PA_ERR_NOENTITY)
return;
@@ -104,9 +126,9 @@ static void store_volume_from_sink_cb(pa_context *c,
* DEFAULT_SINK_INDEX as the index, and another with its proper value
* (using bitwise OR to avoid early-out logic) */
if ((info->index == default_sink_idx &&
- save_volume(DEFAULT_SINK_INDEX, composed_volume, NULL)) |
- save_volume(info->index, composed_volume, info->name)) {
- /* if the volume or mute flag changed, wake the main thread */
+ save_info(DEFAULT_SINK_INDEX, composed_volume, info->description, NULL)) |
+ save_info(info->index, composed_volume, info->description, info->name)) {
+ /* if the volume, mute flag or description changed, wake the main thread */
pthread_kill(main_thread, SIGUSR1);
}
}
@@ -116,10 +138,10 @@ static void get_sink_info(pa_context *c, uint32_t idx, const char *name) {
if (name || idx == DEFAULT_SINK_INDEX) {
o = pa_context_get_sink_info_by_name(
- c, name ? name : "@DEFAULT_SINK@", store_volume_from_sink_cb, NULL);
+ c, name ? name : "@DEFAULT_SINK@", store_info_from_sink_cb, NULL);
} else {
o = pa_context_get_sink_info_by_index(
- c, idx, store_volume_from_sink_cb, NULL);
+ c, idx, store_info_from_sink_cb, NULL);
}
if (o) {
pulseaudio_free_operation(c, o);
@@ -134,7 +156,7 @@ static void store_default_sink_cb(pa_context *c,
if (default_sink_idx != i->index) {
/* default sink changed? */
default_sink_idx = i->index;
- store_volume_from_sink_cb(c, i, eol, userdata);
+ store_info_from_sink_cb(c, i, eol, userdata);
}
}
}
@@ -210,8 +232,8 @@ int volume_pulseaudio(uint32_t sink_idx, const char *sink_name) {
return -1;
pthread_mutex_lock(&pulse_mutex);
- const indexed_volume_t *entry;
- TAILQ_FOREACH(entry, &cached_volume, entries) {
+ const index_info_t *entry;
+ TAILQ_FOREACH(entry, &cached_info, entries) {
if (sink_name) {
if (!entry->name || strcmp(entry->name, sink_name)) {
continue;
@@ -226,9 +248,9 @@ int volume_pulseaudio(uint32_t sink_idx, const char *sink_name) {
return vol;
}
pthread_mutex_unlock(&pulse_mutex);
- /* first time requires a prime callback call because we only get
- * updates when the volume actually changes, but we need it to
- * be correct even if it never changes */
+ /* first time requires a prime callback call because we only get updates
+ * when the description or volume actually changes, but we need it to be
+ * correct even if it never changes */
pa_threaded_mainloop_lock(main_loop);
get_sink_info(context, sink_idx, sink_name);
pa_threaded_mainloop_unlock(main_loop);
@@ -236,6 +258,40 @@ int volume_pulseaudio(uint32_t sink_idx, const char *sink_name) {
return 0;
}
+bool description_pulseaudio(uint32_t sink_idx, const char *sink_name, char buffer[MAX_SINK_DESCRIPTION_LEN]) {
+ if (!context_ready || default_sink_idx == DEFAULT_SINK_INDEX) {
+ return false;
+ }
+
+ pthread_mutex_lock(&pulse_mutex);
+ const index_info_t *entry;
+ TAILQ_FOREACH(entry, &cached_info, entries) {
+ if (sink_name) {
+ if (!entry->name || strcmp(entry->name, sink_name)) {
+ continue;
+ }
+ } else {
+ if (entry->idx != sink_idx) {
+ continue;
+ }
+ }
+ strncpy(buffer, entry->description, sizeof(entry->description) - 1);
+ pthread_mutex_unlock(&pulse_mutex);
+ buffer[sizeof(entry->description) - 1] = '\0';
+ return true;
+ }
+ pthread_mutex_unlock(&pulse_mutex);
+ /* first time requires a prime callback call because we only get updates
+ * when the description or volume actually changes, but we need it to be
+ * correct even if it never changes */
+ pa_threaded_mainloop_lock(main_loop);
+ get_sink_info(context, sink_idx, sink_name);
+ pa_threaded_mainloop_unlock(main_loop);
+ /* show empty string while we don't have this information */
+ buffer[0] = '\0';
+ return true;
+}
+
/*
* detect and, if necessary, initialize the PulseAudio API
*/