summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Franzke <benjaminfranzke@googlemail.com>2011-12-12 21:11:19 +0100
committerBenjamin Franzke <benjaminfranzke@googlemail.com>2011-12-12 21:31:59 +0100
commitdacdcf2516ef4520b0bb32fa34837381f65faf30 (patch)
treee58a517a421e6daba2934e6ab69d2f19ce914135
parent3c9e3b69c425ff5bad208f2c52c3d4c3aaeaf2f9 (diff)
downloadpa-sink-ctl-dacdcf2516ef4520b0bb32fa34837381f65faf30.tar.gz
pa-sink-ctl-dacdcf2516ef4520b0bb32fa34837381f65faf30.tar.bz2
pa-sink-ctl-dacdcf2516ef4520b0bb32fa34837381f65faf30.zip
Implement a priority based sink order assignment
This is handsome when using udev based device discovery.
-rw-r--r--src/Makefile.am7
-rw-r--r--src/config.c93
-rw-r--r--src/config.h21
-rw-r--r--src/config.ini16
-rw-r--r--src/pa-sink-ctl.c50
-rw-r--r--src/pa-sink-ctl.h10
-rw-r--r--src/sink.h1
7 files changed, 189 insertions, 9 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3fc41ad..ed5d781 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
bin_PROGRAMS = pa-sink-ctl
-pa_sink_ctl_SOURCES = interface.c pa-sink-ctl.c
+pa_sink_ctl_SOURCES = interface.c config.c pa-sink-ctl.c
EXTRA_pa_sink_ctl_SOURCES = unix_signal.c
if !HAVE_SIGNALFD
@@ -11,4 +11,7 @@ AM_CPPFLAGS = $(PULSE_CFLAGS) $(PULSE_MAINLOOP_CFLAGS) $(GLIB_CFLAGS) \
-include $(top_builddir)/config.h
pa_sink_ctl_LDADD = $(GLIB_LIBS) $(PULSE_LIBS) $(PULSE_MAINLOOP_LIBS) $(CURSES_LIBS)
-noinst_HEADERS = interface.h pa-sink-ctl.h sink.h unix_signal.h
+noinst_HEADERS = interface.h config.h pa-sink-ctl.h sink.h unix_signal.h
+
+pa_sink_ctl_xdgdir = $(sysconfdir)/xdg/pa-sink-ctl
+dist_pa_sink_ctl_xdg_DATA = config.ini
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..e24ece0
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,93 @@
+#include <glib.h>
+#include <string.h>
+
+#include "pa-sink-ctl.h"
+#include "config.h"
+
+static int
+parse_priorities(struct config *cfg)
+{
+ gchar **groups;
+ struct priority p;
+ int i;
+ gsize length;
+ GError *error = NULL;
+
+ groups = g_key_file_get_groups(cfg->keyfile, &length);
+
+ for (i = 0; i < length; ++i) {
+ if (strncmp(groups[i], "priority", 8) != 0)
+ continue;
+
+ memset(&p, 0, sizeof p);
+
+ p.match = g_key_file_get_value(cfg->keyfile, groups[i],
+ "match", &error);
+ if (error)
+ goto error;
+ p.value = g_key_file_get_value(cfg->keyfile, groups[i],
+ "value", &error);
+ if (error)
+ goto error;
+ p.priority = g_key_file_get_integer(cfg->keyfile, groups[i],
+ "priority", &error);
+ if (error)
+ goto error;
+
+ list_append_struct(cfg->priorities, p);
+ }
+
+ return 0;
+
+error:
+ if (p.value)
+ g_free(p.value);
+ if (p.match)
+ g_free(p.match);
+
+ g_printerr("Failed to read property in prioritiy group '%s': %s\n",
+ groups[i], error->message);
+
+ return -1;
+}
+
+static void
+destroy_priority(gpointer data)
+{
+ struct priority *p = data;
+
+ g_free(p->value);
+ g_free(p->match);
+ g_free(p);
+}
+
+int
+config_init(struct config *cfg)
+{
+ GError *error = NULL;
+
+ memset(cfg, 0, sizeof *cfg);
+ cfg->keyfile = g_key_file_new();
+ cfg->priorities = NULL;
+
+ if (!g_key_file_load_from_data_dirs(cfg->keyfile,
+ "pa-sink-ctl/config.ini",
+ NULL, G_KEY_FILE_NONE, &error)
+ && error) {
+ g_printerr("Failed to open config file: %s\n", error->message);
+ return -1;
+ }
+
+ if (parse_priorities(cfg) < 0)
+ return -1;
+
+ return 0;
+}
+
+void
+config_uninit(struct config *cfg)
+{
+ g_list_free_full(cfg->priorities, destroy_priority);
+
+ g_key_file_free(cfg->keyfile);
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..0bdbef2
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,21 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+struct config {
+ GKeyFile *keyfile;
+
+ GList *priorities;
+};
+
+struct priority {
+ gchar *match, *value;
+ gint priority;
+};
+
+int
+config_init(struct config *cfg);
+
+void
+config_uninit(struct config *cfg);
+
+#endif /* CONFIG_H */
diff --git a/src/config.ini b/src/config.ini
new file mode 100644
index 0000000..3d4bc39
--- /dev/null
+++ b/src/config.ini
@@ -0,0 +1,16 @@
+[pa-sink-ctl]
+
+## Priority groups can be used to set the display
+## order for sinks. Priority groups start with "priority".
+## "match": a pulseaudio property to match against (use pacmd list-sinks)
+## "value": the string to compare with
+## "priority": ..to assign to matched sink (default 0)
+#[priority 0]
+#match=device.product.name
+#value=SBx00 Azalia (Intel HDA)
+#priority=2
+#
+#[priority 1]
+#match=device.product.name
+#value=USB Headset
+#priority=1
diff --git a/src/pa-sink-ctl.c b/src/pa-sink-ctl.c
index 799e0f3..df51b5e 100644
--- a/src/pa-sink-ctl.c
+++ b/src/pa-sink-ctl.c
@@ -23,14 +23,9 @@
#include "sink.h"
#include "interface.h"
+#include "config.h"
#include "pa-sink-ctl.h"
-#define list_append_struct(list, data) \
- do { \
- (list) = g_list_append((list), \
- g_memdup(&(data), sizeof(data))); \
- } while (0)
-
static sink_input_info *
find_sink_input_by_idx(struct context *ctx, gint idx)
{
@@ -101,6 +96,41 @@ sink_input_info_cb(pa_context *c, const pa_sink_input_info *i,
list_append_struct(ctx->input_list, sink_input);
}
+static int
+get_sink_priority(struct context *ctx, const pa_sink_info *sink_info)
+{
+ GList *l;
+ const char *value;
+
+ for (l = ctx->config.priorities; l; l = l->next) {
+ struct priority *p = l->data;
+
+ value = pa_proplist_gets(sink_info->proplist, p->match);
+
+ if (g_strcmp0(value, p->value) == 0)
+ return p->priority;
+ }
+
+ return 0;
+}
+
+static void
+add_sink(struct context *ctx, sink_info *new)
+{
+ GList *l, *pos = NULL;
+
+ for (l = ctx->sink_list; l; l = l->next) {
+ sink_info *sink = l->data;
+
+ if (new->priority > sink->priority) {
+ pos = l;
+ break;
+ }
+ }
+ ctx->sink_list = g_list_insert_before(ctx->sink_list, pos,
+ g_memdup(new, sizeof *new));
+}
+
static void
sink_info_cb(pa_context *c, const pa_sink_info *i,
gint is_last, gpointer userdata)
@@ -132,13 +162,14 @@ sink_info_cb(pa_context *c, const pa_sink_info *i,
g_strdup(pa_proplist_gets(i->proplist,
"device.product.name")) :
NULL,
+ .priority = get_sink_priority(ctx, i),
};
sink_info *inlist = find_sink_by_idx(ctx, i->index);
if (inlist)
*inlist = sink;
else
- list_append_struct(ctx->sink_list, sink);
+ add_sink(ctx, &sink);
}
static void
@@ -275,6 +306,9 @@ main(int argc, char** argv)
ctx->context_ready = FALSE;
ctx->loop = g_main_loop_new(NULL, FALSE);
+
+ if (config_init(&ctx->config) < 0)
+ return -1;
interface_init(ctx);
@@ -310,6 +344,8 @@ main(int argc, char** argv)
pa_glib_mainloop_free(m);
g_main_loop_unref(ctx->loop);
+ config_uninit(&ctx->config);
+
return 0;
}
diff --git a/src/pa-sink-ctl.h b/src/pa-sink-ctl.h
index d68401a..d8cd5bb 100644
--- a/src/pa-sink-ctl.h
+++ b/src/pa-sink-ctl.h
@@ -24,6 +24,8 @@
#include <pulse/pulseaudio.h>
#include <ncurses.h>
+#include "config.h"
+
struct context {
pa_context *context;
pa_operation *op;
@@ -49,6 +51,8 @@ struct context {
GList *input_list;
gchar *status;
+
+ struct config config;
};
void
@@ -57,4 +61,10 @@ quit(struct context *ctx);
void
change_callback(pa_context* c, gint success, gpointer);
+#define list_append_struct(list, data) \
+ do { \
+ (list) = g_list_append((list), \
+ g_memdup(&(data), sizeof(data))); \
+ } while (0)
+
#endif
diff --git a/src/sink.h b/src/sink.h
index 65c23b5..f26d00f 100644
--- a/src/sink.h
+++ b/src/sink.h
@@ -30,6 +30,7 @@ typedef struct _sink_info {
gint mute;
guint8 channels;
pa_volume_t vol;
+ gint priority;
} sink_info;
typedef struct _sink_input_info {