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@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)