[alsa-devel] [PATCH - amixer] listen to D-Bus for volume-up, volume-down and mute signals.

Łukasz Stelmach stlman at poczta.fm
Mon Aug 3 12:43:21 CEST 2009


The patch introduces D-Bus support in amixer. With --dbus option given amixer
works continiously (like with the --stdin) waiting for
org.freedesktop.Hal.Device.Condition with the first argument "ButtonPressed"
and the second being one of: "volume-up", "volume-down" and "mute". These
are emmited by the HAL input helper. A single amixer process can controll
one mixer item. There are options to choose a specific item to controll,
an input device to listen and a single adjustment step size.

Signed-off-by: Łukasz Stelmach <stlman at poczta.fm>
---
 amixer/Makefile.am |    3 +-
 amixer/amixer.1    |   18 ++++++
 amixer/amixer.c    |  171 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure.in       |   12 ++++
 4 files changed, 203 insertions(+), 1 deletions(-)

diff --git a/amixer/Makefile.am b/amixer/Makefile.am
index fcd0e81..fa951a6 100644
--- a/amixer/Makefile.am
+++ b/amixer/Makefile.am
@@ -1,5 +1,6 @@
 INCLUDES = -I$(top_srcdir)/include
-LDADD = -lm
+LDADD = -lm @DBUS_LIBS@
+CFLAGS += @DBUS_CFLAGS@
 # LDFLAGS = -static
 # CFLAGS += -g -Wall
 
diff --git a/amixer/amixer.1 b/amixer/amixer.1
index b1ac323..bc160be 100644
--- a/amixer/amixer.1
+++ b/amixer/amixer.1
@@ -88,6 +88,24 @@ Select the card number to control. The device name created from this
 parameter has syntax 'hw:N' where N is specified card number.
 
 .TP
+\fI\-\-dbus[=\fISCONTROL\fP]
+
+Connect to system D-Bus and wait for \fIorg.freedesktop.Hal.Device.Condition\fP
+signals with ButtonPressed as the first argument and one of: \fIvolume-up\fP,
+\fIvolume-down\fP or \fImute\fP as the second one. Control the selected
+\fISCONTROL\fP item.  The default item is 'Master,0'.
+
+.TP
+\fI\-\-dbus-path=<\fIUDI\fP>
+
+Choose a particular input device that emits control events.
+
+.TP
+\fI\-\-dbus-step=<\fIN\fP>
+
+Set an amount by wich to increase or decrease volume upon event.
+
+.TP
 \fI\-D\fP device
 
 Select the device name to control. The default control name is 'default'.
diff --git a/amixer/amixer.c b/amixer/amixer.c
index 9620721..31de674 100644
--- a/amixer/amixer.c
+++ b/amixer/amixer.c
@@ -18,6 +18,8 @@
  *
  */
 
+#include "aconfig.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -31,6 +33,10 @@
 #include <sys/poll.h>
 #include "amixer.h"
 
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+#endif /* HAVE_DBUS */
+
 #define LEVEL_BASIC		(1<<0)
 #define LEVEL_INACTIVE		(1<<1)
 #define LEVEL_ID		(1<<2)
@@ -43,6 +49,12 @@ static int ignore_error = 0;
 static struct snd_mixer_selem_regopt smixer_options;
 static char card[64] = "default";
 
+#ifdef HAVE_DBUS
+static char dbus_sctl[64] = "Master,0";
+static char *dbus_path = NULL;
+static char dbus_step[8] = "1";
+#endif /* HAVE_DBUS */
+
 static void error(const char *fmt,...)
 {
 	va_list va;
@@ -60,6 +72,11 @@ static int help(void)
 	printf("\nAvailable options:\n");
 	printf("  -h,--help       this help\n");
 	printf("  -c,--card N     select the card\n");
+#ifdef HAVE_DBUS
+	printf("  --dbus[=sID]    Connect to the system D-Bus and wait for volume messages\n");
+	printf("  --dbus-path UDI Choose a particular input device (udi)\n");
+	printf("  --dbus-step N   Set the step size (default: 1)\n");
+#endif /* HAVE_DBUS */
 	printf("  -D,--device N   select the device, default '%s'\n", card);
 	printf("  -d,--debug      debug mode\n");
 	printf("  -n,--nocheck    do not perform range checking\n");
@@ -1887,15 +1904,147 @@ static int exec_stdin(void)
 	return 0;
 }
 
