summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Franzke <benjaminfranzke@googlemail.com>2011-12-13 10:59:52 +0100
committerBenjamin Franzke <benjaminfranzke@googlemail.com>2011-12-13 16:30:37 +0100
commit1e064f8a0a3e3c5d9b8b3cf3f2faf65f4ca55b66 (patch)
tree7c76fc0869607332d5ea08556e79dcf3b98b9af1
parent0cd9f7602fe16b52cf728d4b1b52650fff338efd (diff)
downloadpa-sink-ctl-1e064f8a0a3e3c5d9b8b3cf3f2faf65f4ca55b66.tar.gz
pa-sink-ctl-1e064f8a0a3e3c5d9b8b3cf3f2faf65f4ca55b66.tar.bz2
pa-sink-ctl-1e064f8a0a3e3c5d9b8b3cf3f2faf65f4ca55b66.zip
config: Read keymap from configuration file
This needed a split of the switch that handled input before into several small callback functions.
-rw-r--r--src/Makefile.am4
-rw-r--r--src/command.c231
-rw-r--r--src/command.h33
-rw-r--r--src/config.c76
-rw-r--r--src/config.h22
-rw-r--r--src/config.ini14
-rw-r--r--src/interface.c195
-rw-r--r--src/interface.h2
8 files changed, 386 insertions, 191 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ed5d781..0d527f6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
bin_PROGRAMS = pa-sink-ctl
-pa_sink_ctl_SOURCES = interface.c config.c pa-sink-ctl.c
+pa_sink_ctl_SOURCES = interface.c command.c config.c pa-sink-ctl.c
EXTRA_pa_sink_ctl_SOURCES = unix_signal.c
if !HAVE_SIGNALFD
@@ -11,7 +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 config.h pa-sink-ctl.h sink.h unix_signal.h
+noinst_HEADERS = interface.h command.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/command.c b/src/command.c
new file mode 100644
index 0000000..0491831
--- /dev/null
+++ b/src/command.c
@@ -0,0 +1,231 @@
+/*
+ * pa-sink-ctl - NCurses based Pulseaudio control client
+ * Copyright (C) 2011 Benjamin Franzke <benjaminfranzke@googlemail.com>
+ * Copyright (C) 2010 Jan Klemkow <web2p10@wemelug.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pa-sink-ctl.h"
+#include "interface.h"
+#include "sink.h"
+#include "command.h"
+
+static int
+sink_input_len(struct context *ctx, struct sink_info *sink)
+{
+ int len = 0;
+ GList *l;
+
+ for (l = ctx->input_list; l; l = l->next) {
+ struct sink_input_info *input = l->data;
+
+ if (input->sink == sink->index)
+ len++;
+ }
+
+ return len;
+}
+
+static struct sink_input_info *
+sink_get_nth_input(struct context *ctx, struct sink_info *sink, int n)
+{
+ GList *l;
+ int i = 0;
+
+ for (l = ctx->input_list; l; l = l->next) {
+ struct sink_input_info *input = l->data;
+ if (input->sink != sink->index)
+ continue;
+ if (i++ == n)
+ return input;
+ }
+
+ return NULL;
+}
+
+static void
+up(struct context *ctx, int key)
+{
+ struct sink_info *sink = NULL;
+
+ if (ctx->chooser_input == SELECTED_SINK &&
+ ctx->chooser_sink > 0) {
+ sink = g_list_nth_data(ctx->sink_list, --ctx->chooser_sink);
+ /* autoassigment to SELECTED_SINK (=-1) if length = 0 */
+ ctx->chooser_input = sink_input_len(ctx, sink) - 1;
+ } else if (ctx->chooser_input >= 0)
+ --ctx->chooser_input;
+
+ print_sink_list(ctx);
+}
+
+static void
+down(struct context *ctx, int key)
+{
+ struct sink_info *sink;
+
+ sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
+ if (ctx->chooser_input == (sink_input_len(ctx, sink) - 1) &&
+ ctx->chooser_sink < (gint) g_list_length(ctx->sink_list)-1) {
+ ++ctx->chooser_sink;
+ ctx->chooser_input = SELECTED_SINK;
+ }
+ else if (ctx->chooser_input < (sink_input_len(ctx, sink) - 1))
+ ++ctx->chooser_input;
+ print_sink_list(ctx);
+}
+
+static void
+volume_change(struct context *ctx, gboolean volume_increment)
+{
+ struct sink_info *sink;
+ struct sink_input_info *input;
+ guint32 index;
+
+ sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
+ pa_cvolume volume;
+ pa_volume_t tmp_vol;
+ pa_operation* (*volume_set) (pa_context*, guint32, const pa_cvolume *,
+ pa_context_success_cb_t, gpointer);
+
+ if (ctx->chooser_input >= 0) {
+ input = sink_get_nth_input(ctx, sink, ctx->chooser_input);
+ index = input->index;
+ volume = (pa_cvolume) { .channels = input->channels };
+ tmp_vol = input->vol;
+ volume_set = pa_context_set_sink_input_volume;
+ } else if (ctx->chooser_input == SELECTED_SINK) {
+ index = sink->index;
+ volume = (pa_cvolume) { .channels = sink->channels };
+ tmp_vol = sink->vol;
+ volume_set = pa_context_set_sink_volume_by_index;
+ } else
+ return;
+
+ pa_cvolume_set(&volume, volume.channels, tmp_vol);
+ pa_volume_t inc = 2 * PA_VOLUME_NORM / 100;
+
+ if (volume_increment)
+ if (PA_VOLUME_NORM > tmp_vol &&
+ PA_VOLUME_NORM - tmp_vol > inc)
+ pa_cvolume_inc(&volume, inc);
+ else
+ pa_cvolume_set(&volume, volume.channels,
+ PA_VOLUME_NORM);
+ else
+ pa_cvolume_dec(&volume, inc);
+
+
+ pa_operation_unref(volume_set(ctx->context, index, &volume,
+ change_callback, ctx));
+}
+
+static void
+volume_down(struct context *ctx, int key)
+{
+ volume_change(ctx, FALSE);
+}
+
+static void
+volume_up(struct context *ctx, int key)
+{
+ volume_change(ctx, TRUE);
+}
+
+static void
+mute(struct context *ctx, int key)
+{
+ struct sink_info *sink;
+ struct sink_input_info *input;
+ guint32 index;
+ gint mute;
+
+ sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
+ pa_operation* (*mute_set) (pa_context*, guint32, int,
+ pa_context_success_cb_t, void*);
+
+ if (ctx->chooser_input >= 0) {
+ input = sink_get_nth_input(ctx, sink, ctx->chooser_input);
+ index = input->index;
+ mute = !input->mute;
+ mute_set = pa_context_set_sink_input_mute;
+ } else if (ctx->chooser_input == SELECTED_SINK) {
+ index = sink->index;
+ mute = !sink->mute;
+ mute_set = pa_context_set_sink_mute_by_index;
+ } else
+ return;
+
+ pa_operation_unref(mute_set(ctx->context, index, mute,
+ change_callback, ctx));
+}
+
+static void
+switch_sink(struct context *ctx, int key)
+{
+ struct sink_info *sink = NULL;
+ struct sink_input_info *input;
+ pa_operation *o;
+ gint i;
+
+ if (ctx->chooser_input == SELECTED_SINK)
+ return;
+ sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
+ input = sink_get_nth_input(ctx, sink, ctx->chooser_input);
+ if (g_list_length(ctx->sink_list) <= 1)
+ return;
+ if (ctx->chooser_sink < (gint) g_list_length(ctx->sink_list) - 1)
+ ctx->chooser_sink++;
+ else
+ ctx->chooser_sink = 0;
+
+ sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
+ /* chooser_input needs to be derived from $selected_index */
+ o = pa_context_move_sink_input_by_index(ctx->context,
+ input->index, sink->index,
+ change_callback, NULL);
+ pa_operation_unref(o);
+
+ /* get new chooser_input, if non, select sink as fallback */
+ ctx->chooser_input = SELECTED_SINK;
+ i = -1;
+ for (GList *l = ctx->input_list; l; l = l->next) {
+ struct sink_input_info *t = l->data;
+
+ if (t->index == input->index) {
+ ctx->chooser_input = ++i;
+ break;
+ }
+ if (t->sink == sink->index)
+ ++i;
+ }
+}
+
+static void
+quit_cmd(struct context *ctx, int key)
+{
+ quit(ctx);
+}
+
+struct command_cb_descriptor command_cbs[] = {
+ { "up", up },
+ { "down", down },
+ { "volume-down", volume_down },
+ { "volume-up", volume_up },
+ { "mute", mute },
+ { "switch", switch_sink },
+ { "quit", quit_cmd },
+ { NULL, NULL }
+};
diff --git a/src/command.h b/src/command.h
new file mode 100644
index 0000000..41bfd0d
--- /dev/null
+++ b/src/command.h
@@ -0,0 +1,33 @@
+/*
+ * pa-sink-ctl - NCurses based Pulseaudio control client
+ * Copyright (C) 2011 Benjamin Franzke <benjaminfranzke@googlemail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+struct context;
+
+typedef void (*command_cb)(struct context *ctx, int key);
+
+struct command_cb_descriptor {
+ char *command;
+ command_cb cb;
+};
+
+extern struct command_cb_descriptor command_cbs[];
+
+#endif /* COMMAND_H */
diff --git a/src/config.c b/src/config.c
index b6b2c5d..a3535d1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,9 +1,29 @@
+/*
+ * pa-sink-ctl - NCurses based Pulseaudio control client
+ * Copyright (C) 2011 Benjamin Franzke <benjaminfranzke@googlemail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include <glib.h>
#include <string.h>
#include "pa-sink-ctl.h"
#include "config.h"
+#include "command.h"
+
static int
parse_priorities(struct config *cfg)
{
@@ -76,6 +96,59 @@ read_settings(struct config *cfg)
return 0;
}
+static unsigned int
+read_int(const char *string)
+{
+ unsigned int value;
+
+ if (string[1] == 'x')
+ sscanf(string, "%x", &value);
+ else
+ sscanf(string, "%o", &value);
+
+ return value;
+}
+
+static int
+read_input_mappings(struct config *cfg)
+{
+ int i, j;
+ gchar **list;
+ GError *error = NULL;
+
+ cfg->keymap = g_hash_table_new(g_direct_hash, g_direct_equal);
+ if (cfg->keymap == NULL)
+ return -1;
+
+ for (i = 0; command_cbs[i].command; ++i) {
+ char *attrib = command_cbs[i].command;
+
+ error = NULL;
+ list = g_key_file_get_string_list(cfg->keyfile, "input",
+ attrib, NULL, &error);
+ if (error) {
+ g_printerr("error reading keymap for '%s': %s\n",
+ attrib, error->message);
+ return -1;
+ }
+
+ for (j = 0; list[j]; ++j) {
+ int key;
+ if (strlen(list[j]) == 0)
+ continue;
+
+ if (strlen(list[j]) > 2 && list[j][0] == '0') {
+ key = read_int(list[j]);
+ } else
+ key = list[j][0];
+ g_hash_table_insert(cfg->keymap, GINT_TO_POINTER(key),
+ &command_cbs[i]);
+ }
+ }
+
+ return 0;
+}
+
int
config_init(struct config *cfg)
{
@@ -109,6 +182,8 @@ config_init(struct config *cfg)
if (parse_priorities(cfg) < 0)
return -1;
+ if (read_input_mappings(cfg) < 0)
+ return -1;
return 0;
}
@@ -118,6 +193,7 @@ config_uninit(struct config *cfg)
{
g_list_free_full(cfg->priorities, destroy_priority);
+ g_hash_table_destroy(cfg->keymap);
g_strfreev(cfg->name_props);
g_key_file_free(cfg->keyfile);
}
diff --git a/src/config.h b/src/config.h
index 9cf320e..62fac07 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,11 +1,33 @@
+/*
+ * pa-sink-ctl - NCurses based Pulseaudio control client
+ * Copyright (C) 2011 Benjamin Franzke <benjaminfranzke@googlemail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#ifndef CONFIG_H
#define CONFIG_H
+#include <glib.h>
+
struct config {
GKeyFile *keyfile;
GList *priorities;
gchar **name_props;
+
+ GHashTable *keymap;
};
struct priority {
diff --git a/src/config.ini b/src/config.ini
index ecedead..cd658f1 100644
--- a/src/config.ini
+++ b/src/config.ini
@@ -3,6 +3,20 @@
## List of properties to try to use as sink names, or sink name itself if empty.
name-properties = device.description; device.product.name
+[input]
+## Key starting with a 0 and length > 1 are encoded in octal or hexadecimal,
+## depending on second character being an 'x'.
+## See ncurses.h for other presudo-character tokens
+up = 0403:k;w
+down = 0402;j;s
+volume-down = 0404;h;a
+volume-up = 0405;l;d
+mute = m;x;M;
+# switch sink input using return, tab or space
+switch = 0xa;0x9; ;
+# quit on q or escape
+quit = q;033
+
## 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)
diff --git a/src/interface.c b/src/interface.c
index 1e3d41f..8d9819a 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -27,6 +27,7 @@
#include "interface.h"
#include "sink.h"
+#include "command.h"
#include "pa-sink-ctl.h"
#ifdef HAVE_SIGNALFD
@@ -36,43 +37,6 @@
#include "unix_signal.h"
#endif
-#define H_MSG_BOX 3
-
-#define SELECTED_SINK -1
-
-static int
-sink_input_len(struct context *ctx, struct sink_info *sink)
-{
- int len = 0;
- GList *l;
-
- for (l = ctx->input_list; l; l = l->next) {
- struct sink_input_info *input = l->data;
-
- if (input->sink == sink->index)
- len++;
- }
-
- return len;
-}
-
-static struct sink_input_info *
-sink_get_nth_input(struct context *ctx, struct sink_info *sink, int n)
-{
- GList *l;
- int i = 0;
-
- for (l = ctx->input_list; l; l = l->next) {
- struct sink_input_info *input = l->data;
- if (input->sink != sink->index)
- continue;
- if (i++ == n)
- return input;
- }
-
- return NULL;
-}
-
static gboolean
interface_resize(gpointer data)
{
@@ -218,164 +182,17 @@ static gboolean
interface_get_input(GIOChannel *source, GIOCondition condition, gpointer data)
{
struct context *ctx = data;
+ struct command_cb_descriptor *cmd;
gint c;
- gboolean volume_increment = TRUE;
- struct sink_info *sink = NULL;
- guint32 index;
- pa_operation *o;
if (!ctx->context_ready)
return TRUE;
c = wgetch(ctx->menu_win);
- switch (c) {
- case 'k':
- case 'w':
- case KEY_UP:
- if (ctx->chooser_input == SELECTED_SINK &&
- ctx->chooser_sink > 0) {
- sink = g_list_nth_data(ctx->sink_list,
- --ctx->chooser_sink);
- /* autoassigment to SELECTED_SINK (=-1) if length = 0 */
- ctx->chooser_input = sink_input_len(ctx, sink) - 1;
- }
-
- else if (ctx->chooser_input >= 0)
- --ctx->chooser_input;
- print_sink_list(ctx);
- break;
-
- case 'j':
- case 's':
- case KEY_DOWN:
- sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
- if (ctx->chooser_input == (sink_input_len(ctx, sink) - 1) &&
- ctx->chooser_sink < (gint)g_list_length(ctx->sink_list)-1) {
- ++ctx->chooser_sink;
- ctx->chooser_input = SELECTED_SINK;
- }
- else if (ctx->chooser_input < (sink_input_len(ctx, sink) - 1))
- ++ctx->chooser_input;
- print_sink_list(ctx);
- break;
-
- case 'h':
- case 'a':
- case KEY_LEFT:
- volume_increment = FALSE;
- /* fall through */
- case 'l':
- case 'd':
- case KEY_RIGHT:
- sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
- pa_cvolume volume;
- pa_volume_t tmp_vol;
- pa_operation* (*volume_set) (pa_context*, guint32,
- const pa_cvolume *,
- pa_context_success_cb_t, gpointer);
-
- if (ctx->chooser_input >= 0) {
- struct sink_input_info *input =
- sink_get_nth_input(ctx, sink,
- ctx->chooser_input);
- index = input->index;
- volume = (pa_cvolume) {.channels = input->channels};
- tmp_vol = input->vol;
- volume_set = pa_context_set_sink_input_volume;
- } else if (ctx->chooser_input == SELECTED_SINK) {
- index = sink->index;
- volume = (pa_cvolume) {.channels = sink->channels};
- tmp_vol = sink->vol;
- volume_set = pa_context_set_sink_volume_by_index;
- } else
- break;
-
- pa_cvolume_set(&volume, volume.channels, tmp_vol);
- pa_volume_t inc = 2 * PA_VOLUME_NORM / 100;
-
- if (volume_increment)
- if (PA_VOLUME_NORM > tmp_vol &&
- PA_VOLUME_NORM - tmp_vol > inc)
- pa_cvolume_inc(&volume, inc);
- else
- pa_cvolume_set(&volume, volume.channels,
- PA_VOLUME_NORM);
- else
- pa_cvolume_dec(&volume, inc);
-
-
- pa_operation_unref(volume_set(ctx->context, index, &volume,
- change_callback, ctx));
- break;
- case 'm':
- case 'x':
- case 'M':
- sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
- gint mute;
- pa_operation* (*mute_set) (pa_context*, guint32, int,
- pa_context_success_cb_t, void*);
-
- if (ctx->chooser_input >= 0) {
- struct sink_input_info *input =
- sink_get_nth_input(ctx, sink,
- ctx->chooser_input);
- index = input->index;
- mute = !input->mute;
- mute_set = pa_context_set_sink_input_mute;
- } else if (ctx->chooser_input == SELECTED_SINK) {
- index = sink->index;
- mute = !sink->mute;
- mute_set = pa_context_set_sink_mute_by_index;
- } else
- break;
-
- pa_operation_unref(mute_set(ctx->context, index, mute,
- change_callback, ctx));
- break;
-
- case '\n':
- case '\t':
- case ' ':
- if (ctx->chooser_input == SELECTED_SINK)
- break;
- sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
- struct sink_input_info *input =
- sink_get_nth_input(ctx, sink, ctx->chooser_input);
- if (g_list_length(ctx->sink_list) <= 1)
- break;
- if (ctx->chooser_sink < (gint)g_list_length(ctx->sink_list) - 1)
- ctx->chooser_sink++;
- else
- ctx->chooser_sink = 0;
-
- sink = g_list_nth_data(ctx->sink_list, ctx->chooser_sink);
- /* chooser_input needs to be derived from $selected_index */
- o = pa_context_move_sink_input_by_index(ctx->context,
- input->index,
- sink->index,
- change_callback, NULL);
- pa_operation_unref(o);
-
- /* get new chooser_input, if non, select sink as fallback */
- ctx->chooser_input = SELECTED_SINK;
- gint i = -1;
- for (GList *l = ctx->input_list; l; l = l->next) {
- struct sink_input_info *t = l->data;
-
- if (t->index == input->index) {
- ctx->chooser_input = ++i;
- break;
- }
- if (t->sink == sink->index)
- ++i;
- }
- break;
-
- case 'q':
- default:
- quit(ctx);
- break;
- }
+
+ cmd = g_hash_table_lookup(ctx->config.keymap, GINT_TO_POINTER(c));
+ if (cmd)
+ cmd->cb(ctx, c);
return TRUE;
}
diff --git a/src/interface.h b/src/interface.h
index 1e884d8..da99612 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -23,6 +23,8 @@
#include <glib.h>
#include <pulse/pulseaudio.h>
+#define SELECTED_SINK -1
+#define H_MSG_BOX 3
struct context;