+#ifdef HAVE_DBUS
+
+#define OPT_DBUS 0x81
+#define OPT_DBUS_PATH 0x82
+#define OPT_DBUS_STEP 0x83
+
+void* get_dbus_arg(DBusMessageIter* args, int type, void** out) {
+	if (type != dbus_message_iter_get_arg_type(args))
+		return NULL;
+	dbus_message_iter_get_basic(args, out);
+	dbus_message_iter_next(args);
+	return *out;
+}
+
+int exec_dbus(void)
+{
+	char *sargv[MAX_ARGS];
+	int sargc;
+	const char* s;
+	int stepl;
+	DBusError error;
+	DBusMessage* msg;
+	DBusConnection *conn;
+	DBusMessageIter args;
+
+	stepl = strlen(dbus_step);
+	dbus_step[stepl+1] = '\0';
+
+	dbus_error_init (&error);
+	conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+
+	if(!conn) {
+		fprintf(stderr, "dbus:%s: %s\n",
+			error.name, error.message);
+		return 1;
+	}
+
+	dbus_bus_add_match(conn,
+		"type='signal',"
+		"interface='org.freedesktop.Hal.Device',"
+		"member='Condition'",
+		&error);
+	/* The first message is a NameOwnerChanged signal, ignore it. */
+	msg = dbus_connection_pop_message(conn);
+	dbus_message_unref(msg);
+
+	if(dbus_error_is_set(&error)) {
+		fprintf(stderr, "dbus:match error:%s\n",
+			error.message);
+		return 1;
+	}
+
+	while (1) {
+		sargv[0] = dbus_sctl; sargc = 1;
+
+		if (!(msg = dbus_connection_pop_message(conn))) {
+			dbus_connection_read_write(conn, -1);
+			continue;
+		}
+
+		s = dbus_message_get_interface(msg);
+		if(strncmp("org.freedesktop.Hal.Device", s, 26)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:unsupported interface:%s\n", s);
+			goto badmessage;
+		}
+
+		if(dbus_path &&
+		    (s=dbus_message_get_path(msg)) &&
+		    strncmp(dbus_path, s, strlen(s))) {
+			if(debugflag)
+				fprintf(stderr, "dbus:not the selected udi:%s\n", s);
+			goto badmessage;
+		}
+
+		if(!dbus_message_iter_init(msg, &args)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:message has no arguments\n");
+			goto badmessage;
+		}
+
+		if (strncmp(get_dbus_arg(&args, DBUS_TYPE_STRING, (void*)&s), "ButtonPressed", 13)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:not a ButtonPressed event\n");
+			goto badmessage;
+		}
+
+		if (!get_dbus_arg(&args, DBUS_TYPE_STRING, (void*)&s)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:invalid argument type\n");
+			goto badmessage;
+		}
+
+		/* static int sset(unsigned int argc, char *argv[], int roflag, int keep_handle) */
+		if (!strncmp(s, "volume-up", 9)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:volume-up\n");
+			dbus_step[stepl]='+';
+			sargv[1] = dbus_step;
+		} else if (!strncmp(s, "volume-down", 11)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:volume-down\n");
+			dbus_step[stepl]='-';
+			sargv[1] = dbus_step;
+		} else if (!strncmp(s, "mute", 4)) {
+			if(debugflag)
+				fprintf(stderr, "dbus:mute (toggle)\n");
+			sargv[1] = "toggle";
+		} else {
+			if(debugflag)
+				fprintf(stderr, "dbus:invalid argument value\n");
+			goto badmessage;
+		}
+		sset(++sargc, sargv, 0, 1);
+badmessage:
+		dbus_message_unref(msg);
+	}
+
+	if(conn)
+		dbus_connection_unref(conn);
+	return 0;
+}
+#endif /* HAVE_DBUS */
 
 int main(int argc, char *argv[])
 {
 	int morehelp, level = 0;
 	int read_stdin = 0;
+#ifdef HAVE_DBUS
+	int read_dbus = 0;
+	int t;
+#endif /* HAVE_DBUS */
 	static const struct option long_option[] =
 	{
 		{"help", 0, NULL, 'h'},
 		{"card", 1, NULL, 'c'},
+#ifdef HAVE_DBUS
+		{"dbus", optional_argument, NULL, OPT_DBUS},
+		{"dbus-path", 1, NULL, OPT_DBUS_PATH},
+		{"dbus-step", 1, NULL, OPT_DBUS_STEP},
+#endif /* HAVE_DBUS */
 		{"device", 1, NULL, 'D'},
 		{"quiet", 0, NULL, 'q'},
 		{"inactive", 0, NULL, 'i'},
@@ -1964,6 +2113,24 @@ int main(int argc, char *argv[])
 		case 's':
 			read_stdin = 1;
 			break;
+#ifdef HAVE_DBUS
+		case OPT_DBUS:
+			if(optarg) {
+				strncpy(dbus_sctl, optarg, sizeof(dbus_sctl)-1);
+				dbus_sctl[sizeof(dbus_sctl)-1] = '\0';
+			}
+			read_dbus = 1;
+			break;
+		case OPT_DBUS_PATH:
+			dbus_path=optarg;
+			break;
+		case OPT_DBUS_STEP:
+			t=atoi(optarg);
+			if(t > 0 && t <= 999999) {
+				snprintf(dbus_step, sizeof(dbus_step), "%d", atoi(optarg));
+			}
+			break;
+#endif /* HAVE_DBUS */
 		default:
 			fprintf(stderr, "Invalid switch or option needs an argument.\n");
 			morehelp++;
@@ -1978,6 +2145,10 @@ int main(int argc, char *argv[])
 	if (read_stdin)
 		return exec_stdin();
 
+#ifdef HAVE_DBUS
+	if(read_dbus)
+		return exec_dbus();
+#endif /* HAVE_DBUS */
 	if (argc - optind <= 0) {
 		return selems(LEVEL_BASIC | level) ? 1 : 0;
 	}
diff --git a/configure.in b/configure.in
index 1349ff3..717d6c5 100644
--- a/configure.in
+++ b/configure.in
@@ -7,6 +7,9 @@ AM_INIT_AUTOMAKE(alsa-utils, 1.0.20)
 AM_GNU_GETTEXT([external])
 AM_GNU_GETTEXT_VERSION([0.15])
 
+dnl required versions of other packages
+m4_define([dbus_required_version], [1.2.0])
+
 dnl Checks for programs.
 
 dnl try to gues cross-compiler if not set
@@ -74,6 +77,15 @@ AC_ARG_ENABLE(alsamixer,
        no)  alsamixer=false ;;
        *) AC_MSG_ERROR(bad value ${enableval} for --enable-alsamixer) ;;
      esac],[alsamixer=true])
+AC_ARG_WITH(dbus, [  --with-dbus             Enable D-Bus support in amixer])
+  if test "x$with_dbus" != xno; then
+    PKG_CHECK_MODULES(DBUS, dbus-1 >= dbus_required_version,
+      have_dbus=yes,
+      have_dbus=no)
+  fi
+  if test "x$have_dbus" = xyes; then
+    AC_DEFINE(HAVE_DBUS, 1, [Define to 1 if amixer is to support D-Bus])
+  fi
 fi
 AM_CONDITIONAL(ALSAMIXER, test x$alsamixer = xtrue)
 
-- 
1.6.3.3


----------------------------------------------------------------------
Zostan Dziewczyna lub Chlopakiem lata!
Wygraj skuter >> http://link.interia.pl/f22a7 



More information about the Alsa-devel mailing list