From c94e7421aad58c7714f6e26f20642c51af17cc4d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 3 Jun 2009 15:21:50 +0300 Subject: Create module-dbus-protocol with "Hello, world!" functionality. --- src/Makefile.am | 13 ++++++-- src/daemon/default.pa.in | 3 ++ src/daemon/system.pa.in | 3 ++ src/modules/module-dbus-protocol.c | 66 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/modules/module-dbus-protocol.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a7ec6917..70b16be3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1072,7 +1072,8 @@ endif if HAVE_DBUS modlibexec_LTLIBRARIES += \ - module-rygel-media-server.la + module-rygel-media-server.la \ + module-dbus-protocol.la endif if HAVE_BLUEZ @@ -1164,7 +1165,8 @@ SYMDEF_FILES = \ modules/module-position-event-sounds-symdef.h \ modules/module-augment-properties-symdef.h \ modules/module-cork-music-on-phone-symdef.h \ - modules/module-console-kit-symdef.h + modules/module-console-kit-symdef.h \ + modules/module-dbus-protocol-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1213,6 +1215,13 @@ module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(A module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS) module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +# D-Bus protocol + +module_dbus_protocol_la_SOURCES = modules/module-dbus-protocol.c +module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS) +module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + # Native protocol module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index fa0683e1..ba8e3c80 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -66,6 +66,9 @@ load-module module-bluetooth-discover .ifexists module-esound-protocol-unix@PA_SOEXT@ load-module module-esound-protocol-unix .endif +.ifexists module-dbus-protocol@PA_SOEXT@ +load-module module-dbus-protocol +.endif load-module module-native-protocol-unix ### Network access (may be configured with paprefs, so leave this commented diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 27e42815..5541bbe4 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -32,6 +32,9 @@ load-module module-detect .ifexists module-esound-protocol-unix@PA_SOEXT@ load-module module-esound-protocol-unix .endif +.ifexists module-dbus-protocol@PA_SOEXT@ +load-module module-dbus-protocol +.endif load-module module-native-protocol-unix ### Automatically restore the volume of streams and devices diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c new file mode 100644 index 00000000..077f4178 --- /dev/null +++ b/src/modules/module-dbus-protocol.c @@ -0,0 +1,66 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "module-dbus-protocol-symdef.h" + +PA_MODULE_DESCRIPTION("D-Bus interface"); +PA_MODULE_USAGE(""); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_AUTHOR("Tanu Kaskinen"); +PA_MODULE_VERSION(PACKAGE_VERSION); + +struct userdata { + pa_module *module; +}; + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + + pa_assert(m); + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->module = m; + + pa_log_notice("Hello, world!"); + + return 0; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + pa_xfree(u); + m->userdata = NULL; +} -- cgit From 5babbaafb26ac4f83db0d8bca53006a843472b8f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 12 Jun 2009 07:16:05 +0300 Subject: daemon: Implement the DBus server lookup service. --- src/Makefile.am | 5 +- src/daemon/daemon-conf.c | 48 ++++++++ src/daemon/daemon-conf.h | 9 ++ src/daemon/daemon.conf.in | 1 + src/daemon/main.c | 136 ++++++++++++++++------ src/daemon/server-lookup.c | 277 +++++++++++++++++++++++++++++++++++++++++++++ src/daemon/server-lookup.h | 42 +++++++ src/pulse/client-conf.c | 3 + src/pulse/client-conf.h | 3 +- src/pulse/client.conf.in | 1 + 10 files changed, 489 insertions(+), 36 deletions(-) create mode 100644 src/daemon/server-lookup.c create mode 100644 src/daemon/server-lookup.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 70b16be3..c56f7608 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,13 +135,14 @@ BUILT_SOURCES = \ bin_PROGRAMS = pulseaudio pulseaudio_SOURCES = \ - daemon/caps.h daemon/caps.c \ + daemon/caps.c daemon/caps.h \ daemon/cmdline.c daemon/cmdline.h \ daemon/cpulimit.c daemon/cpulimit.h \ daemon/daemon-conf.c daemon/daemon-conf.h \ daemon/dumpmodules.c daemon/dumpmodules.h \ daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \ - daemon/main.c + daemon/main.c \ + daemon/server-lookup.c daemon/server-lookup.h pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS) pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS) diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index ac6cc8aa..e6fa8c64 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -83,6 +83,9 @@ static const pa_daemon_conf default_conf = { .config_file = NULL, .use_pid_file = TRUE, .system_instance = FALSE, +#ifdef HAVE_DBUS + .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */ +#endif .no_cpu_limit = FALSE, .disable_shm = FALSE, .default_n_fragments = 4, @@ -203,6 +206,22 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) { return 0; } +int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) { + pa_assert(c); + pa_assert(string); + + if (!strcmp(string, "user")) + c->local_server_type = PA_SERVER_TYPE_USER; + else if (!strcmp(string, "system")) { + c->local_server_type = PA_SERVER_TYPE_SYSTEM; + } else if (!strcmp(string, "none")) { + c->local_server_type = PA_SERVER_TYPE_NONE; + } else + return -1; + + return 0; +} + static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { pa_daemon_conf *c = data; @@ -430,6 +449,22 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section return 0; } +static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) { + pa_daemon_conf *c = data; + + pa_assert(filename); + pa_assert(lvalue); + pa_assert(rvalue); + pa_assert(data); + + if (pa_daemon_conf_set_local_server_type(c, rvalue) < 0) { + pa_log(_("[%s:%u] Invalid server type '%s'."), filename, line, rvalue); + return -1; + } + + return 0; +} + int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { int r = -1; FILE *f = NULL; @@ -443,6 +478,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL }, { "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL }, { "system-instance", pa_config_parse_bool, &c->system_instance, NULL }, +#ifdef HAVE_DBUS + { "local-server-type", parse_server_type, c, NULL }, +#endif { "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, { "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL }, @@ -604,6 +642,13 @@ static const char* const log_level_to_string[] = { [PA_LOG_ERROR] = "error" }; +static const char* const server_type_to_string[] = { + [PA_SERVER_TYPE_UNSET] = "!!UNSET!!", + [PA_SERVER_TYPE_USER] = "user", + [PA_SERVER_TYPE_SYSTEM] = "system", + [PA_SERVER_TYPE_NONE] = "none" +}; + char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf *s; char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; @@ -627,6 +672,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit)); pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file)); pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance)); +#ifdef HAVE_DBUS + pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]); +#endif pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit)); pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm)); pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes)); diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 9cec189f..98db8641 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -48,6 +48,13 @@ typedef enum pa_daemon_conf_cmd { PA_CMD_CLEANUP_SHM } pa_daemon_conf_cmd_t; +typedef enum pa_daemon_conf_server_type { + PA_SERVER_TYPE_UNSET, + PA_SERVER_TYPE_USER, + PA_SERVER_TYPE_SYSTEM, + PA_SERVER_TYPE_NONE +} pa_daemon_conf_server_type_t; + #ifdef HAVE_SYS_RESOURCE_H typedef struct pa_rlimit { rlim_t value; @@ -74,6 +81,7 @@ typedef struct pa_daemon_conf { log_meta, log_time, flat_volumes; + pa_daemon_conf_server_type_t local_server_type; int exit_idle_time, scache_idle_time, auto_log_target, @@ -151,6 +159,7 @@ int pa_daemon_conf_env(pa_daemon_conf *c); int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string); +int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string); const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c); FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c); diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index fcd2513a..ecdb3a64 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -25,6 +25,7 @@ ; disallow-exit = no ; use-pid-file = yes ; system-instance = no +; local-server-type = user ; disable-shm = no ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB diff --git a/src/daemon/main.c b/src/daemon/main.c index 3e50baad..1b6ed793 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -99,6 +99,7 @@ #include "caps.h" #include "ltdl-bind-now.h" #include "polkit.h" +#include "server-lookup.h" #ifdef HAVE_LIBWRAP /* Only one instance of these variables */ @@ -335,33 +336,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) { #endif #ifdef HAVE_DBUS -static pa_dbus_connection *register_dbus(pa_core *c) { +static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) { DBusError error; pa_dbus_connection *conn; dbus_error_init(&error); - if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) { pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message); goto fail; } - if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - pa_log_debug("Got org.pulseaudio.Server!"); + if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + pa_log_debug("Got %s!", name); return conn; } if (dbus_error_is_set(&error)) - pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message); + pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message); else - pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!"); + pa_log_error("D-Bus name %s already taken. Weird shit!", name); /* PA cannot be started twice by the same user and hence we can - * ignore mostly the case that org.pulseaudio.Server is already - * taken. */ + * ignore mostly the case that a name is already taken. */ fail: - if (conn) pa_dbus_connection_unref(conn); @@ -393,7 +392,10 @@ int main(int argc, char *argv[]) { int autospawn_fd = -1; pa_bool_t autospawn_locked = FALSE; #ifdef HAVE_DBUS - pa_dbus_connection *dbus = NULL; + pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */ + pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */ + pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */ + pa_bool_t start_server; #endif pa_log_set_ident("pulseaudio"); @@ -486,8 +488,45 @@ int main(int argc, char *argv[]) { pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET); pa_log_set_show_backtrace(conf->log_backtrace); +#ifdef HAVE_DBUS + /* conf->system_instance and conf->local_server_type control almost the + * same thing; make them agree about what is requested. */ + switch (conf->local_server_type) { + case PA_SERVER_TYPE_UNSET: + conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER; + break; + case PA_SERVER_TYPE_USER: + case PA_SERVER_TYPE_NONE: + conf->system_instance = FALSE; + break; + case PA_SERVER_TYPE_SYSTEM: + conf->system_instance = TRUE; + break; + default: + pa_assert_not_reached(); + } + + start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (real_root && conf->local_server_type == PA_SERVER_TYPE_SYSTEM); + + if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) { + pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service.")); + conf->system_instance = FALSE; + } +#endif + pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root)); +#ifdef HAVE_DBUS + /* XXX: Uhh, goto programming... as if this wasn't hard enough to follow + * already. But if we won't start the full server, we want to just skip all + * the capability stuff. */ + if (!start_server) { + if (!real_root && pa_have_caps()) + pa_drop_caps(); + goto after_caps_setup; + } +#endif + if (!real_root && pa_have_caps()) { #ifdef HAVE_SYS_RESOURCE_H struct rlimit rl; @@ -628,6 +667,10 @@ int main(int argc, char *argv[]) { conf->realtime_scheduling = FALSE; } +#ifdef HAVE_DBUS +after_caps_setup: +#endif + pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority())); LTDL_SET_PRELOADED_SYMBOLS(); @@ -716,10 +759,12 @@ int main(int argc, char *argv[]) { if (real_root && !conf->system_instance) pa_log_warn(_("This program is not intended to be run as root (unless --system is specified).")); +#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */ else if (!real_root && conf->system_instance) { pa_log(_("Root privileges required.")); goto finish; } +#endif if (conf->cmd == PA_CMD_START && conf->system_instance) { pa_log(_("--start not supported for system instances.")); @@ -1007,34 +1052,47 @@ int main(int argc, char *argv[]) { pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); buf = pa_strbuf_new(); - if (conf->load_default_script_file) { - FILE *f; - if ((f = pa_daemon_conf_open_default_script_file(conf))) { - r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); - fclose(f); +#ifdef HAVE_DBUS + if (start_server) { +#endif + if (conf->load_default_script_file) { + FILE *f; + + if ((f = pa_daemon_conf_open_default_script_file(conf))) { + r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); + fclose(f); + } } - } - if (r >= 0) - r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + if (r >= 0) + r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); - pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); - pa_xfree(s); + pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); + pa_xfree(s); - /* We completed the initial module loading, so let's disable it - * from now on, if requested */ - c->disallow_module_loading = !!conf->disallow_module_loading; + if (r < 0 && conf->fail) { + pa_log(_("Failed to initialize daemon.")); + goto finish; + } - if (r < 0 && conf->fail) { - pa_log(_("Failed to initialize daemon.")); - goto finish; + if (!c->modules || pa_idxset_size(c->modules) == 0) { + pa_log(_("Daemon startup without any loaded modules, refusing to work.")); + goto finish; + } +#ifdef HAVE_DBUS + } else { + /* When we just provide the D-Bus server lookup service, we don't want + * any modules to be loaded. We haven't loaded any so far, so one might + * think there's no way to contact the server, but receiving certain + * signals could still cause modules to load. */ + conf->disallow_module_loading = TRUE; } +#endif - if (!c->modules || pa_idxset_size(c->modules) == 0) { - pa_log(_("Daemon startup without any loaded modules, refusing to work.")); - goto finish; - } + /* We completed the initial module loading, so let's disable it + * from now on, if requested */ + c->disallow_module_loading = !!conf->disallow_module_loading; #ifdef HAVE_FORK if (daemon_pipe[1] >= 0) { @@ -1046,7 +1104,15 @@ int main(int argc, char *argv[]) { #endif #ifdef HAVE_DBUS - dbus = register_dbus(c); + if (!conf->system_instance) { + if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type))) + goto finish; + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) + goto finish; + } + + if (start_server && !(server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server"))) + goto finish; #endif pa_log_info(_("Daemon startup complete.")); @@ -1059,8 +1125,12 @@ int main(int argc, char *argv[]) { finish: #ifdef HAVE_DBUS - if (dbus) - pa_dbus_connection_unref(dbus); + if (server_bus) + pa_dbus_connection_unref(server_bus); + if (lookup_service_bus) + pa_dbus_connection_unref(lookup_service_bus); + if (server_lookup) + pa_dbusobj_server_lookup_free(server_lookup); #endif if (autospawn_fd >= 0) { diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c new file mode 100644 index 00000000..867c3a1c --- /dev/null +++ b/src/daemon/server-lookup.c @@ -0,0 +1,277 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include +#include +#include + +#include "server-lookup.h" + +struct pa_dbusobj_server_lookup { + pa_dbus_connection *conn; + pa_bool_t path_registered; + pa_daemon_conf_server_type_t server_type; +}; + +static const char introspection[] = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "" + " \n" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static void unregister_cb(DBusConnection *conn, void *user_data) { + pa_dbusobj_server_lookup *sl = user_data; + + pa_assert(sl); + pa_assert(sl->path_registered); + + sl->path_registered = FALSE; +} + +static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + const char *i = introspection; + DBusMessage *reply = NULL; + + pa_assert(conn); + pa_assert(msg); + + if (!(reply = dbus_message_new_method_return(msg))) + goto fail; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +/* Caller frees the string. */ +static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) { + char *server_string = NULL; + char *runtime_dir = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + runtime_dir = pa_get_runtime_dir(); + + if (!runtime_dir) + return NULL; + + server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir); + break; + + case PA_SERVER_TYPE_SYSTEM: + server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket"); + break; + + case PA_SERVER_TYPE_NONE: + server_string = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + return server_string; +} + +static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusMessage *reply = NULL; + pa_client_conf *conf = NULL; + char *server_string = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + conf = pa_client_conf_new(); + + if (pa_client_conf_load(conf, NULL) < 0) { + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) + goto fail; + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + return DBUS_HANDLER_RESULT_HANDLED; + } + + server_string = pa_xstrdup(conf->default_dbus_server); + + pa_client_conf_free(conf); + + if (!server_string) { + if (!(server_string = get_dbus_server_from_type(sl->server_type))) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) + goto fail; + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + if (!(reply = dbus_message_new_method_return(msg))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + pa_log("Sent reply with server_string '%s'.", server_string); + + pa_xfree(server_string); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (conf) + pa_client_conf_free(conf); + + pa_xfree(server_string); + + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (conf) + pa_client_conf_free(conf); + + pa_xfree(server_string); + + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) { + pa_dbusobj_server_lookup *sl = user_data; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */ + + if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) + return handle_introspect(conn, msg, sl); + + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers")) + return handle_get_dbus_servers(conn, msg, sl); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = unregister_cb, + .message_function = message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) { + pa_dbusobj_server_lookup *sl; + DBusError error; + + dbus_error_init(&error); + + sl = pa_xnew(pa_dbusobj_server_lookup, 1); + sl->path_registered = FALSE; + sl->server_type = server_type; + + if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message); + goto fail; + } + + if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) { + pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup."); + goto fail; + } + + sl->path_registered = TRUE; + + return sl; + +fail: + dbus_error_free(&error); + + pa_dbusobj_server_lookup_free(sl); + + return NULL; +} + +void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { + pa_assert(sl); + + if (sl->path_registered) { + pa_assert(sl->conn); + if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup")) + pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup."); + } + + if (sl->conn) + pa_dbus_connection_unref(sl->conn); + + pa_xfree(sl); +} \ No newline at end of file diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h new file mode 100644 index 00000000..69fdacdd --- /dev/null +++ b/src/daemon/server-lookup.h @@ -0,0 +1,42 @@ +#ifndef fooserverlookuphfoo +#define fooserverlookuphfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus object at path + * /org/pulseaudio/server_lookup. Implemented interfaces + * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable. + * + * See http://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface + * documentation. + */ + +#include + +#include "daemon-conf.h" + +typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup; + +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type); +void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl); + +#endif \ No newline at end of file diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index 940d0b67..8eab1094 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -57,6 +57,7 @@ static const pa_client_conf default_conf = { .default_sink = NULL, .default_source = NULL, .default_server = NULL, + .default_dbus_server = NULL, .autospawn = TRUE, .disable_shm = FALSE, .cookie_file = NULL, @@ -81,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) { pa_xfree(c->default_sink); pa_xfree(c->default_source); pa_xfree(c->default_server); + pa_xfree(c->default_dbus_server); pa_xfree(c->cookie_file); pa_xfree(c); } @@ -97,6 +99,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { { "default-sink", pa_config_parse_string, &c->default_sink, NULL }, { "default-source", pa_config_parse_string, &c->default_source, NULL }, { "default-server", pa_config_parse_string, &c->default_server, NULL }, + { "default-dbus-server", pa_config_parse_string, &c->default_dbus_server, NULL }, { "autospawn", pa_config_parse_bool, &c->autospawn, NULL }, { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL }, { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL }, diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h index ab97dc6a..618216f4 100644 --- a/src/pulse/client-conf.h +++ b/src/pulse/client-conf.h @@ -22,12 +22,13 @@ USA. ***/ +#include #include /* A structure containing configuration data for PulseAudio clients. */ typedef struct pa_client_conf { - char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file; + char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *default_dbus_server, *cookie_file; pa_bool_t autospawn, disable_shm; uint8_t cookie[PA_NATIVE_COOKIE_LENGTH]; pa_bool_t cookie_valid; /* non-zero, when cookie is valid */ diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in index 579bcc20..3340b0b2 100644 --- a/src/pulse/client.conf.in +++ b/src/pulse/client.conf.in @@ -22,6 +22,7 @@ ; default-sink = ; default-source = ; default-server = +; default-dbus-server = ; autospawn = yes ; daemon-binary = @PA_BINARY@ -- cgit From c8d819a5adbe32e14d7f03a252bca6f7df01d795 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 16 Jun 2009 19:03:22 +0300 Subject: dbus-protocol: Connection handling for local connections. --- src/Makefile.am | 2 + src/daemon/daemon-conf.h | 10 +- src/daemon/main.c | 3 +- src/daemon/server-lookup.c | 69 ++---- src/daemon/server-lookup.h | 6 +- src/modules/module-dbus-protocol.c | 470 ++++++++++++++++++++++++++++++++++++- src/pulsecore/core.h | 9 + src/pulsecore/dbus-common.c | 73 ++++++ src/pulsecore/dbus-common.h | 39 +++ src/pulsecore/dbus-util.c | 21 ++ src/pulsecore/dbus-util.h | 1 + 11 files changed, 639 insertions(+), 64 deletions(-) create mode 100644 src/pulsecore/dbus-common.c create mode 100644 src/pulsecore/dbus-common.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index c56f7608..5302bc22 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -783,6 +783,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ + pulsecore/dbus-common.c pulsecore/dbus-common.h \ pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/g711.c pulsecore/g711.h \ @@ -796,6 +797,7 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/object.c pulsecore/object.h \ pulsecore/play-memblockq.c pulsecore/play-memblockq.h \ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \ + pulsecore/protocol-dbus.h \ pulsecore/resampler.c pulsecore/resampler.h \ pulsecore/rtpoll.c pulsecore/rtpoll.h \ pulsecore/rtsig.c pulsecore/rtsig.h \ diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 98db8641..c4f78cbe 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -28,6 +28,7 @@ #include #include +#include #include #ifdef HAVE_SYS_RESOURCE_H @@ -48,13 +49,6 @@ typedef enum pa_daemon_conf_cmd { PA_CMD_CLEANUP_SHM } pa_daemon_conf_cmd_t; -typedef enum pa_daemon_conf_server_type { - PA_SERVER_TYPE_UNSET, - PA_SERVER_TYPE_USER, - PA_SERVER_TYPE_SYSTEM, - PA_SERVER_TYPE_NONE -} pa_daemon_conf_server_type_t; - #ifdef HAVE_SYS_RESOURCE_H typedef struct pa_rlimit { rlim_t value; @@ -81,7 +75,7 @@ typedef struct pa_daemon_conf { log_meta, log_time, flat_volumes; - pa_daemon_conf_server_type_t local_server_type; + pa_server_type_t local_server_type; int exit_idle_time, scache_idle_time, auto_log_target, diff --git a/src/daemon/main.c b/src/daemon/main.c index 1b6ed793..62214a56 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -1028,6 +1028,7 @@ after_caps_setup: c->running_as_daemon = !!conf->daemonize; c->disallow_exit = conf->disallow_exit; c->flat_volumes = conf->flat_volumes; + c->server_type = conf->local_server_type; pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); pa_signal_new(SIGINT, signal_callback, c); @@ -1105,7 +1106,7 @@ after_caps_setup: #ifdef HAVE_DBUS if (!conf->system_instance) { - if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type))) + if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) goto finish; diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 867c3a1c..b53fda76 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -28,16 +28,18 @@ #include #include +#include #include +#include #include #include #include "server-lookup.h" struct pa_dbusobj_server_lookup { + pa_core *core; pa_dbus_connection *conn; pa_bool_t path_registered; - pa_daemon_conf_server_type_t server_type; }; static const char introspection[] = @@ -46,7 +48,7 @@ static const char introspection[] = " \n" " " - " " + " " " " " " " " @@ -99,40 +101,10 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -/* Caller frees the string. */ -static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) { - char *server_string = NULL; - char *runtime_dir = NULL; - - switch (server_type) { - case PA_SERVER_TYPE_USER: - runtime_dir = pa_get_runtime_dir(); - - if (!runtime_dir) - return NULL; - - server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir); - break; - - case PA_SERVER_TYPE_SYSTEM: - server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket"); - break; - - case PA_SERVER_TYPE_NONE: - server_string = pa_xnew0(char, 1); - break; - - default: - pa_assert_not_reached(); - } - - return server_string; -} - -static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { +static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { DBusMessage *reply = NULL; pa_client_conf *conf = NULL; - char *server_string = NULL; + char *address = NULL; pa_assert(conn); pa_assert(msg); @@ -148,12 +120,13 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa return DBUS_HANDLER_RESULT_HANDLED; } - server_string = pa_xstrdup(conf->default_dbus_server); - pa_client_conf_free(conf); - if (!server_string) { - if (!(server_string = get_dbus_server_from_type(sl->server_type))) { + if (conf->default_dbus_server) { + if (!(address = dbus_address_escape_value(conf->default_dbus_server))) + goto oom; + } else { + if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) goto fail; if (!dbus_connection_send(conn, reply, NULL)) @@ -165,15 +138,15 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa if (!(reply = dbus_message_new_method_return(msg))) goto oom; - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID)) + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) goto fail; if (!dbus_connection_send(conn, reply, NULL)) goto oom; - pa_log("Sent reply with server_string '%s'.", server_string); + pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address); - pa_xfree(server_string); + pa_xfree(address); dbus_message_unref(reply); @@ -183,7 +156,7 @@ fail: if (conf) pa_client_conf_free(conf); - pa_xfree(server_string); + pa_xfree(address); if (reply) dbus_message_unref(reply); @@ -194,7 +167,7 @@ oom: if (conf) pa_client_conf_free(conf); - pa_xfree(server_string); + pa_xfree(address); if (reply) dbus_message_unref(reply); @@ -214,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers")) - return handle_get_dbus_servers(conn, msg, sl); + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress")) + return handle_get_dbus_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -229,15 +202,15 @@ static DBusObjectPathVTable vtable = { .dbus_internal_pad4 = NULL }; -pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) { +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) { pa_dbusobj_server_lookup *sl; DBusError error; dbus_error_init(&error); sl = pa_xnew(pa_dbusobj_server_lookup, 1); + sl->core = c; sl->path_registered = FALSE; - sl->server_type = server_type; if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message); @@ -274,4 +247,4 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { pa_dbus_connection_unref(sl->conn); pa_xfree(sl); -} \ No newline at end of file +} diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h index 69fdacdd..c930d5b7 100644 --- a/src/daemon/server-lookup.h +++ b/src/daemon/server-lookup.h @@ -32,11 +32,9 @@ #include -#include "daemon-conf.h" - typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup; -pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type); +pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c); void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl); -#endif \ No newline at end of file +#endif diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 077f4178..14551488 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -2,6 +2,8 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2006 Lennart Poettering + Copyright 2006 Shams E. King PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -23,37 +25,487 @@ #include #endif +#include + +#include +#include #include +#include +#include +#include +#include +#include #include +#include #include #include "module-dbus-protocol-symdef.h" PA_MODULE_DESCRIPTION("D-Bus interface"); -PA_MODULE_USAGE(""); +PA_MODULE_USAGE( + "access=local|remote|local,remote " + "tcp_port="); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_AUTHOR("Tanu Kaskinen"); PA_MODULE_VERSION(PACKAGE_VERSION); +#define CLEANUP_INTERVAL 10 /* seconds */ + +struct server; +struct connection; + struct userdata { pa_module *module; + pa_bool_t local_access; + pa_bool_t remote_access; + uint32_t tcp_port; + + struct server *local_server; + struct server *tcp_server; + + pa_idxset *connections; + + pa_time_event *cleanup_event; +}; + +struct server { + struct userdata *userdata; + DBusServer *dbus_server; +}; + +struct connection { + struct server *server; + pa_dbus_wrap_connection *wrap_conn; + pa_client *client; +}; + +static const char* const valid_modargs[] = { + "access", + "tcp_port", + NULL }; +static void connection_free(struct connection *c) { + pa_assert(c); + + pa_client_free(c->client); + pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL)); + pa_dbus_wrap_connection_free(c->wrap_conn); + pa_xfree(c); +} + +static void client_kill_cb(pa_client *c) { + struct connection *conn; + + pa_assert(c); + pa_assert(c->userdata); + + conn = c->userdata; + connection_free(conn); + + pa_log_info("Connection killed."); +} + +/* Called by D-Bus when a new client connection is received. */ +static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) { + struct server *s = data; + struct connection *c; + pa_client_new_data new_data; + pa_client *client; + + pa_assert(new_connection); + pa_assert(s); + + pa_client_new_data_init(&new_data); + new_data.module = s->userdata->module; + new_data.driver = __FILE__; + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: Fancier name. */ + client = pa_client_new(s->userdata->module->core, &new_data); + pa_client_new_data_done(&new_data); + + if (!client) + return; + + c = pa_xnew(struct connection, 1); + c->server = s; + c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection); + c->client = client; + + c->client->kill = client_kill_cb; + c->client->send_event = NULL; + c->client->userdata = c; + + pa_idxset_put(s->userdata->connections, c, NULL); +} + +/* Called by PA mainloop when a D-Bus fd watch event needs handling. */ +static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { + unsigned int flags = 0; + DBusWatch *watch = userdata; + +#if HAVE_DBUS_WATCH_GET_UNIX_FD + pa_assert(fd == dbus_watch_get_unix_fd(watch)); +#else + pa_assert(fd == dbus_watch_get_fd(watch)); +#endif + + if (!dbus_watch_get_enabled(watch)) { + pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd); + return; + } + + if (events & PA_IO_EVENT_INPUT) + flags |= DBUS_WATCH_READABLE; + if (events & PA_IO_EVENT_OUTPUT) + flags |= DBUS_WATCH_WRITABLE; + if (events & PA_IO_EVENT_HANGUP) + flags |= DBUS_WATCH_HANGUP; + if (events & PA_IO_EVENT_ERROR) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); +} + +/* Called by PA mainloop when a D-Bus timer event needs handling. */ +static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) { + DBusTimeout *timeout = userdata; + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval next = *tv; + dbus_timeout_handle(timeout); + + /* restart it for the next scheduled time */ + pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + mainloop->time_restart(e, &next); + } +} + +/* Translates D-Bus fd watch event flags to PA IO event flags. */ +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { + unsigned int flags; + pa_io_event_flags_t events = 0; + + pa_assert(watch); + + flags = dbus_watch_get_flags(watch); + + /* no watch flags for disabled watches */ + if (!dbus_watch_get_enabled(watch)) + return PA_IO_EVENT_NULL; + + if (flags & DBUS_WATCH_READABLE) + events |= PA_IO_EVENT_INPUT; + if (flags & DBUS_WATCH_WRITABLE) + events |= PA_IO_EVENT_OUTPUT; + + return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; +} + +/* Called by D-Bus when a D-Bus fd watch event is added. */ +static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + mainloop = s->userdata->module->core->mainloop; + + ev = mainloop->io_new( + mainloop, +#if HAVE_DBUS_WATCH_GET_UNIX_FD + dbus_watch_get_unix_fd(watch), +#else + dbus_watch_get_fd(watch), +#endif + get_watch_flags(watch), io_event_cb, watch); + + dbus_watch_set_data(watch, ev, NULL); + + return TRUE; +} + +/* Called by D-Bus when a D-Bus fd watch event is removed. */ +static void watch_remove_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + if ((ev = dbus_watch_get_data(watch))) + s->userdata->module->core->mainloop->io_free(ev); +} + +/* Called by D-Bus when a D-Bus fd watch event is toggled. */ +static void watch_toggled_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + pa_assert_se(ev = dbus_watch_get_data(watch)); + + /* get_watch_flags() checks if the watch is enabled */ + s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch)); +} + +/* Called by D-Bus when a D-Bus timer event is added. */ +static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_time_event *ev; + struct timeval tv; + + pa_assert(timeout); + pa_assert(s); + + if (!dbus_timeout_get_enabled(timeout)) + return FALSE; + + mainloop = s->userdata->module->core->mainloop; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout); + + dbus_timeout_set_data(timeout, ev, NULL); + + return TRUE; +} + +/* Called by D-Bus when a D-Bus timer event is removed. */ +static void timeout_remove_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(s); + + if ((ev = dbus_timeout_get_data(timeout))) + s->userdata->module->core->mainloop->time_free(ev); +} + +/* Called by D-Bus when a D-Bus timer event is toggled. */ +static void timeout_toggled_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(s); + + mainloop = s->userdata->module->core->mainloop; + + pa_assert_se(ev = dbus_timeout_get_data(timeout)); + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval tv; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + mainloop->time_restart(ev, &tv); + } else + mainloop->time_restart(ev, NULL); +} + +static void server_free(struct server *s) { + pa_assert(s); + + if (s->dbus_server) { + dbus_server_disconnect(s->dbus_server); + dbus_server_unref(s->dbus_server); + } + + pa_xfree(s); +} + +static struct server *start_server(struct userdata *u, const char *address) { + /* XXX: We assume that when we unref the DBusServer instance at module + * shutdown, nobody else holds any references to it. If we stop assuming + * that someday, dbus_server_set_new_connection_function, + * dbus_server_set_watch_functions and dbus_server_set_timeout_functions + * calls should probably register free callbacks, instead of providing NULL + * as they do now. */ + + struct server *s = NULL; + DBusError error; + + pa_assert(u); + pa_assert(address); + + dbus_error_init(&error); + + s = pa_xnew0(struct server, 1); + s->userdata = u; + s->dbus_server = dbus_server_listen(address, &error); + + if (dbus_error_is_set(&error)) { + pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message); + goto fail; + } + + dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL); + + if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) { + pa_log("dbus_server_set_watch_functions() ran out of memory."); + goto fail; + } + + if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) { + pa_log("dbus_server_set_timeout_functions() ran out of memory."); + goto fail; + } + + return s; + +fail: + if (s) + server_free(s); + + dbus_error_free(&error); + + return NULL; +} + +static struct server *start_local_server(struct userdata *u) { + struct server *s = NULL; + char *address = NULL; + + pa_assert(u); + + address = pa_get_dbus_address_from_server_type(u->module->core->server_type); + + s = start_server(u, address); /* May return NULL */ + + pa_xfree(address); + + return s; +} + +static struct server *start_tcp_server(struct userdata *u) { + pa_log("start_tcp_server(): Not implemented!"); + return NULL; +} + +static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) { + const char *value = NULL; + + pa_assert(ma); + pa_assert(local_access); + pa_assert(remote_access); + + if (!(value = pa_modargs_get_value(ma, "access", NULL))) + return 0; + + if (!strcmp(value, "local")) { + *local_access = TRUE; + *remote_access = FALSE; + } else if (!strcmp(value, "remote")) { + *local_access = FALSE; + *remote_access = TRUE; + } else if (!strcmp(value, "local,remote")) { + *local_access = TRUE; + *local_access = TRUE; + } else + return -1; + + return 0; +} + +/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */ +static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct connection *conn = NULL; + uint32_t idx; + struct timeval cleanup_timeval; + unsigned free_count = 0; + + for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) { + if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) { + connection_free(conn); + ++free_count; + } + } + + if (free_count > 0) + pa_log_debug("Freed %u dead D-Bus client connections.", free_count); + + pa_gettimeofday(&cleanup_timeval); + cleanup_timeval.tv_sec += CLEANUP_INTERVAL; + u->module->core->mainloop->time_restart(e, &cleanup_timeval); +} + int pa__init(pa_module *m) { struct userdata *u = NULL; + pa_modargs *ma = NULL; + struct timeval cleanup_timeval; pa_assert(m); + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + m->userdata = u = pa_xnew0(struct userdata, 1); u->module = m; + u->local_access = TRUE; + u->remote_access = FALSE; + u->tcp_port = PA_DBUS_DEFAULT_PORT; + + if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) { + pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL)); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) { + pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL)); + goto fail; + } + + if (u->local_access && !(u->local_server = start_local_server(u))) { + pa_log("Starting the local D-Bus server failed."); + goto fail; + } + + if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) { + pa_log("Starting the D-Bus server for remote connections failed."); + goto fail; + } - pa_log_notice("Hello, world!"); + u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + pa_gettimeofday(&cleanup_timeval); + cleanup_timeval.tv_sec += CLEANUP_INTERVAL; + u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u); return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; } -void pa__done(pa_module*m) { +/* Called by idxset when the connection set is freed. */ +static void connection_free_cb(void *p, void *userdata) { + struct connection *conn = p; + + pa_assert(conn); + + connection_free(conn); +} + +void pa__done(pa_module *m) { struct userdata *u; pa_assert(m); @@ -61,6 +513,18 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + if (u->cleanup_event) + m->core->mainloop->time_free(u->cleanup_event); + + if (u->connections) + pa_idxset_free(u->connections, connection_free_cb, NULL); + + if (u->tcp_server) + server_free(u->tcp_server); + + if (u->local_server) + server_free(u->local_server); + pa_xfree(u); m->userdata = NULL; } diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index c6794445..f93652e2 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -42,6 +42,13 @@ typedef struct pa_core pa_core; #include #include +typedef enum pa_server_type { + PA_SERVER_TYPE_UNSET, + PA_SERVER_TYPE_USER, + PA_SERVER_TYPE_SYSTEM, + PA_SERVER_TYPE_NONE +} pa_server_type_t; + typedef enum pa_core_state { PA_CORE_STARTUP, PA_CORE_RUNNING, @@ -152,6 +159,8 @@ struct pa_core { pa_resample_method_t resample_method; int realtime_priority; + pa_server_type_t server_type; + /* hooks */ pa_hook hooks[PA_CORE_HOOK_MAX]; }; diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c new file mode 100644 index 00000000..05931e0a --- /dev/null +++ b/src/pulsecore/dbus-common.c @@ -0,0 +1,73 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "dbus-common.h" + +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { + char *address = NULL; + char *runtime_path = NULL; + char *escaped_path = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) { + pa_log("pa_runtime_path() failed."); + break; + } + + if (!(escaped_path = dbus_address_escape_value(runtime_path))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_SYSTEM: + if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) { + pa_log("dbus_address_escape_value() failed."); + break; + } + + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_NONE: + address = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(runtime_path); + pa_xfree(escaped_path); + + return address; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h new file mode 100644 index 00000000..26bd05d4 --- /dev/null +++ b/src/pulsecore/dbus-common.h @@ -0,0 +1,39 @@ +#ifndef foodbuscommonhfoo +#define foodbuscommonhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +#define PA_DBUS_DEFAULT_PORT 24883 +#define PA_DBUS_SOCKET_NAME "dbus_socket" + +#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME + +/* Returns the default address of the server type in the escaped form. For + * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the + * string. This function may fail in some rare cases, in which case NULL is + * returned. */ +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); + +#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index ece36def..d8bd0e0a 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -276,6 +276,27 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus return pconn; } +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) { + pa_dbus_wrap_connection *pconn; + + pa_assert(m); + pa_assert(conn); + + pconn = pa_xnew(pa_dbus_wrap_connection, 1); + pconn->mainloop = m; + pconn->connection = dbus_connection_ref(conn); + + dbus_connection_set_exit_on_disconnect(conn, FALSE); + dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL); + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL); + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL); + dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL); + + pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn); + + return pconn; +} + void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) { pa_assert(c); diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 55cda7a0..cd08485d 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -31,6 +31,7 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); -- cgit From 123c6a3c6ffc9903c0855e38445fc3b6588311ce Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 19 Jun 2009 10:28:08 +0300 Subject: dbus-common: Implement infrastructure for registering D-Bus objects on all client connections and for receiving method calls from clients. --- src/Makefile.am | 9 +- src/daemon/server-lookup.c | 8 +- src/modules/module-dbus-protocol.c | 18 +- src/pulsecore/dbus-common.c | 430 +++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-common.h | 34 +++ src/pulsecore/dbus-objs/core.c | 120 +++++++++++ src/pulsecore/dbus-objs/core.h | 39 ++++ 7 files changed, 649 insertions(+), 9 deletions(-) create mode 100644 src/pulsecore/dbus-objs/core.c create mode 100644 src/pulsecore/dbus-objs/core.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5302bc22..d28ffa1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -783,7 +783,6 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \ pulsecore/core-scache.c pulsecore/core-scache.h \ pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ pulsecore/core.c pulsecore/core.h \ - pulsecore/dbus-common.c pulsecore/dbus-common.h \ pulsecore/envelope.c pulsecore/envelope.h \ pulsecore/fdsem.c pulsecore/fdsem.h \ pulsecore/g711.c pulsecore/g711.h \ @@ -830,7 +829,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS) endif if HAVE_DBUS -libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h +libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += \ + pulsecore/dbus-shared.c pulsecore/dbus-shared.h \ + pulsecore/dbus-common.c pulsecore/dbus-common.h libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) endif @@ -1220,7 +1221,9 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI # D-Bus protocol -module_dbus_protocol_la_SOURCES = modules/module-dbus-protocol.c +module_dbus_protocol_la_SOURCES = \ + pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \ + modules/module-dbus-protocol.c module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS) module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index b53fda76..2d2d8ce6 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -48,7 +48,7 @@ static const char introspection[] = " \n" " " - " " + " " " " " " " " @@ -101,7 +101,7 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { +static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { DBusMessage *reply = NULL; pa_client_conf *conf = NULL; char *address = NULL; @@ -187,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress")) - return handle_get_dbus_address(conn, msg, sl); + if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress")) + return handle_get_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 14551488..74dfeef0 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -40,6 +40,8 @@ #include #include +#include + #include "module-dbus-protocol-symdef.h" PA_MODULE_DESCRIPTION("D-Bus interface"); @@ -67,6 +69,8 @@ struct userdata { pa_idxset *connections; pa_time_event *cleanup_event; + + pa_dbusobj_core *core_object; }; struct server { @@ -89,12 +93,15 @@ static const char* const valid_modargs[] = { static void connection_free(struct connection *c) { pa_assert(c); + pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0); + pa_client_free(c->client); pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL)); pa_dbus_wrap_connection_free(c->wrap_conn); pa_xfree(c); } +/* Called from pa_client_kill(). */ static void client_kill_cb(pa_client *c) { struct connection *conn; @@ -120,7 +127,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne pa_client_new_data_init(&new_data); new_data.module = s->userdata->module; new_data.driver = __FILE__; - pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: Fancier name. */ + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); @@ -133,10 +140,12 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne c->client = client; c->client->kill = client_kill_cb; - c->client->send_event = NULL; + c->client->send_event = NULL; /* TODO: Implement this. */ c->client->userdata = c; pa_idxset_put(s->userdata->connections, c, NULL); + + pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0); } /* Called by PA mainloop when a D-Bus fd watch event needs handling. */ @@ -485,6 +494,8 @@ int pa__init(pa_module *m) { cleanup_timeval.tv_sec += CLEANUP_INTERVAL; u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u); + u->core_object = pa_dbusobj_core_new(m->core); + return 0; fail: @@ -513,6 +524,9 @@ void pa__done(pa_module *m) { if (!(u = m->userdata)) return; + if (u->core_object) + pa_dbusobj_core_free(u->core_object); + if (u->cleanup_event) m->core->mainloop->time_free(u->cleanup_event); diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 05931e0a..350add82 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -25,10 +25,35 @@ #include +#include + #include +#include +#include +#include #include "dbus-common.h" +struct dbus_state { + pa_core *core; + pa_hashmap *objects; /* Object path -> struct object_entry */ + pa_idxset *connections; /* DBusConnections */ +}; + +struct object_entry { + char *path; + pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ + char *introspection; +}; + +struct interface_entry { + char *name; + char **methods; + char *introspection_snippet; + DBusObjectPathMessageFunction receive; + void *userdata; +}; + char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { char *address = NULL; char *runtime_path = NULL; @@ -71,3 +96,408 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { return address; } + +static void update_introspection(struct object_entry *oe) { + pa_strbuf *buf; + void *state = NULL; + struct interface_entry *iface_entry = NULL; + + pa_assert(oe); + + buf = pa_strbuf_new(); + pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + pa_strbuf_puts(buf, ""); + + while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) + pa_strbuf_puts(buf, iface_entry->introspection_snippet); + + pa_strbuf_puts(buf, " " + " " + " " + " " + " "); + + pa_strbuf_puts(buf, ""); + + pa_xfree(oe->introspection); + oe->introspection = pa_strbuf_tostring_free(buf); +} + +static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { + const char *interface; + struct interface_entry *iface_entry; + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(msg); + + if ((interface = dbus_message_get_interface(msg))) + return pa_hashmap_get(obj_entry->interfaces, interface); + + /* NULL interface, we'll have to search for an interface that contains the + * method. */ + + while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *method; + char **pos = iface_entry->methods; + + while ((method = *pos++)) { + if (!strcmp(dbus_message_get_member(msg), method)) + return iface_entry; + } + } + + return NULL; +} + +static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + struct dbus_state *dbus_state = user_data; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + DBusMessage *reply = NULL; + + pa_assert(connection); + pa_assert(message); + pa_assert(dbus_state); + pa_assert(dbus_state->objects); + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect"); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (!(iface_entry = find_interface(obj_entry, message))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + return iface_entry->receive(connection, message, iface_entry->userdata); + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = NULL, + .message_function = handle_message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +static char **copy_methods(const char * const *methods) { + unsigned n = 0; + char **copy; + unsigned i; + + while (methods[n++]) + ; + + copy = pa_xnew0(char *, n); + + for (i = 0; i < n - 1; ++i) + copy[i] = pa_xstrdup(methods[i]); + + return copy; +} + +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + pa_bool_t state_created = FALSE; + pa_bool_t object_map_created = FALSE; + pa_bool_t obj_entry_created = FALSE; + + pa_assert(c); + pa_assert(path); + pa_assert(introspection_snippet); + pa_assert(receive_cb); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(objects = dbus_state->objects)) { + objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + dbus_state->objects = objects; + object_map_created = TRUE; + } + + if (!(obj_entry = pa_hashmap_get(objects, path))) { + obj_entry = pa_xnew(struct object_entry, 1); + obj_entry->path = pa_xstrdup(path); + obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + obj_entry->introspection = NULL; + pa_hashmap_put(objects, path, obj_entry); + obj_entry_created = TRUE; + } + + if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL) + goto fail; /* The interface was already registered. */ + + iface_entry = pa_xnew(struct interface_entry, 1); + iface_entry->name = pa_xstrdup(interface); + iface_entry->methods = copy_methods(methods); + iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); + iface_entry->receive = receive_cb; + iface_entry->userdata = userdata; + pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); + + update_introspection(obj_entry); + + if (obj_entry_created) + register_object(dbus_state, obj_entry); + + return 0; + +fail: + if (obj_entry_created) { + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry); + } + + if (object_map_created) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { + DBusConnection *conn; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(obj_entry); + + if (!dbus_state->connections) + return; + + while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dbus_connection_unregister_object_path() failed."); + } +} + +static void free_methods(char **methods) { + char **pos = methods; + + while (*pos++) + pa_xfree(*pos); + + pa_xfree(methods); +} + +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { + struct dbus_state *dbus_state; + pa_hashmap *objects; + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + + pa_assert(c); + pa_assert(path); + pa_assert(interface); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(objects = dbus_state->objects)) + return -1; + + if (!(obj_entry = pa_hashmap_get(objects, path))) + return -1; + + if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) + return -1; + + update_introspection(obj_entry); + + pa_xfree(iface_entry->name); + free_methods(iface_entry->methods); + pa_xfree(iface_entry->introspection_snippet); + pa_xfree(iface_entry); + + if (pa_hashmap_isempty(obj_entry->interfaces)) { + unregister_object(dbus_state, obj_entry); + + pa_hashmap_remove(objects, path); + pa_xfree(obj_entry->path); + pa_hashmap_free(obj_entry->interfaces, NULL, NULL); + pa_xfree(obj_entry->introspection); + pa_xfree(obj_entry); + } + + if (pa_hashmap_isempty(objects)) { + dbus_state->objects = NULL; + pa_hashmap_free(objects, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} + +static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) + pa_log_debug("dbus_connection_register_object_path() failed."); + } +} + +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + pa_bool_t state_created = FALSE; + pa_bool_t connection_set_created = FALSE; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { + dbus_state = pa_xnew0(struct dbus_state, 1); + dbus_state->core = c; + pa_hashmap_put(c->shared, "dbus-state", dbus_state); + state_created = TRUE; + } + + if (!(connections = dbus_state->connections)) { + connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + dbus_state->connections = connections; + connection_set_created = TRUE; + } + + if (pa_idxset_get_by_data(connections, conn, NULL)) + goto fail; /* The connection was already registered. */ + + register_all_objects(dbus_state, conn); + + pa_idxset_put(connections, dbus_connection_ref(conn), NULL); + + return 0; + +fail: + if (connection_set_created) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (state_created) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return -1; +} + +static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(dbus_state); + pa_assert(conn); + + if (!dbus_state->objects) + return; + + while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { + if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) + pa_log_debug("dus_connection_unregister_object_path() failed."); + } +} + +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) { + struct dbus_state *dbus_state; + pa_idxset *connections; + + pa_assert(c); + pa_assert(conn); + + if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) + return -1; + + if (!(connections = dbus_state->connections)) + return -1; + + if (!pa_idxset_remove_by_data(connections, conn, NULL)) + return -1; + + unregister_all_objects(dbus_state, conn); + + dbus_connection_unref(conn); + + if (pa_idxset_isempty(connections)) { + dbus_state->connections = NULL; + pa_idxset_free(connections, NULL, NULL); + } + + if (!dbus_state->objects && !dbus_state->connections) { + pa_hashmap_remove(c->shared, "dbus-state"); + pa_xfree(dbus_state); + } + + return 0; +} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 26bd05d4..23c7c221 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -22,6 +22,8 @@ USA. ***/ +#include + #include #include @@ -30,10 +32,42 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +/* NOTE: These functions may only be called from the main thread. */ + /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the * string. This function may fail in some rare cases, in which case NULL is * returned. */ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); +/* Registers the given interface to the given object path. This is additive: it + * doesn't matter whether or not the object has already been registered; if it + * is, then its interface set is just extended. + * + * Introspection requests are handled automatically. For that to work, the + * caller gives an XML snippet containing the interface introspection element. + * All interface snippets are automatically combined to provide the final + * introspection string. + * + * The introspection snippet contains the interface name and the methods, but + * since this function doesn't do XML parsing, the interface name and the set + * of method names have to be supplied separately. If the interface doesn't + * contain any methods, NULL may be given as the methods parameter, otherwise + * the methods parameter must be a NULL-terminated array of strings. + * + * Fails and returns a negative number if the object already has the interface + * registered. */ +int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); + +/* Returns a negative number if the given object doesn't have the given + * interface registered. */ +int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface); + +/* Fails and returns a negative number if the connection is already + * registered. */ +int pa_dbus_register_connection(pa_core *c, DBusConnection *conn); + +/* Returns a negative number if the connection wasn't registered. */ +int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn); + #endif diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c new file mode 100644 index 00000000..f59c478a --- /dev/null +++ b/src/pulsecore/dbus-objs/core.c @@ -0,0 +1,120 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include "core.h" + +#define OBJECT_NAME "/org/pulseaudio/core" +#define INTERFACE_NAME "org.pulseaudio.Core" + +struct pa_dbusobj_core { + pa_core *core; +}; + +static const char *introspection_snippet = + " " + " " + " " + " " + " "; + +static const char *methods[] = { + "Test", + NULL +}; + +static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusMessage *reply = NULL; + const char *reply_message = "Hello!"; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!(reply = dbus_message_new_method_return(msg))) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) + goto fail; + + if (!dbus_connection_send(conn, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + +fail: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + pa_dbusobj_core *c = user_data; + + pa_assert(connection); + pa_assert(message); + pa_assert(c); + + if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) + return handle_test(connection, message, c); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { + pa_dbusobj_core *c; + + pa_assert(core); + + c = pa_xnew(pa_dbusobj_core, 1); + c->core = core; + + pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + + return c; +} + +void pa_dbusobj_core_free(pa_dbusobj_core *c) { + pa_assert(c); + + pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + + pa_xfree(c); +} diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h new file mode 100644 index 00000000..8e59cc3d --- /dev/null +++ b/src/pulsecore/dbus-objs/core.h @@ -0,0 +1,39 @@ +#ifndef foodbusobjscorehfoo +#define foodbusobjscorehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus object at path /org/pulseaudio/core. + * The implemented interface is org.pulseaudio.Core. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Core interface + * documentation. + */ + +#include + +typedef struct pa_dbusobj_core pa_dbusobj_core; + +pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core); +void pa_dbusobj_core_free(pa_dbusobj_core *c); + +#endif -- cgit From 3c6a0acc98a8326ad7a19c29005bba353396a88b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 19 Jun 2009 15:17:57 +0300 Subject: dbus-protocol: Implement TCP server startup. --- src/daemon/server-lookup.c | 11 +++------- src/daemon/system.pa.in | 3 ++- src/modules/module-dbus-protocol.c | 43 ++++++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 2d2d8ce6..33d6b24c 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -120,14 +120,11 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m return DBUS_HANDLER_RESULT_HANDLED; } - pa_client_conf_free(conf); - if (conf->default_dbus_server) { - if (!(address = dbus_address_escape_value(conf->default_dbus_server))) - goto oom; + address = pa_xstrdup(conf->default_dbus_server); } else { if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed."))) + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) goto fail; if (!dbus_connection_send(conn, reply, NULL)) goto oom; @@ -144,10 +141,8 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m if (!dbus_connection_send(conn, reply, NULL)) goto oom; - pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address); - + pa_client_conf_free(conf); pa_xfree(address); - dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in index 5541bbe4..0ca32bd3 100755 --- a/src/daemon/system.pa.in +++ b/src/daemon/system.pa.in @@ -33,7 +33,8 @@ load-module module-detect load-module module-esound-protocol-unix .endif .ifexists module-dbus-protocol@PA_SOEXT@ -load-module module-dbus-protocol +### If you want to allow TCP connections, set access to "remote" or "local,remote". +load-module module-dbus-protocol access=local .endif load-module module-native-protocol-unix diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 74dfeef0..8be9d1aa 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -54,6 +54,11 @@ PA_MODULE_VERSION(PACKAGE_VERSION); #define CLEANUP_INTERVAL 10 /* seconds */ +enum server_type { + SERVER_TYPE_LOCAL, + SERVER_TYPE_TCP +}; + struct server; struct connection; @@ -75,6 +80,7 @@ struct userdata { struct server { struct userdata *userdata; + enum server_type type; DBusServer *dbus_server; }; @@ -114,6 +120,12 @@ static void client_kill_cb(pa_client *c) { pa_log_info("Connection killed."); } +static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { + pa_log_debug("Allowing connection by user %lu.", uid); + + return TRUE; +} + /* Called by D-Bus when a new client connection is received. */ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) { struct server *s = data; @@ -131,8 +143,17 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); - if (!client) + if (!client) { + dbus_connection_close(new_connection); return; + } + + if (s->type == SERVER_TYPE_TCP) { + /* FIXME: Here we allow anyone from anywhere to access the server, + * anonymously. Access control should be configurable. */ + dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL); + dbus_connection_set_allow_anonymous(new_connection, TRUE); + } c = pa_xnew(struct connection, 1); c->server = s; @@ -334,7 +355,7 @@ static void server_free(struct server *s) { pa_xfree(s); } -static struct server *start_server(struct userdata *u, const char *address) { +static struct server *start_server(struct userdata *u, const char *address, enum server_type type) { /* XXX: We assume that when we unref the DBusServer instance at module * shutdown, nobody else holds any references to it. If we stop assuming * that someday, dbus_server_set_new_connection_function, @@ -390,7 +411,7 @@ static struct server *start_local_server(struct userdata *u) { address = pa_get_dbus_address_from_server_type(u->module->core->server_type); - s = start_server(u, address); /* May return NULL */ + s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */ pa_xfree(address); @@ -398,8 +419,18 @@ static struct server *start_local_server(struct userdata *u) { } static struct server *start_tcp_server(struct userdata *u) { - pa_log("start_tcp_server(): Not implemented!"); - return NULL; + struct server *s = NULL; + char *address = NULL; + + pa_assert(u); + + address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port); + + s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */ + + pa_xfree(address); + + return s; } static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) { @@ -420,7 +451,7 @@ static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *re *remote_access = TRUE; } else if (!strcmp(value, "local,remote")) { *local_access = TRUE; - *local_access = TRUE; + *remote_access = TRUE; } else return -1; -- cgit From b152f3a052eca7225870a7dc4d8a719bee107f0f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 20 Jun 2009 10:17:46 +0300 Subject: module-dbus-protocol: Allow anyone to connect the daemon in system mode. --- src/modules/module-dbus-protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c index 8be9d1aa..46f64921 100644 --- a/src/modules/module-dbus-protocol.c +++ b/src/modules/module-dbus-protocol.c @@ -148,7 +148,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne return; } - if (s->type == SERVER_TYPE_TCP) { + if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) { /* FIXME: Here we allow anyone from anywhere to access the server, * anonymously. Access control should be configurable. */ dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL); -- cgit From 3bff2eee4bf02ff3bc2619079c3c5fe8a07040a9 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 30 Jun 2009 15:18:11 +0300 Subject: module-cli: Fix compilation by adding libpulsecommon to module_cli_la_LIBADD. --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6cddb83a..58668890 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1250,7 +1250,7 @@ module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR module_cli_la_SOURCES = modules/module-cli.c module_cli_la_LDFLAGS = $(MODULE_LDFLAGS) -module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la +module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la libpulsecommon-@PA_MAJORMINORMICRO@.la module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS) -- cgit From 6e2fec05ddd38fa460276202ca2f4ecf68761ed0 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 30 Jun 2009 15:21:20 +0300 Subject: server-lookup: Update the D-Bus identifiers to be versioned. --- src/daemon/main.c | 2 +- src/daemon/server-lookup.c | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/daemon/main.c b/src/daemon/main.c index 61b0c840..d320c9e4 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -943,7 +943,7 @@ int main(int argc, char *argv[]) { if (!conf->system_instance) { if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; - if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio"))) + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1"))) goto finish; } diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 33d6b24c..7c80d677 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -36,6 +36,9 @@ #include "server-lookup.h" +#define OBJECT_PATH "/org/pulseaudio1/server_lookup" +#define INTERFACE "org.pulseaudio.ServerLookup1" + struct pa_dbusobj_server_lookup { pa_core *core; pa_dbus_connection *conn; @@ -47,7 +50,7 @@ static const char introspection[] = "" " \n" - " " + " " " " " " " " @@ -182,7 +185,7 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress")) + if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress")) return handle_get_address(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -212,8 +215,8 @@ pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) { goto fail; } - if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) { - pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup."); + if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) { + pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH "."); goto fail; } @@ -234,8 +237,8 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { if (sl->path_registered) { pa_assert(sl->conn); - if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup")) - pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup."); + if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH)) + pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH "."); } if (sl->conn) -- cgit From 5c7952e4fa5e560f64255ef173c3e6570bee433a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 3 Jul 2009 02:49:07 +0300 Subject: dbus: Implement the Name property of the core object. --- src/daemon/main.c | 2 +- src/daemon/server-lookup.c | 414 ++++++++++++++++++++++++++++++++++------- src/pulsecore/dbus-common.c | 215 +++++++++++++++++---- src/pulsecore/dbus-common.h | 28 ++- src/pulsecore/dbus-objs/core.c | 414 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 932 insertions(+), 141 deletions(-) (limited to 'src') diff --git a/src/daemon/main.c b/src/daemon/main.c index d320c9e4..f4209859 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -943,7 +943,7 @@ int main(int argc, char *argv[]) { if (!conf->system_instance) { if (!(server_lookup = pa_dbusobj_server_lookup_new(c))) goto finish; - if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1"))) + if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1"))) goto finish; } diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index 7c80d677..ebacc099 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -37,7 +37,7 @@ #include "server-lookup.h" #define OBJECT_PATH "/org/pulseaudio1/server_lookup" -#define INTERFACE "org.pulseaudio.ServerLookup1" +#define INTERFACE "org.PulseAudio.ServerLookup1" struct pa_dbusobj_server_lookup { pa_core *core; @@ -50,17 +50,31 @@ static const char introspection[] = "" " \n" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; static void unregister_cb(DBusConnection *conn, void *user_data) { pa_dbusobj_server_lookup *sl = user_data; @@ -72,105 +86,352 @@ static void unregister_cb(DBusConnection *conn, void *user_data) { } static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; const char *i = introspection; DBusMessage *reply = NULL; pa_assert(conn); pa_assert(msg); - if (!(reply = dbus_message_new_method_return(msg))) - goto fail; + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) - goto fail; +finish: + if (reply) + dbus_message_unref(reply); - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + return r; +} - dbus_message_unref(reply); +enum get_address_result_t { + SUCCESS, + FAILED_TO_LOAD_CLIENT_CONF, + SERVER_FROM_TYPE_FAILED +}; - return DBUS_HANDLER_RESULT_HANDLED; +/* Caller frees the returned address. */ +static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) { + enum get_address_result_t r = SUCCESS; + pa_client_conf *conf = pa_client_conf_new(); -fail: - if (reply) - dbus_message_unref(reply); + *address = NULL; - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + if (pa_client_conf_load(conf, NULL) < 0) { + r = FAILED_TO_LOAD_CLIENT_CONF; + goto finish; + } -oom: - if (reply) - dbus_message_unref(reply); + if (conf->default_dbus_server) + *address = pa_xstrdup(conf->default_dbus_server); + else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) { + r = SERVER_FROM_TYPE_FAILED; + goto finish; + } - return DBUS_HANDLER_RESULT_NEED_MEMORY; +finish: + pa_client_conf_free(conf); + return r; } static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; DBusMessage *reply = NULL; - pa_client_conf *conf = NULL; char *address = NULL; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; pa_assert(conn); pa_assert(msg); pa_assert(sl); - conf = pa_client_conf_new(); + switch (get_address(sl->core->server_type, &address)) { + case SUCCESS: + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + case FAILED_TO_LOAD_CLIENT_CONF: + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + case SERVER_FROM_TYPE_FAILED: + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + default: + pa_assert_not_reached(); + } - if (pa_client_conf_load(conf, NULL) < 0) { - if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) - goto fail; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (conf->default_dbus_server) { - address = pa_xstrdup(conf->default_dbus_server); - } else { - if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) - goto fail; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; - return DBUS_HANDLER_RESULT_HANDLED; +finish: + pa_xfree(address); + if (reply) + dbus_message_unref(reply); + + return r; +} + +static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; } - if (!(reply = dbus_message_new_method_return(msg))) - goto oom; + if (*interface && !pa_streq(interface, INTERFACE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) - goto fail; + if (!pa_streq(property, "Address")) { + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + r = handle_get_address(conn, msg, sl); - pa_client_conf_free(conf); - pa_xfree(address); - dbus_message_unref(reply); +finish: + if (reply) + dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + return r; +} -fail: - if (conf) - pa_client_conf_free(conf); +static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - pa_xfree(address); + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (!pa_streq(property, "Address")) { + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return r; +} -oom: - if (conf) - pa_client_conf_free(conf); +static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + DBusMessage *reply = NULL; + const char *property = "Address"; + char *interface = NULL; + char *address = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter variant_iter; - pa_xfree(address); + pa_assert(conn); + pa_assert(msg); + pa_assert(sl); + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + switch (get_address(sl->core->server_type, &address)) { + case SUCCESS: + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + case FAILED_TO_LOAD_CLIENT_CONF: + if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + case SERVER_FROM_TYPE_FAILED: + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + default: + pa_assert_not_reached(); + } + +finish: + pa_xfree(address); if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return r; } static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -182,11 +443,24 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */ - if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect"))) return handle_introspect(conn, msg, sl); - if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress")) - return handle_get_address(conn, msg, sl); + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get"))) + return handle_get(conn, msg, sl); + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set"))) + return handle_set(conn, msg, sl); + + if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") || + (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll"))) + return handle_get_all(conn, msg, sl); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c index 350add82..6562cda2 100644 --- a/src/pulsecore/dbus-common.c +++ b/src/pulsecore/dbus-common.c @@ -48,6 +48,7 @@ struct object_entry { struct interface_entry { char *name; + char **properties; char **methods; char *introspection_snippet; DBusObjectPathMessageFunction receive; @@ -106,48 +107,152 @@ static void update_introspection(struct object_entry *oe) { buf = pa_strbuf_new(); pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, "\n"); while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) pa_strbuf_puts(buf, iface_entry->introspection_snippet); - pa_strbuf_puts(buf, " " - " " - " " - " " - " "); - - pa_strbuf_puts(buf, ""); + pa_strbuf_puts(buf, " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + + pa_strbuf_puts(buf, "\n"); pa_xfree(oe->introspection); oe->introspection = pa_strbuf_tostring_free(buf); } -static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) { - const char *interface; - struct interface_entry *iface_entry; +enum find_result_t { + SUCCESS, + NO_SUCH_PROPERTY, + NO_SUCH_METHOD, + INVALID_MESSAGE_ARGUMENTS +}; + +static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) { void *state = NULL; pa_assert(obj_entry); - pa_assert(msg); + pa_assert(property); + pa_assert(entry); + + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_property; + char **pos = (*entry)->properties; + + while ((iface_property = *pos++)) { + if (pa_streq(iface_property, property)) + return SUCCESS; + } + } + + return NO_SUCH_PROPERTY; +} - if ((interface = dbus_message_get_interface(msg))) - return pa_hashmap_get(obj_entry->interfaces, interface); +static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) { + void *state = NULL; - /* NULL interface, we'll have to search for an interface that contains the - * method. */ + pa_assert(obj_entry); + pa_assert(method); + pa_assert(entry); - while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *method; - char **pos = iface_entry->methods; + while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + char *iface_method; + char **pos = (*entry)->methods; - while ((method = *pos++)) { - if (!strcmp(dbus_message_get_member(msg), method)) - return iface_entry; + while ((iface_method = *pos++)) { + if (pa_streq(iface_method, method)) + return SUCCESS; } } - return NULL; + return NO_SUCH_METHOD; +} + +static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + const char *property; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + if (dbus_message_has_member(msg, "GetAll")) { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else { + pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces))); + return SUCCESS; + } + } else { + pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set")); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + } else + return find_interface_by_property(obj_entry, property, entry); + } +} + +static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(entry); + + *entry = NULL; + + if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) + return find_interface_from_properties_call(obj_entry, msg, entry); + + else if ((interface = dbus_message_get_interface(msg))) { + if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return SUCCESS; + else + return NO_SUCH_METHOD; + + } else { /* The method call doesn't contain an interface. */ + if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { + if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS) + return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + else + /* Assume this is a .Properties call. */ + return find_interface_from_properties_call(obj_entry, msg, entry); + + } else /* This is not a .Properties call. */ + return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry); + } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -166,7 +271,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) { + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { if (!(reply = dbus_message_new_method_return(message))) goto oom; @@ -183,10 +289,38 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa return DBUS_HANDLER_RESULT_HANDLED; } - if (!(iface_entry = find_interface(obj_entry, message))) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + switch (find_interface(obj_entry, message, &iface_entry)) { + case SUCCESS: + return iface_entry->receive(connection, message, iface_entry->userdata); + + case NO_SUCH_PROPERTY: + if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property"))) + goto fail; - return iface_entry->receive(connection, message, iface_entry->userdata); + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + case NO_SUCH_METHOD: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + case INVALID_MESSAGE_ARGUMENTS: + if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) + goto fail; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + default: + pa_assert_not_reached(); + } fail: if (reply) @@ -226,23 +360,30 @@ static void register_object(struct dbus_state *dbus_state, struct object_entry * } } -static char **copy_methods(const char * const *methods) { +static char **copy_strarray(const char * const *array) { unsigned n = 0; char **copy; unsigned i; - while (methods[n++]) + while (array[n++]) ; copy = pa_xnew0(char *, n); for (i = 0; i < n - 1; ++i) - copy[i] = pa_xstrdup(methods[i]); + copy[i] = pa_xstrdup(array[i]); return copy; } -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) { +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata) { struct dbus_state *dbus_state; pa_hashmap *objects; struct object_entry *obj_entry; @@ -283,7 +424,8 @@ int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, c iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(interface); - iface_entry->methods = copy_methods(methods); + iface_entry->properties = copy_strarray(properties); + iface_entry->methods = copy_strarray(methods); iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); iface_entry->receive = receive_cb; iface_entry->userdata = userdata; @@ -331,13 +473,13 @@ static void unregister_object(struct dbus_state *dbus_state, struct object_entry } } -static void free_methods(char **methods) { - char **pos = methods; +static void free_strarray(char **array) { + char **pos = array; while (*pos++) pa_xfree(*pos); - pa_xfree(methods); + pa_xfree(array); } int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { @@ -365,7 +507,8 @@ int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface update_introspection(obj_entry); pa_xfree(iface_entry->name); - free_methods(iface_entry->methods); + free_strarray(iface_entry->properties); + free_strarray(iface_entry->methods); pa_xfree(iface_entry->introspection_snippet); pa_xfree(iface_entry); diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h index 23c7c221..4354c4ea 100644 --- a/src/pulsecore/dbus-common.h +++ b/src/pulsecore/dbus-common.h @@ -28,10 +28,12 @@ #include #define PA_DBUS_DEFAULT_PORT 24883 -#define PA_DBUS_SOCKET_NAME "dbus_socket" +#define PA_DBUS_SOCKET_NAME "dbus-socket" #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" + /* NOTE: These functions may only be called from the main thread. */ /* Returns the default address of the server type in the escaped form. For @@ -47,17 +49,27 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); * Introspection requests are handled automatically. For that to work, the * caller gives an XML snippet containing the interface introspection element. * All interface snippets are automatically combined to provide the final - * introspection string. + * introspection string for the object. * - * The introspection snippet contains the interface name and the methods, but - * since this function doesn't do XML parsing, the interface name and the set - * of method names have to be supplied separately. If the interface doesn't - * contain any methods, NULL may be given as the methods parameter, otherwise - * the methods parameter must be a NULL-terminated array of strings. + * The introspection snippet contains the interface name, the property names + * and the method namess, but since this function doesn't do XML parsing, the + * information needs to be given separately. Property and method names are + * given as a NULL-terminated array of strings. The interface name is used for + * message routing, and so are the property and method names too in case the + * client doesn't tell which interface he's trying to access; in absence of + * interface information from the client, the correct interface is searched + * based on the property or method name. * * Fails and returns a negative number if the object already has the interface * registered. */ -int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata); +int pa_dbus_add_interface(pa_core *c, + const char* path, + const char* interface, + const char * const *properties, + const char * const *methods, + const char* introspection_snippet, + DBusObjectPathMessageFunction receive_cb, + void *userdata); /* Returns a negative number if the given object doesn't have the given * interface registered. */ diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c index f59c478a..18bbf789 100644 --- a/src/pulsecore/dbus-objs/core.c +++ b/src/pulsecore/dbus-objs/core.c @@ -27,62 +27,414 @@ #include +#include #include #include #include "core.h" -#define OBJECT_NAME "/org/pulseaudio/core" -#define INTERFACE_NAME "org.pulseaudio.Core" +#define OBJECT_PATH "/org/pulseaudio1" +#define INTERFACE_CORE "org.PulseAudio.Core1" struct pa_dbusobj_core { pa_core *core; }; static const char *introspection_snippet = - " " - " " - " " - " " - " "; + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"; + +/* If you need to modify this list, note that handle_get_all() uses hard-coded + * indexes to point to these strings, so make sure the indexes don't go wrong + * there. */ +static const char *properties[] = { + "InterfaceRevision", + "Name", + "Version", + "Username", + "Hostname", + "DefaultChannels", + "DefaultSampleFormat", + "DefaultSampleRate", + "Sinks", + "FallbackSink", + "Sources", + "FallbackSource", + "PlaybackStreams", + "RecordStreams", + "Samples", + "Modules", + "Clients", + "Extensions", + NULL +}; static const char *methods[] = { - "Test", + "GetCardByName", + "GetSinkByName", + "GetSourceByName", + "GetSampleByName", + "UploadSample", + "LoadSampleFromFile", + "AddLazySample", + "AddLazySamplesFromDirectory", + "LoadModule", + "Exit", NULL }; -static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { +static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; DBusMessage *reply = NULL; - const char *reply_message = "Hello!"; + const char *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; pa_assert(conn); pa_assert(msg); pa_assert(c); - if (!(reply = dbus_message_new_method_return(msg))) - goto oom; + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID)) - goto fail; +static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - if (!dbus_connection_send(conn, reply, NULL)) - goto oom; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + r = handle_get_name(conn, msg, c); + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: + if (reply) + dbus_message_unref(reply); + + return r; +} - dbus_message_unref(reply); +static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + const char* interface; + const char* property; + DBusMessage *reply = NULL; - return DBUS_HANDLER_RESULT_HANDLED; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); -fail: + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (*interface && !pa_streq(interface, INTERFACE_CORE)) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + + if (pa_streq(property, "Name")) { + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + + if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return r; +} + +static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { + DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; + DBusMessage *reply = NULL; + char *interface = NULL; + char const *server_name = PACKAGE_NAME; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter variant_iter; -oom: + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { + if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { + r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + goto finish; + } + + if (!(reply = dbus_message_new_method_return(msg))) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + dbus_message_iter_init_append(reply, &msg_iter); + if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */ + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + if (!dbus_connection_send(conn, reply, NULL)) { + r = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto finish; + } + r = DBUS_HANDLER_RESULT_HANDLED; + +finish: if (reply) dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return r; } static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { @@ -92,8 +444,17 @@ static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *mes pa_assert(message); pa_assert(c); - if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test")) - return handle_test(connection, message, c); + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get"))) + return handle_get(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set"))) + return handle_set(connection, message, c); + + if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll"))) + return handle_get_all(connection, message, c); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -106,7 +467,8 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { c = pa_xnew(pa_dbusobj_core, 1); c->core = core; - pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c); + pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c); + return c; } @@ -114,7 +476,7 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { void pa_dbusobj_core_free(pa_dbusobj_core *c) { pa_assert(c); - pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME); + pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE); pa_xfree(c); } -- cgit From 9347e90fed732dac619bb88f6518c344e7436447 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 21 Jul 2009 00:02:27 +0300 Subject: Finish the Core dbus interface. --- src/Makefile.am | 14 +- src/daemon/server-lookup.c | 4 +- src/modules/module-dbus-protocol.c | 575 ----------------------- src/pulsecore/core-util.c | 22 + src/pulsecore/core-util.h | 7 + src/pulsecore/dbus-common.c | 646 -------------------------- src/pulsecore/dbus-common.h | 85 ---- src/pulsecore/dbus-objs/core.c | 482 ------------------- src/pulsecore/dbus-objs/core.h | 39 -- src/pulsecore/dbus-util.c | 333 +++++++++++++ src/pulsecore/dbus-util.h | 29 ++ src/pulsecore/namereg.c | 63 +-- src/pulsecore/protocol-dbus.c | 930 +++++++++++++++++++++++++++++++++++++ src/pulsecore/protocol-dbus.h | 158 +++++++ 14 files changed, 1526 insertions(+), 1861 deletions(-) delete mode 100644 src/modules/module-dbus-protocol.c delete mode 100644 src/pulsecore/dbus-common.c delete mode 100644 src/pulsecore/dbus-common.h delete mode 100644 src/pulsecore/dbus-objs/core.c delete mode 100644 src/pulsecore/dbus-objs/core.h create mode 100644 src/pulsecore/protocol-dbus.c create mode 100644 src/pulsecore/protocol-dbus.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 58668890..47eb6479 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -852,7 +852,7 @@ endif if HAVE_DBUS libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += \ pulsecore/dbus-shared.c pulsecore/dbus-shared.h \ - pulsecore/dbus-common.c pulsecore/dbus-common.h + pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS) libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS) endif @@ -1225,7 +1225,7 @@ SYMDEF_FILES = \ modules/module-augment-properties-symdef.h \ modules/module-cork-music-on-phone-symdef.h \ modules/module-console-kit-symdef.h \ - modules/module-dbus-protocol-symdef.h + modules/dbus/module-dbus-protocol-symdef.h EXTRA_DIST += $(SYMDEF_FILES) BUILT_SOURCES += $(SYMDEF_FILES) @@ -1277,8 +1277,14 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI # D-Bus protocol module_dbus_protocol_la_SOURCES = \ - pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \ - modules/module-dbus-protocol.c + modules/dbus/iface-card.c modules/dbus/iface-card.h \ + modules/dbus/iface-client.c modules/dbus/iface-client.h \ + modules/dbus/iface-core.c modules/dbus/iface-core.h \ + modules/dbus/iface-device.c modules/dbus/iface-device.h \ + modules/dbus/iface-module.c modules/dbus/iface-module.h \ + modules/dbus/iface-sample.c modules/dbus/iface-sample.h \ + modules/dbus/iface-stream.c modules/dbus/iface-stream.h \ + modules/dbus/module-dbus-protocol.c module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS) module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c index ebacc099..45796e72 100644 --- a/src/daemon/server-lookup.c +++ b/src/daemon/server-lookup.c @@ -30,13 +30,13 @@ #include #include -#include #include #include +#include #include "server-lookup.h" -#define OBJECT_PATH "/org/pulseaudio1/server_lookup" +#define OBJECT_PATH "/org/pulseaudio/server_lookup1" #define INTERFACE "org.PulseAudio.ServerLookup1" struct pa_dbusobj_server_lookup { diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c deleted file mode 100644 index f7c1f0a9..00000000 --- a/src/modules/module-dbus-protocol.c +++ /dev/null @@ -1,575 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - Copyright 2006 Lennart Poettering - Copyright 2006 Shams E. King - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio 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 Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "module-dbus-protocol-symdef.h" - -PA_MODULE_DESCRIPTION("D-Bus interface"); -PA_MODULE_USAGE( - "access=local|remote|local,remote " - "tcp_port="); -PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_AUTHOR("Tanu Kaskinen"); -PA_MODULE_VERSION(PACKAGE_VERSION); - -#define CLEANUP_INTERVAL 10 /* seconds */ - -enum server_type { - SERVER_TYPE_LOCAL, - SERVER_TYPE_TCP -}; - -struct server; -struct connection; - -struct userdata { - pa_module *module; - pa_bool_t local_access; - pa_bool_t remote_access; - uint32_t tcp_port; - - struct server *local_server; - struct server *tcp_server; - - pa_idxset *connections; - - pa_time_event *cleanup_event; - - pa_dbusobj_core *core_object; -}; - -struct server { - struct userdata *userdata; - enum server_type type; - DBusServer *dbus_server; -}; - -struct connection { - struct server *server; - pa_dbus_wrap_connection *wrap_conn; - pa_client *client; -}; - -static const char* const valid_modargs[] = { - "access", - "tcp_port", - NULL -}; - -static void connection_free(struct connection *c) { - pa_assert(c); - - pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0); - - pa_client_free(c->client); - pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL)); - pa_dbus_wrap_connection_free(c->wrap_conn); - pa_xfree(c); -} - -/* Called from pa_client_kill(). */ -static void client_kill_cb(pa_client *c) { - struct connection *conn; - - pa_assert(c); - pa_assert(c->userdata); - - conn = c->userdata; - connection_free(conn); - - pa_log_info("Connection killed."); -} - -static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { - pa_log_debug("Allowing connection by user %lu.", uid); - - return TRUE; -} - -/* Called by D-Bus when a new client connection is received. */ -static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) { - struct server *s = data; - struct connection *c; - pa_client_new_data new_data; - pa_client *client; - - pa_assert(new_connection); - pa_assert(s); - - pa_client_new_data_init(&new_data); - new_data.module = s->userdata->module; - new_data.driver = __FILE__; - pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ - client = pa_client_new(s->userdata->module->core, &new_data); - pa_client_new_data_done(&new_data); - - if (!client) { - dbus_connection_close(new_connection); - return; - } - - if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) { - /* FIXME: Here we allow anyone from anywhere to access the server, - * anonymously. Access control should be configurable. */ - dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL); - dbus_connection_set_allow_anonymous(new_connection, TRUE); - } - - c = pa_xnew(struct connection, 1); - c->server = s; - c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection); - c->client = client; - - c->client->kill = client_kill_cb; - c->client->send_event = NULL; /* TODO: Implement this. */ - c->client->userdata = c; - - pa_idxset_put(s->userdata->connections, c, NULL); - - pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0); -} - -/* Called by PA mainloop when a D-Bus fd watch event needs handling. */ -static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { - unsigned int flags = 0; - DBusWatch *watch = userdata; - -#if HAVE_DBUS_WATCH_GET_UNIX_FD - pa_assert(fd == dbus_watch_get_unix_fd(watch)); -#else - pa_assert(fd == dbus_watch_get_fd(watch)); -#endif - - if (!dbus_watch_get_enabled(watch)) { - pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd); - return; - } - - if (events & PA_IO_EVENT_INPUT) - flags |= DBUS_WATCH_READABLE; - if (events & PA_IO_EVENT_OUTPUT) - flags |= DBUS_WATCH_WRITABLE; - if (events & PA_IO_EVENT_HANGUP) - flags |= DBUS_WATCH_HANGUP; - if (events & PA_IO_EVENT_ERROR) - flags |= DBUS_WATCH_ERROR; - - dbus_watch_handle(watch, flags); -} - -/* Called by PA mainloop when a D-Bus timer event needs handling. */ -static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) { - DBusTimeout *timeout = userdata; - - if (dbus_timeout_get_enabled(timeout)) { - struct timeval next = *tv; - dbus_timeout_handle(timeout); - - /* restart it for the next scheduled time */ - pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - mainloop->time_restart(e, &next); - } -} - -/* Translates D-Bus fd watch event flags to PA IO event flags. */ -static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { - unsigned int flags; - pa_io_event_flags_t events = 0; - - pa_assert(watch); - - flags = dbus_watch_get_flags(watch); - - /* no watch flags for disabled watches */ - if (!dbus_watch_get_enabled(watch)) - return PA_IO_EVENT_NULL; - - if (flags & DBUS_WATCH_READABLE) - events |= PA_IO_EVENT_INPUT; - if (flags & DBUS_WATCH_WRITABLE) - events |= PA_IO_EVENT_OUTPUT; - - return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; -} - -/* Called by D-Bus when a D-Bus fd watch event is added. */ -static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - mainloop = s->userdata->module->core->mainloop; - - ev = mainloop->io_new( - mainloop, -#if HAVE_DBUS_WATCH_GET_UNIX_FD - dbus_watch_get_unix_fd(watch), -#else - dbus_watch_get_fd(watch), -#endif - get_watch_flags(watch), io_event_cb, watch); - - dbus_watch_set_data(watch, ev, NULL); - - return TRUE; -} - -/* Called by D-Bus when a D-Bus fd watch event is removed. */ -static void watch_remove_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - if ((ev = dbus_watch_get_data(watch))) - s->userdata->module->core->mainloop->io_free(ev); -} - -/* Called by D-Bus when a D-Bus fd watch event is toggled. */ -static void watch_toggled_cb(DBusWatch *watch, void *data) { - struct server *s = data; - pa_io_event *ev; - - pa_assert(watch); - pa_assert(s); - - pa_assert_se(ev = dbus_watch_get_data(watch)); - - /* get_watch_flags() checks if the watch is enabled */ - s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch)); -} - -/* Called by D-Bus when a D-Bus timer event is added. */ -static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_time_event *ev; - struct timeval tv; - - pa_assert(timeout); - pa_assert(s); - - if (!dbus_timeout_get_enabled(timeout)) - return FALSE; - - mainloop = s->userdata->module->core->mainloop; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout); - - dbus_timeout_set_data(timeout, ev, NULL); - - return TRUE; -} - -/* Called by D-Bus when a D-Bus timer event is removed. */ -static void timeout_remove_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_time_event *ev; - - pa_assert(timeout); - pa_assert(s); - - if ((ev = dbus_timeout_get_data(timeout))) - s->userdata->module->core->mainloop->time_free(ev); -} - -/* Called by D-Bus when a D-Bus timer event is toggled. */ -static void timeout_toggled_cb(DBusTimeout *timeout, void *data) { - struct server *s = data; - pa_mainloop_api *mainloop; - pa_time_event *ev; - - pa_assert(timeout); - pa_assert(s); - - mainloop = s->userdata->module->core->mainloop; - - pa_assert_se(ev = dbus_timeout_get_data(timeout)); - - if (dbus_timeout_get_enabled(timeout)) { - struct timeval tv; - - pa_gettimeofday(&tv); - pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); - - mainloop->time_restart(ev, &tv); - } else - mainloop->time_restart(ev, NULL); -} - -static void server_free(struct server *s) { - pa_assert(s); - - if (s->dbus_server) { - dbus_server_disconnect(s->dbus_server); - dbus_server_unref(s->dbus_server); - } - - pa_xfree(s); -} - -static struct server *start_server(struct userdata *u, const char *address, enum server_type type) { - /* XXX: We assume that when we unref the DBusServer instance at module - * shutdown, nobody else holds any references to it. If we stop assuming - * that someday, dbus_server_set_new_connection_function, - * dbus_server_set_watch_functions and dbus_server_set_timeout_functions - * calls should probably register free callbacks, instead of providing NULL - * as they do now. */ - - struct server *s = NULL; - DBusError error; - - pa_assert(u); - pa_assert(address); - - dbus_error_init(&error); - - s = pa_xnew0(struct server, 1); - s->userdata = u; - s->dbus_server = dbus_server_listen(address, &error); - - if (dbus_error_is_set(&error)) { - pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message); - goto fail; - } - - dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL); - - if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) { - pa_log("dbus_server_set_watch_functions() ran out of memory."); - goto fail; - } - - if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) { - pa_log("dbus_server_set_timeout_functions() ran out of memory."); - goto fail; - } - - return s; - -fail: - if (s) - server_free(s); - - dbus_error_free(&error); - - return NULL; -} - -static struct server *start_local_server(struct userdata *u) { - struct server *s = NULL; - char *address = NULL; - - pa_assert(u); - - address = pa_get_dbus_address_from_server_type(u->module->core->server_type); - - s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */ - - pa_xfree(address); - - return s; -} - -static struct server *start_tcp_server(struct userdata *u) { - struct server *s = NULL; - char *address = NULL; - - pa_assert(u); - - address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port); - - s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */ - - pa_xfree(address); - - return s; -} - -static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) { - const char *value = NULL; - - pa_assert(ma); - pa_assert(local_access); - pa_assert(remote_access); - - if (!(value = pa_modargs_get_value(ma, "access", NULL))) - return 0; - - if (!strcmp(value, "local")) { - *local_access = TRUE; - *remote_access = FALSE; - } else if (!strcmp(value, "remote")) { - *local_access = FALSE; - *remote_access = TRUE; - } else if (!strcmp(value, "local,remote")) { - *local_access = TRUE; - *remote_access = TRUE; - } else - return -1; - - return 0; -} - -/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */ -static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { - struct userdata *u = userdata; - struct connection *conn = NULL; - uint32_t idx; - struct timeval cleanup_timeval; - unsigned free_count = 0; - - for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) { - if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) { - connection_free(conn); - ++free_count; - } - } - - if (free_count > 0) - pa_log_debug("Freed %u dead D-Bus client connections.", free_count); - - pa_gettimeofday(&cleanup_timeval); - cleanup_timeval.tv_sec += CLEANUP_INTERVAL; - u->module->core->mainloop->time_restart(e, &cleanup_timeval); -} - -int pa__init(pa_module *m) { - struct userdata *u = NULL; - pa_modargs *ma = NULL; - struct timeval cleanup_timeval; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->module = m; - u->local_access = TRUE; - u->remote_access = FALSE; - u->tcp_port = PA_DBUS_DEFAULT_PORT; - - if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) { - pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL)); - goto fail; - } - - if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) { - pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL)); - goto fail; - } - - if (u->local_access && !(u->local_server = start_local_server(u))) { - pa_log("Starting the local D-Bus server failed."); - goto fail; - } - - if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) { - pa_log("Starting the D-Bus server for remote connections failed."); - goto fail; - } - - u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - pa_gettimeofday(&cleanup_timeval); - cleanup_timeval.tv_sec += CLEANUP_INTERVAL; - u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u); - - u->core_object = pa_dbusobj_core_new(m->core); - - return 0; - -fail: - if (ma) - pa_modargs_free(ma); - - pa__done(m); - - return -1; -} - -/* Called by idxset when the connection set is freed. */ -static void connection_free_cb(void *p, void *userdata) { - struct connection *conn = p; - - pa_assert(conn); - - connection_free(conn); -} - -void pa__done(pa_module *m) { - struct userdata *u; - - pa_assert(m); - - if (!(u = m->userdata)) - return; - - if (u->core_object) - pa_dbusobj_core_free(u->core_object); - - if (u->cleanup_event) - m->core->mainloop->time_free(u->cleanup_event); - - if (u->connections) - pa_idxset_free(u->connections, connection_free_cb, NULL); - - if (u->tcp_server) - server_free(u->tcp_server); - - if (u->local_server) - server_free(u->local_server); - - pa_xfree(u); - m->userdata = NULL; -} diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 04e7eb24..ebddf363 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2656,6 +2656,28 @@ char *pa_replace(const char*s, const char*a, const char *b) { return pa_strbuf_tostring_free(sb); } +char *pa_escape(const char *p, const char *chars) { + const char *s; + const char *c; + pa_strbuf *buf = pa_strbuf_new(); + + for (s = p; *s; ++s) { + if (*s == '\\') + pa_strbuf_putc(buf, '\\'); + else if (chars) { + for (c = chars; *c; ++c) { + if (*s == *c) { + pa_strbuf_putc(buf, '\\'); + break; + } + } + } + pa_strbuf_putc(buf, *s); + } + + return pa_strbuf_tostring_free(buf); +} + char *pa_unescape(char *p) { char *s, *d; pa_bool_t escaped = FALSE; diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 96a0480a..aaa51bd6 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -220,6 +220,13 @@ unsigned pa_ncpus(void); char *pa_replace(const char*s, const char*a, const char *b); +/* Escapes p by inserting backslashes in front of backslashes. chars is a + * regular (ie. NULL-terminated) string containing additional characters that + * should be escaped. chars can be NULL. The caller has to free the returned + * string. */ +char *pa_escape(const char *p, const char *chars); + +/* Does regular backslash unescaping. Returns the argument p. */ char *pa_unescape(char *p); char *pa_realpath(const char *path); diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c deleted file mode 100644 index 6562cda2..00000000 --- a/src/pulsecore/dbus-common.c +++ /dev/null @@ -1,646 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio 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 Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include -#include -#include - -#include "dbus-common.h" - -struct dbus_state { - pa_core *core; - pa_hashmap *objects; /* Object path -> struct object_entry */ - pa_idxset *connections; /* DBusConnections */ -}; - -struct object_entry { - char *path; - pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ - char *introspection; -}; - -struct interface_entry { - char *name; - char **properties; - char **methods; - char *introspection_snippet; - DBusObjectPathMessageFunction receive; - void *userdata; -}; - -char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { - char *address = NULL; - char *runtime_path = NULL; - char *escaped_path = NULL; - - switch (server_type) { - case PA_SERVER_TYPE_USER: - if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) { - pa_log("pa_runtime_path() failed."); - break; - } - - if (!(escaped_path = dbus_address_escape_value(runtime_path))) { - pa_log("dbus_address_escape_value() failed."); - break; - } - - address = pa_sprintf_malloc("unix:path=%s", escaped_path); - break; - - case PA_SERVER_TYPE_SYSTEM: - if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) { - pa_log("dbus_address_escape_value() failed."); - break; - } - - address = pa_sprintf_malloc("unix:path=%s", escaped_path); - break; - - case PA_SERVER_TYPE_NONE: - address = pa_xnew0(char, 1); - break; - - default: - pa_assert_not_reached(); - } - - pa_xfree(runtime_path); - pa_xfree(escaped_path); - - return address; -} - -static void update_introspection(struct object_entry *oe) { - pa_strbuf *buf; - void *state = NULL; - struct interface_entry *iface_entry = NULL; - - pa_assert(oe); - - buf = pa_strbuf_new(); - pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); - pa_strbuf_puts(buf, "\n"); - - while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL))) - pa_strbuf_puts(buf, iface_entry->introspection_snippet); - - pa_strbuf_puts(buf, " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"); - - pa_strbuf_puts(buf, "\n"); - - pa_xfree(oe->introspection); - oe->introspection = pa_strbuf_tostring_free(buf); -} - -enum find_result_t { - SUCCESS, - NO_SUCH_PROPERTY, - NO_SUCH_METHOD, - INVALID_MESSAGE_ARGUMENTS -}; - -static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) { - void *state = NULL; - - pa_assert(obj_entry); - pa_assert(property); - pa_assert(entry); - - while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *iface_property; - char **pos = (*entry)->properties; - - while ((iface_property = *pos++)) { - if (pa_streq(iface_property, property)) - return SUCCESS; - } - } - - return NO_SUCH_PROPERTY; -} - -static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) { - void *state = NULL; - - pa_assert(obj_entry); - pa_assert(method); - pa_assert(entry); - - while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { - char *iface_method; - char **pos = (*entry)->methods; - - while ((iface_method = *pos++)) { - if (pa_streq(iface_method, method)) - return SUCCESS; - } - } - - return NO_SUCH_METHOD; -} - -static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { - const char *interface; - const char *property; - - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(entry); - - if (dbus_message_has_member(msg, "GetAll")) { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - } else { - pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces))); - return SUCCESS; - } - } else { - pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set")); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - } else - return find_interface_by_property(obj_entry, property, entry); - } -} - -static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) { - const char *interface; - - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(entry); - - *entry = NULL; - - if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) - return find_interface_from_properties_call(obj_entry, msg, entry); - - else if ((interface = dbus_message_get_interface(msg))) { - if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface))) - return SUCCESS; - else - return NO_SUCH_METHOD; - - } else { /* The method call doesn't contain an interface. */ - if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { - if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS) - return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ - else - /* Assume this is a .Properties call. */ - return find_interface_from_properties_call(obj_entry, msg, entry); - - } else /* This is not a .Properties call. */ - return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry); - } -} - -static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { - struct dbus_state *dbus_state = user_data; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - DBusMessage *reply = NULL; - - pa_assert(connection); - pa_assert(message); - pa_assert(dbus_state); - pa_assert(dbus_state->objects); - - if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message)))); - - if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect"); - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - } - - switch (find_interface(obj_entry, message, &iface_entry)) { - case SUCCESS: - return iface_entry->receive(connection, message, iface_entry->userdata); - - case NO_SUCH_PROPERTY: - if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property"))) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - - case NO_SUCH_METHOD: - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - case INVALID_MESSAGE_ARGUMENTS: - if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) - goto fail; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; - - default: - pa_assert_not_reached(); - } - -fail: - if (reply) - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static DBusObjectPathVTable vtable = { - .unregister_function = NULL, - .message_function = handle_message_cb, - .dbus_internal_pad1 = NULL, - .dbus_internal_pad2 = NULL, - .dbus_internal_pad3 = NULL, - .dbus_internal_pad4 = NULL -}; - -static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { - DBusConnection *conn; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(obj_entry); - - if (!dbus_state->connections) - return; - - while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { - if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) - pa_log_debug("dbus_connection_register_object_path() failed."); - } -} - -static char **copy_strarray(const char * const *array) { - unsigned n = 0; - char **copy; - unsigned i; - - while (array[n++]) - ; - - copy = pa_xnew0(char *, n); - - for (i = 0; i < n - 1; ++i) - copy[i] = pa_xstrdup(array[i]); - - return copy; -} - -int pa_dbus_add_interface(pa_core *c, - const char* path, - const char* interface, - const char * const *properties, - const char * const *methods, - const char* introspection_snippet, - DBusObjectPathMessageFunction receive_cb, - void *userdata) { - struct dbus_state *dbus_state; - pa_hashmap *objects; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - pa_bool_t state_created = FALSE; - pa_bool_t object_map_created = FALSE; - pa_bool_t obj_entry_created = FALSE; - - pa_assert(c); - pa_assert(path); - pa_assert(introspection_snippet); - pa_assert(receive_cb); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { - dbus_state = pa_xnew0(struct dbus_state, 1); - dbus_state->core = c; - pa_hashmap_put(c->shared, "dbus-state", dbus_state); - state_created = TRUE; - } - - if (!(objects = dbus_state->objects)) { - objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - dbus_state->objects = objects; - object_map_created = TRUE; - } - - if (!(obj_entry = pa_hashmap_get(objects, path))) { - obj_entry = pa_xnew(struct object_entry, 1); - obj_entry->path = pa_xstrdup(path); - obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - obj_entry->introspection = NULL; - pa_hashmap_put(objects, path, obj_entry); - obj_entry_created = TRUE; - } - - if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL) - goto fail; /* The interface was already registered. */ - - iface_entry = pa_xnew(struct interface_entry, 1); - iface_entry->name = pa_xstrdup(interface); - iface_entry->properties = copy_strarray(properties); - iface_entry->methods = copy_strarray(methods); - iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet); - iface_entry->receive = receive_cb; - iface_entry->userdata = userdata; - pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); - - update_introspection(obj_entry); - - if (obj_entry_created) - register_object(dbus_state, obj_entry); - - return 0; - -fail: - if (obj_entry_created) { - pa_hashmap_remove(objects, path); - pa_xfree(obj_entry); - } - - if (object_map_created) { - dbus_state->objects = NULL; - pa_hashmap_free(objects, NULL, NULL); - } - - if (state_created) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return -1; -} - -static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) { - DBusConnection *conn; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(obj_entry); - - if (!dbus_state->connections) - return; - - while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) { - if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) - pa_log_debug("dbus_connection_unregister_object_path() failed."); - } -} - -static void free_strarray(char **array) { - char **pos = array; - - while (*pos++) - pa_xfree(*pos); - - pa_xfree(array); -} - -int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) { - struct dbus_state *dbus_state; - pa_hashmap *objects; - struct object_entry *obj_entry; - struct interface_entry *iface_entry; - - pa_assert(c); - pa_assert(path); - pa_assert(interface); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) - return -1; - - if (!(objects = dbus_state->objects)) - return -1; - - if (!(obj_entry = pa_hashmap_get(objects, path))) - return -1; - - if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) - return -1; - - update_introspection(obj_entry); - - pa_xfree(iface_entry->name); - free_strarray(iface_entry->properties); - free_strarray(iface_entry->methods); - pa_xfree(iface_entry->introspection_snippet); - pa_xfree(iface_entry); - - if (pa_hashmap_isempty(obj_entry->interfaces)) { - unregister_object(dbus_state, obj_entry); - - pa_hashmap_remove(objects, path); - pa_xfree(obj_entry->path); - pa_hashmap_free(obj_entry->interfaces, NULL, NULL); - pa_xfree(obj_entry->introspection); - pa_xfree(obj_entry); - } - - if (pa_hashmap_isempty(objects)) { - dbus_state->objects = NULL; - pa_hashmap_free(objects, NULL, NULL); - } - - if (!dbus_state->objects && !dbus_state->connections) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return 0; -} - -static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { - struct object_entry *obj_entry; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(conn); - - if (!dbus_state->objects) - return; - - while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { - if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state)) - pa_log_debug("dbus_connection_register_object_path() failed."); - } -} - -int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) { - struct dbus_state *dbus_state; - pa_idxset *connections; - pa_bool_t state_created = FALSE; - pa_bool_t connection_set_created = FALSE; - - pa_assert(c); - pa_assert(conn); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) { - dbus_state = pa_xnew0(struct dbus_state, 1); - dbus_state->core = c; - pa_hashmap_put(c->shared, "dbus-state", dbus_state); - state_created = TRUE; - } - - if (!(connections = dbus_state->connections)) { - connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - dbus_state->connections = connections; - connection_set_created = TRUE; - } - - if (pa_idxset_get_by_data(connections, conn, NULL)) - goto fail; /* The connection was already registered. */ - - register_all_objects(dbus_state, conn); - - pa_idxset_put(connections, dbus_connection_ref(conn), NULL); - - return 0; - -fail: - if (connection_set_created) { - dbus_state->connections = NULL; - pa_idxset_free(connections, NULL, NULL); - } - - if (state_created) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return -1; -} - -static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) { - struct object_entry *obj_entry; - void *state = NULL; - - pa_assert(dbus_state); - pa_assert(conn); - - if (!dbus_state->objects) - return; - - while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) { - if (!dbus_connection_unregister_object_path(conn, obj_entry->path)) - pa_log_debug("dus_connection_unregister_object_path() failed."); - } -} - -int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) { - struct dbus_state *dbus_state; - pa_idxset *connections; - - pa_assert(c); - pa_assert(conn); - - if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) - return -1; - - if (!(connections = dbus_state->connections)) - return -1; - - if (!pa_idxset_remove_by_data(connections, conn, NULL)) - return -1; - - unregister_all_objects(dbus_state, conn); - - dbus_connection_unref(conn); - - if (pa_idxset_isempty(connections)) { - dbus_state->connections = NULL; - pa_idxset_free(connections, NULL, NULL); - } - - if (!dbus_state->objects && !dbus_state->connections) { - pa_hashmap_remove(c->shared, "dbus-state"); - pa_xfree(dbus_state); - } - - return 0; -} diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h deleted file mode 100644 index 4354c4ea..00000000 --- a/src/pulsecore/dbus-common.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef foodbuscommonhfoo -#define foodbuscommonhfoo - -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio 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 Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include - -#include -#include - -#define PA_DBUS_DEFAULT_PORT 24883 -#define PA_DBUS_SOCKET_NAME "dbus-socket" - -#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME - -#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" - -/* NOTE: These functions may only be called from the main thread. */ - -/* Returns the default address of the server type in the escaped form. For - * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the - * string. This function may fail in some rare cases, in which case NULL is - * returned. */ -char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); - -/* Registers the given interface to the given object path. This is additive: it - * doesn't matter whether or not the object has already been registered; if it - * is, then its interface set is just extended. - * - * Introspection requests are handled automatically. For that to work, the - * caller gives an XML snippet containing the interface introspection element. - * All interface snippets are automatically combined to provide the final - * introspection string for the object. - * - * The introspection snippet contains the interface name, the property names - * and the method namess, but since this function doesn't do XML parsing, the - * information needs to be given separately. Property and method names are - * given as a NULL-terminated array of strings. The interface name is used for - * message routing, and so are the property and method names too in case the - * client doesn't tell which interface he's trying to access; in absence of - * interface information from the client, the correct interface is searched - * based on the property or method name. - * - * Fails and returns a negative number if the object already has the interface - * registered. */ -int pa_dbus_add_interface(pa_core *c, - const char* path, - const char* interface, - const char * const *properties, - const char * const *methods, - const char* introspection_snippet, - DBusObjectPathMessageFunction receive_cb, - void *userdata); - -/* Returns a negative number if the given object doesn't have the given - * interface registered. */ -int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface); - -/* Fails and returns a negative number if the connection is already - * registered. */ -int pa_dbus_register_connection(pa_core *c, DBusConnection *conn); - -/* Returns a negative number if the connection wasn't registered. */ -int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn); - -#endif diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c deleted file mode 100644 index 18bbf789..00000000 --- a/src/pulsecore/dbus-objs/core.c +++ /dev/null @@ -1,482 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio 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 Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include -#include - -#include "core.h" - -#define OBJECT_PATH "/org/pulseaudio1" -#define INTERFACE_CORE "org.PulseAudio.Core1" - -struct pa_dbusobj_core { - pa_core *core; -}; - -static const char *introspection_snippet = - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"; - -/* If you need to modify this list, note that handle_get_all() uses hard-coded - * indexes to point to these strings, so make sure the indexes don't go wrong - * there. */ -static const char *properties[] = { - "InterfaceRevision", - "Name", - "Version", - "Username", - "Hostname", - "DefaultChannels", - "DefaultSampleFormat", - "DefaultSampleRate", - "Sinks", - "FallbackSink", - "Sources", - "FallbackSource", - "PlaybackStreams", - "RecordStreams", - "Samples", - "Modules", - "Clients", - "Extensions", - NULL -}; - -static const char *methods[] = { - "GetCardByName", - "GetSinkByName", - "GetSourceByName", - "GetSampleByName", - "UploadSample", - "LoadSampleFromFile", - "AddLazySample", - "AddLazySamplesFromDirectory", - "LoadModule", - "Exit", - NULL -}; - -static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - DBusMessage *reply = NULL; - const char *server_name = PACKAGE_NAME; - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!(reply = dbus_message_new_method_return(msg))) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - dbus_message_iter_init_append(reply, &msg_iter); - if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - const char* interface; - const char* property; - DBusMessage *reply = NULL; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (*interface && !pa_streq(interface, INTERFACE_CORE)) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - - if (pa_streq(property, "Name")) { - r = handle_get_name(conn, msg, c); - goto finish; - } - - if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - const char* interface; - const char* property; - DBusMessage *reply = NULL; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (*interface && !pa_streq(interface, INTERFACE_CORE)) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - - if (pa_streq(property, "Name")) { - if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - - if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) { - DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; - DBusMessage *reply = NULL; - char *interface = NULL; - char const *server_name = PACKAGE_NAME; - DBusMessageIter msg_iter; - DBusMessageIter dict_iter; - DBusMessageIter dict_entry_iter; - DBusMessageIter variant_iter; - - pa_assert(conn); - pa_assert(msg); - pa_assert(c); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { - if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { - r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - goto finish; - } - - if (!(reply = dbus_message_new_method_return(msg))) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - dbus_message_iter_init_append(reply, &msg_iter); - if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */ - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - if (!dbus_connection_send(conn, reply, NULL)) { - r = DBUS_HANDLER_RESULT_NEED_MEMORY; - goto finish; - } - r = DBUS_HANDLER_RESULT_HANDLED; - -finish: - if (reply) - dbus_message_unref(reply); - - return r; -} - -static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { - pa_dbusobj_core *c = user_data; - - pa_assert(connection); - pa_assert(message); - pa_assert(c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get"))) - return handle_get(connection, message, c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set"))) - return handle_set(connection, message, c); - - if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") || - (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll"))) - return handle_get_all(connection, message, c); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) { - pa_dbusobj_core *c; - - pa_assert(core); - - c = pa_xnew(pa_dbusobj_core, 1); - c->core = core; - - pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c); - - - return c; -} - -void pa_dbusobj_core_free(pa_dbusobj_core *c) { - pa_assert(c); - - pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE); - - pa_xfree(c); -} diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h deleted file mode 100644 index 8e59cc3d..00000000 --- a/src/pulsecore/dbus-objs/core.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef foodbusobjscorehfoo -#define foodbusobjscorehfoo - -/*** - This file is part of PulseAudio. - - Copyright 2009 Tanu Kaskinen - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2.1 of the License, - or (at your option) any later version. - - PulseAudio 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 Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* This object implements the D-Bus object at path /org/pulseaudio/core. - * The implemented interface is org.pulseaudio.Core. - * - * See http://pulseaudio.org/wiki/DBusInterface for the Core interface - * documentation. - */ - -#include - -typedef struct pa_dbusobj_core pa_dbusobj_core; - -pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core); -void pa_dbusobj_core_free(pa_dbusobj_core *c); - -#endif diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index e047dc31..cfc3e8cb 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -444,3 +445,335 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) { pa_dbus_pending_free(i); } } + +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(name); + pa_assert(message); + + pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message))); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + + dbus_message_unref(reply); +} + +void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) { + DBusMessage *reply = NULL; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +static const char *signature_from_basic_type(int type) { + switch (type) { + case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING; + case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING; + case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING; + case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING; + case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING; + case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING; + case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING; + case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING; + case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING; + case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING; + case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING; + case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING; + default: pa_assert_not_reached(); + } +} + +void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter)); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +/* Note: returns sizeof(char*) for strings, object paths and signatures. */ +static unsigned basic_type_size(int type) { + switch (type) { + case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t); + case DBUS_TYPE_BYTE: return 1; + case DBUS_TYPE_INT16: return sizeof(dbus_int16_t); + case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t); + case DBUS_TYPE_INT32: return sizeof(dbus_int32_t); + case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t); + case DBUS_TYPE_INT64: return sizeof(dbus_int64_t); + case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t); + case DBUS_TYPE_DOUBLE: return sizeof(double); + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: return sizeof(char*); + default: pa_assert_not_reached(); + } +} + +void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + +void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { + DBusMessageIter array_iter; + unsigned i; + unsigned item_size; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + item_size = basic_type_size(item_type); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter)); + + for (i = 0; i < n; ++i) + pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size])); + + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); +}; + +void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { + DBusMessageIter variant_iter; + char *array_signature; + + pa_assert(iter); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type)); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter)); + pa_dbus_append_basic_array(&variant_iter, item_type, array, n); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); + + pa_xfree(array_signature); +} + +void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_basic_variant(&dict_entry_iter, type, data); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + +void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(dbus_type_is_basic(item_type)); + pa_assert(array || n == 0); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + +int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(msg); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return -1; + } + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return -1; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type."); + return -1; + } + + dbus_message_iter_get_basic(&variant_iter, data); + + return 0; +} + +int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + pa_assert(dbus_type_is_basic(type)); + pa_assert(data); + + if (dbus_message_iter_get_arg_type(iter) != type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments."); + return -1; + } + + dbus_message_iter_get_basic(iter, data); + + return 0; +} + +int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { + DBusMessageIter array_iter; + int signed_n; + + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + pa_assert(dbus_type_is_fixed(item_type)); + pa_assert(array); + pa_assert(n); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return -1; + } + + if (dbus_message_iter_get_element_type(iter) != item_type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type."); + return -1; + } + + dbus_message_iter_recurse(iter, &array_iter); + + dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); + + pa_assert(signed_n >= 0); + + *n = signed_n; + + return 0; +} + +pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { + DBusMessageIter dict_iter; + pa_proplist *proplist = NULL; + const char *key; + const uint8_t *value; + int value_length; + + pa_assert(c); + pa_assert(msg); + pa_assert(iter); + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return NULL; + } + + if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + return NULL; + } + + proplist = pa_proplist_new(); + + dbus_message_iter_recurse(iter, &dict_iter); + + while (dbus_message_iter_has_next(&dict_iter)) { + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + goto fail; + } + + dbus_message_iter_get_basic(&dict_iter, &key); + + if (strlen(key) <= 0 || !pa_ascii_valid(key)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); + goto fail; + } + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected."); + goto fail; + } + + if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected."); + goto fail; + } + + dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length); + + pa_assert(value_length >= 0); + + pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); + } + + return proplist; + +fail: + if (proplist) + pa_proplist_free(proplist); + + return NULL; +} diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 97328735..1a8aeac9 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -26,6 +26,7 @@ #include #include +#include /* A wrap connection is not shared or refcounted, it is available in client side */ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; @@ -61,4 +62,32 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); /* Free up a list of pa_dbus_pending_call objects */ void pa_dbus_free_pending_list(pa_dbus_pending **p); +/* Sends an error message as the reply to the given message. */ +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message); + +void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); +void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); +void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); +void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); + +void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); +void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); +void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); + +/* Helper function for extracting the value argument of a Set call for a + * property with a basic type. If the message is invalid, an error reply is + * sent and a negative number is returned. */ +int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); + +/* If the arguments can't be read from the iterator, an error reply is sent and + * a negative number is returned. */ +int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); +int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); + +/* Returns a new proplist, that the caller has to free. If the proplist can't + * be read from the iterator, an error reply is sent and NULL is returned. */ +pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); + #endif diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 9df2f583..046c87b2 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -149,21 +149,48 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); + if (type == PA_NAMEREG_SINK && !c->default_sink) + pa_namereg_set_default_sink(c, data); + else if (type == PA_NAMEREG_SOURCE && !c->default_source) + pa_namereg_set_default_source(c, data); + return e->name; } void pa_namereg_unregister(pa_core *c, const char *name) { struct namereg_entry *e; + uint32_t idx; pa_assert(c); pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - if (c->default_sink == e->data) - pa_namereg_set_default_sink(c, NULL); - else if (c->default_source == e->data) - pa_namereg_set_default_source(c, NULL); + if (c->default_sink == e->data) { + pa_sink *new_default = pa_idxset_first(c->sinks, &idx); + + if (new_default == e->data) + new_default = pa_idxset_next(c->sinks, &idx); + + pa_namereg_set_default_sink(c, new_default); + + } else if (c->default_source == e->data) { + pa_source *new_default; + + for (new_default = pa_idxset_first(c->sources, &idx); new_default; new_default = pa_idxset_next(c->sources, &idx)) { + if (new_default != e->data && !new_default->monitor_of) + break; + } + + if (!new_default) { + new_default = pa_idxset_first(c->sources, &idx); + + if (new_default == e->data) + new_default = pa_idxset_next(c->sources, &idx); + } + + pa_namereg_set_default_source(c, new_default); + } pa_xfree(e->name); pa_xfree(e); @@ -191,7 +218,6 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) return s->monitor_source; - } if (!name) @@ -242,35 +268,16 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { return s; } +/* XXX: After removing old functionality, has this function become useless? */ pa_sink *pa_namereg_get_default_sink(pa_core *c) { - pa_sink *s; - pa_assert(c); - if (c->default_sink) - return c->default_sink; - - if ((s = pa_idxset_first(c->sinks, NULL))) - return pa_namereg_set_default_sink(c, s); - - return NULL; + return c->default_sink; } +/* XXX: After removing old functionality, has this function become useless? */ pa_source *pa_namereg_get_default_source(pa_core *c) { - pa_source *s; - uint32_t idx; - pa_assert(c); - if (c->default_source) - return c->default_source; - - for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx))) - if (!s->monitor_of) - return pa_namereg_set_default_source(c, s); - - if ((s = pa_idxset_first(c->sources, NULL))) - return pa_namereg_set_default_source(c, s); - - return NULL; + return c->default_source; } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c new file mode 100644 index 00000000..fb7d168d --- /dev/null +++ b/src/pulsecore/protocol-dbus.c @@ -0,0 +1,930 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include +#include +#include +#include + +#include "protocol-dbus.h" + +struct pa_dbus_protocol { + PA_REFCNT_DECLARE; + + pa_core *core; + pa_hashmap *objects; /* Object path -> struct object_entry */ + pa_hashmap *connections; /* DBusConnection -> struct connection_entry */ + pa_hashmap *extensions; /* String -> anything */ +}; + +struct object_entry { + char *path; + pa_hashmap *interfaces; /* Interface name -> struct interface_entry */ + char *introspection; +}; + +struct connection_entry { + DBusConnection *connection; + pa_client *client; + + pa_bool_t listening_for_all_signals; + + /* Contains object paths. If this is empty, then signals from all objects + * are accepted. Only used when listening_for_all_signals == TRUE. */ + pa_idxset *all_signals_objects; + + /* Signal name -> idxset. The idxsets contain object paths. If an idxset is + * empty, then that signal is accepted from all objects. Only used when + * listening_for_all_signals == FALSE. */ + pa_hashmap *listening_signals; +}; + +struct interface_entry { + char *name; + pa_hashmap *method_handlers; + pa_hashmap *property_handlers; + pa_dbus_receive_cb_t get_all_properties_cb; + pa_dbus_signal_info *signals; + unsigned n_signals; + void *userdata; +}; + +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { + char *address = NULL; + char *runtime_path = NULL; + char *escaped_path = NULL; + + switch (server_type) { + case PA_SERVER_TYPE_USER: + pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))); + pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path))); + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_SYSTEM: + pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))); + address = pa_sprintf_malloc("unix:path=%s", escaped_path); + break; + + case PA_SERVER_TYPE_NONE: + address = pa_xnew0(char, 1); + break; + + default: + pa_assert_not_reached(); + } + + pa_xfree(runtime_path); + pa_xfree(escaped_path); + + return address; +} + +static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { + pa_dbus_protocol *p; + + pa_assert(c); + + p = pa_xnew(pa_dbus_protocol, 1); + PA_REFCNT_INIT(p); + p->core = c; + p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0); + + return p; +} + +pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) { + pa_dbus_protocol *p; + + if ((p = pa_shared_get(c, "dbus-protocol"))) + return pa_dbus_protocol_ref(p); + + return dbus_protocol_new(c); +} + +pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) { + pa_assert(p); + pa_assert(PA_REFCNT_VALUE(p) >= 1); + + PA_REFCNT_INC(p); + + return p; +} + +void pa_dbus_protocol_unref(pa_dbus_protocol *p) { + pa_assert(p); + pa_assert(PA_REFCNT_VALUE(p) >= 1); + + if (PA_REFCNT_DEC(p) > 0) + return; + + pa_assert(pa_hashmap_isempty(p->objects)); + pa_assert(pa_hashmap_isempty(p->connections)); + pa_assert(pa_hashmap_isempty(p->extensions)); + + pa_hashmap_free(p->objects, NULL, NULL); + pa_hashmap_free(p->connections, NULL, NULL); + pa_hashmap_free(p->extensions, NULL, NULL); + + pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); + + pa_xfree(p); +} + +static void update_introspection(struct object_entry *oe) { + pa_strbuf *buf; + void *interfaces_state = NULL; + struct interface_entry *iface_entry = NULL; + + pa_assert(oe); + + buf = pa_strbuf_new(); + pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); + pa_strbuf_puts(buf, "\n"); + + while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) { + pa_dbus_method_handler *method_handler; + pa_dbus_property_handler *property_handler; + void *handlers_state = NULL; + unsigned i; + unsigned j; + + pa_strbuf_printf(buf, " \n", iface_entry->name); + + while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) { + pa_strbuf_printf(buf, " \n", method_handler->method_name); + + for (i = 0; i < method_handler->n_arguments; ++i) + pa_strbuf_printf(buf, " \n", method_handler->arguments[i].name, + method_handler->arguments[i].type, + method_handler->arguments[i].direction); + + pa_strbuf_puts(buf, " \n"); + } + + handlers_state = NULL; + + while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL))) + pa_strbuf_printf(buf, " \n", property_handler->property_name, + property_handler->type, + property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); + + for (i = 0; i < iface_entry->n_signals; ++i) { + pa_strbuf_printf(buf, " \n", iface_entry->signals[i].name); + + for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) + pa_strbuf_printf(buf, " \n", iface_entry->signals[i].arguments[j].name, + iface_entry->signals[i].arguments[j].type); + + pa_strbuf_puts(buf, " \n"); + } + + pa_strbuf_puts(buf, " \n"); + } + + pa_strbuf_puts(buf, " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + + pa_strbuf_puts(buf, "\n"); + + pa_xfree(oe->introspection); + oe->introspection = pa_strbuf_tostring_free(buf); +} + +enum find_result_t { + FOUND_METHOD, + FOUND_GET_PROPERTY, + FOUND_SET_PROPERTY, + FOUND_GET_ALL, + PROPERTY_ACCESS_DENIED, + NO_SUCH_METHOD, + NO_SUCH_PROPERTY, + INVALID_MESSAGE_ARGUMENTS +}; + +static enum find_result_t find_handler_by_property(struct object_entry *obj_entry, + DBusMessage *msg, + const char *property, + struct interface_entry **iface_entry, + pa_dbus_property_handler **property_handler) { + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(property); + pa_assert(iface_entry); + pa_assert(property_handler); + + while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { + if (dbus_message_has_member(msg, "Get")) + return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + else if (dbus_message_has_member(msg, "Set")) + return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + pa_assert_not_reached(); + } + } + + return NO_SUCH_PROPERTY; +} + +static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, + const char *method, + struct interface_entry **iface_entry, + pa_dbus_method_handler **method_handler) { + void *state = NULL; + + pa_assert(obj_entry); + pa_assert(method); + pa_assert(iface_entry); + pa_assert(method_handler); + + while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) + return FOUND_METHOD; + } + + return NO_SUCH_METHOD; +} + +static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry, + DBusMessage *msg, + struct interface_entry **iface_entry, + pa_dbus_property_handler **property_handler, + const char **attempted_property) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(iface_entry); + + if (dbus_message_has_member(msg, "GetAll")) { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) + return FOUND_GET_ALL; + else + return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ + } else { + pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); + return FOUND_GET_ALL; + } + } else { + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID)) + return INVALID_MESSAGE_ARGUMENTS; + + if (*interface) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && + (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) { + if (dbus_message_has_member(msg, "Get")) + return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + else if (dbus_message_has_member(msg, "Set")) + return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + pa_assert_not_reached(); + } else + return NO_SUCH_PROPERTY; + } else + return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler); + } +} + +static enum find_result_t find_handler(struct object_entry *obj_entry, + DBusMessage *msg, + struct interface_entry **iface_entry, + pa_dbus_method_handler **method_handler, + pa_dbus_property_handler **property_handler, + const char **attempted_property) { + const char *interface; + + pa_assert(obj_entry); + pa_assert(msg); + pa_assert(iface_entry); + pa_assert(method_handler); + pa_assert(property_handler); + pa_assert(attempted_property); + + *iface_entry = NULL; + *method_handler = NULL; + + if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) + return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + + else if ((interface = dbus_message_get_interface(msg))) { + if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && + (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) + return FOUND_METHOD; + else + return NO_SUCH_METHOD; + + } else { /* The method call doesn't contain an interface. */ + if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { + if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD) + return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + else + /* Assume this is a .Properties call. */ + return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + + } else /* This is not a .Properties call. */ + return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler); + } +} + +static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { + pa_dbus_protocol *p = user_data; + struct object_entry *obj_entry = NULL; + struct interface_entry *iface_entry = NULL; + pa_dbus_method_handler *method_handler = NULL; + pa_dbus_property_handler *property_handler = NULL; + const char *attempted_property = NULL; + DBusMessage *reply = NULL; + + pa_assert(connection); + pa_assert(message); + pa_assert(p); + pa_assert(p->objects); + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); + + if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || + (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { + pa_assert_se((reply = dbus_message_new_method_return(message))); + pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + + pa_log_debug("%s.Introspect handled.", obj_entry->path); + + goto finish; + } + + switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) { + case FOUND_METHOD: + method_handler->receive_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_GET_PROPERTY: + property_handler->get_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_SET_PROPERTY: + property_handler->set_cb(connection, message, iface_entry->userdata); + break; + + case FOUND_GET_ALL: + if (iface_entry->get_all_properties_cb) + iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); + break; + + case PROPERTY_ACCESS_DENIED: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case NO_SUCH_METHOD: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case NO_SUCH_PROPERTY: + pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + case INVALID_MESSAGE_ARGUMENTS: + pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)))); + pa_assert_se(dbus_connection_send(connection, reply, NULL)); + break; + + default: + pa_assert_not_reached(); + } + +finish: + if (reply) + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusObjectPathVTable vtable = { + .unregister_function = NULL, + .message_function = handle_message_cb, + .dbus_internal_pad1 = NULL, + .dbus_internal_pad2 = NULL, + .dbus_internal_pad3 = NULL, + .dbus_internal_pad4 = NULL +}; + +static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) { + struct connection_entry *conn_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(obj_entry); + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p)); +} + +static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) { + pa_dbus_arg_info *dst; + unsigned i; + + if (n == 0) + return NULL; + + pa_assert(src); + + dst = pa_xnew0(pa_dbus_arg_info, n); + + for (i = 0; i < n; ++i) { + dst[i].name = pa_xstrdup(src[i].name); + dst[i].type = pa_xstrdup(src[i].type); + dst[i].direction = pa_xstrdup(src[i].direction); + } + + return dst; +} + +static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) { + pa_hashmap *handlers; + unsigned i; + + pa_assert(info); + pa_assert(info->method_handlers || info->n_method_handlers == 0); + + handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < info->n_method_handlers; ++i) { + pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1); + h->method_name = pa_xstrdup(info->method_handlers[i].method_name); + h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments); + h->n_arguments = info->method_handlers[i].n_arguments; + h->receive_cb = info->method_handlers[i].receive_cb; + + pa_hashmap_put(handlers, h->method_name, h); + } + + return handlers; +} + +static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) { + pa_hashmap *handlers; + unsigned i = 0; + + pa_assert(info); + pa_assert(info->property_handlers || info->n_property_handlers == 0); + + handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < info->n_property_handlers; ++i) { + pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1); + h->property_name = pa_xstrdup(info->property_handlers[i].property_name); + h->type = pa_xstrdup(info->property_handlers[i].type); + h->get_cb = info->property_handlers[i].get_cb; + h->set_cb = info->property_handlers[i].set_cb; + + pa_hashmap_put(handlers, h->property_name, h); + } + + return handlers; +} + +static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) { + pa_dbus_signal_info *dst; + unsigned i; + + pa_assert(info); + + if (info->n_signals == 0) + return NULL; + + pa_assert(info->signals); + + dst = pa_xnew(pa_dbus_signal_info, info->n_signals); + + for (i = 0; i < info->n_signals; ++i) { + dst[i].name = pa_xstrdup(info->signals[i].name); + dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments); + dst[i].n_arguments = info->signals[i].n_arguments; + } + + return dst; +} + +int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, + const char *path, + const pa_dbus_interface_info *info, + void *userdata) { + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + pa_bool_t obj_entry_created = FALSE; + + pa_assert(p); + pa_assert(path); + pa_assert(info); + pa_assert(info->name); + pa_assert(info->method_handlers || info->n_method_handlers == 0); + pa_assert(info->property_handlers || info->n_property_handlers == 0); + pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0); + pa_assert(info->signals || info->n_signals == 0); + + if (!(obj_entry = pa_hashmap_get(p->objects, path))) { + obj_entry = pa_xnew(struct object_entry, 1); + obj_entry->path = pa_xstrdup(path); + obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + obj_entry->introspection = NULL; + + pa_hashmap_put(p->objects, path, obj_entry); + obj_entry_created = TRUE; + } + + if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL) + goto fail; /* The interface was already registered. */ + + iface_entry = pa_xnew(struct interface_entry, 1); + iface_entry->name = pa_xstrdup(info->name); + iface_entry->method_handlers = create_method_handlers(info); + iface_entry->property_handlers = create_property_handlers(info); + iface_entry->get_all_properties_cb = info->get_all_properties_cb; + iface_entry->signals = copy_signals(info); + iface_entry->n_signals = info->n_signals; + iface_entry->userdata = userdata; + pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry); + + update_introspection(obj_entry); + + if (obj_entry_created) + register_object(p, obj_entry); + + return 0; + +fail: + if (obj_entry_created) { + pa_hashmap_remove(p->objects, path); + pa_dbus_protocol_unref(p); + pa_xfree(obj_entry); + } + + return -1; +} + +static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) { + struct connection_entry *conn_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(obj_entry); + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path)); +} + +static void method_handler_free_cb(void *p, void *userdata) { + pa_dbus_method_handler *h = p; + unsigned i; + + pa_assert(h); + + pa_xfree((char *) h->method_name); + + for (i = 0; i < h->n_arguments; ++i) { + pa_xfree((char *) h->arguments[i].name); + pa_xfree((char *) h->arguments[i].type); + pa_xfree((char *) h->arguments[i].direction); + } + + pa_xfree((pa_dbus_arg_info *) h->arguments); +} + +static void property_handler_free_cb(void *p, void *userdata) { + pa_dbus_property_handler *h = p; + + pa_assert(h); + + pa_xfree((char *) h->property_name); + pa_xfree((char *) h->type); +} + +int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) { + struct object_entry *obj_entry; + struct interface_entry *iface_entry; + unsigned i; + + pa_assert(p); + pa_assert(path); + pa_assert(interface); + + if (!(obj_entry = pa_hashmap_get(p->objects, path))) + return -1; + + if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface))) + return -1; + + update_introspection(obj_entry); + + pa_xfree(iface_entry->name); + pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); + pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); + + for (i = 0; i < iface_entry->n_signals; ++i) { + unsigned j; + + pa_xfree((char *) iface_entry->signals[i].name); + + for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) { + pa_xfree((char *) iface_entry->signals[i].arguments[j].name); + pa_xfree((char *) iface_entry->signals[i].arguments[j].type); + pa_assert(iface_entry->signals[i].arguments[j].direction == NULL); + } + + pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments); + } + + pa_xfree(iface_entry->signals); + pa_xfree(iface_entry); + + if (pa_hashmap_isempty(obj_entry->interfaces)) { + unregister_object(p, obj_entry); + + pa_hashmap_remove(p->objects, path); + pa_xfree(obj_entry->path); + pa_hashmap_free(obj_entry->interfaces, NULL, NULL); + pa_xfree(obj_entry->introspection); + pa_xfree(obj_entry); + } + + return 0; +} + +static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(conn); + + while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p)); +} + +int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + pa_assert(client); + + if (pa_hashmap_get(p->connections, conn)) + return -1; /* The connection was already registered. */ + + register_all_objects(p, conn); + + conn_entry = pa_xnew(struct connection_entry, 1); + conn_entry->connection = dbus_connection_ref(conn); + conn_entry->client = client; + conn_entry->listening_for_all_signals = FALSE; + conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + conn_entry->listening_signals = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_hashmap_put(p->connections, conn, conn_entry); + + return 0; +} + +static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { + struct object_entry *obj_entry; + void *state = NULL; + + pa_assert(p); + pa_assert(conn); + + while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path)); +} + +static void free_listened_object_name_cb(void *p, void *userdata) { + pa_assert(p); + + pa_xfree(p); +} + +static void free_listening_signals_idxset_cb(void *p, void *userdata) { + pa_idxset *set = p; + + pa_assert(set); + + pa_idxset_free(set, free_listened_object_name_cb, NULL); +} + +int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_remove(p->connections, conn))) + return -1; + + unregister_all_objects(p, conn); + + dbus_connection_unref(conn_entry->connection); + pa_idxset_free(conn_entry->all_signals_objects, free_listened_object_name_cb, NULL); + pa_hashmap_free(conn_entry->listening_signals, free_listening_signals_idxset_cb, NULL); + pa_xfree(conn_entry); + + return 0; +} + +void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { + struct connection_entry *conn_entry; + pa_idxset *object_set; + char *object_path; + unsigned i; + + pa_assert(p); + pa_assert(conn); + pa_assert(objects || n_objects == 0); + + pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn))); + + /* all_signals_objects will either be emptied or replaced with new objects, + * so we empty it here unconditionally. If listening_for_all_signals is + * currently FALSE, the idxset is empty already. */ + while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL))) + pa_xfree(object_path); + + if (signal) { + conn_entry->listening_for_all_signals = FALSE; + + /* Replace the old object list with a new one. */ + if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < n_objects; ++i) + pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL); + + } else { + conn_entry->listening_for_all_signals = TRUE; + + /* We're not interested in individual signals anymore, so let's empty + * listening_signals. */ + while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + + for (i = 0; i < n_objects; ++i) + pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL); + } +} + +void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal) { + struct connection_entry *conn_entry; + pa_idxset *object_set; + + pa_assert(p); + pa_assert(conn); + + pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn))); + + if (signal) { + if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + + } else { + char *object_path; + + conn_entry->listening_for_all_signals = FALSE; + + while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL))) + pa_xfree(object_path); + + while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals))) + pa_idxset_free(object_set, free_listened_object_name_cb, NULL); + } +} + +void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { + struct connection_entry *conn_entry; + void *state = NULL; + pa_idxset *object_set; + DBusMessage *signal_copy; + + pa_assert(p); + pa_assert(signal); + pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); + + /* XXX: We have to do some linear searching to find connections that want + * to receive the signal. This shouldn't be very significant performance + * problem, and adding an (object path, signal name) -> connection mapping + * would be likely to create substantial complexity. */ + + while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) { + + if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ + && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL) + || pa_idxset_isempty(conn_entry->all_signals_objects))) + + || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */ + && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal)) + && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL) + || pa_idxset_isempty(object_set)))) { + + pa_assert_se(signal_copy = dbus_message_copy(signal)); + pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL)); + dbus_message_unref(signal_copy); + } + } +} + +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_get(p->connections, conn))) + return NULL; + + return conn_entry->client; +} + +const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { + const char **extensions; + const char *ext_name; + void *state = NULL; + unsigned i = 0; + + pa_assert(p); + pa_assert(n); + + *n = pa_hashmap_size(p->extensions); + + if (*n <= 0) + return NULL; + + extensions = pa_xnew(const char *, *n); + + while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) { + extensions[i] = ext_name; + ++i; + } + + return extensions; +} diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h new file mode 100644 index 00000000..27198f48 --- /dev/null +++ b/src/pulsecore/protocol-dbus.h @@ -0,0 +1,158 @@ +#ifndef fooprotocoldbushfoo +#define fooprotocoldbushfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +#define PA_DBUS_DEFAULT_PORT 24883 +#define PA_DBUS_SOCKET_NAME "dbus-socket" + +#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME + +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" +#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError" + +/* Returns the default address of the server type in the escaped form. For + * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the + * string. */ +char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type); + +typedef struct pa_dbus_protocol pa_dbus_protocol; + +/* This function either creates a new pa_dbus_protocol object, or if one + * already exists, increases the reference count. */ +pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c); + +pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p); +void pa_dbus_protocol_unref(pa_dbus_protocol *p); + +/* Called when a received message needs handling. Completely ignoring the + * message isn't a good idea; if you can't handle the message, reply with an + * error. + * + * All messages are method calls. */ +typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata); + +typedef struct pa_dbus_arg_info { + const char *name; + const char *type; + const char *direction; /* NULL for signal arguments. */ +} pa_dbus_arg_info; + +typedef struct pa_dbus_signal_info { + const char *name; + const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */ + unsigned n_arguments; +} pa_dbus_signal_info; + +typedef struct pa_dbus_method_handler { + const char *method_name; + const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */ + unsigned n_arguments; + pa_dbus_receive_cb_t receive_cb; +} pa_dbus_method_handler; + +typedef struct pa_dbus_property_handler { + const char *property_name; + const char *type; + + /* The access mode for the property is determined by checking whether + * get_cb or set_cb is NULL. */ + pa_dbus_receive_cb_t get_cb; + pa_dbus_receive_cb_t set_cb; +} pa_dbus_property_handler; + +typedef struct pa_dbus_interface_info { + const char* name; + const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */ + unsigned n_method_handlers; + const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */ + unsigned n_property_handlers; + const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */ + const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */ + unsigned n_signals; +} pa_dbus_interface_info; + + +/* The following functions may only be called from the main thread. */ + +/* Registers the given interface to the given object path. It doesn't matter + * whether or not the object has already been registered; if it is, then its + * interface set is extended. + * + * Introspection requests are handled automatically. + * + * Userdata is passed to all the callbacks. + * + * Fails and returns a negative number if the object already has the interface + * registered. */ +int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata); + +/* Returns a negative number if the given object doesn't have the given + * interface registered. */ +int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface); + +/* Fails and returns a negative number if the connection is already + * registered. */ +int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client); + +/* Returns a negative number if the connection wasn't registered. */ +int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn); + +/* Enables signal receiving for the given connection. The connection must have + * been registered earlier. + * + * If the signal argument is NULL, all signals will be sent to the connection, + * otherwise calling this function only adds the given signal to the list of + * signals that will be delivered to the connection. + * + * The objects argument is a list of object paths. If the list is not empty, + * only signals from the given objects are delivered. If this function is + * called multiple time for the same connection and signal, the latest call + * always replaces the previous object list. */ +void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects); + +/* Disables the delivery of the signal for the given connection. The connection + * must have been registered. If signal is NULL, all signals are disabled. If + * signal is non-NULL and _add_signal_listener() was previously called with + * NULL signal (causing all signals to be enabled), this function doesn't do + * anything. Also, if the signal wasn't enabled before, this function doesn't + * do anything in that case either. */ +void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal); + +void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); + +/* Returns NULL if the connection isn't registered. */ +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); + +/* Returns an array of extension identifier strings. The strings pointers point + * to the internal copies, so don't free the strings. The caller must free the + * array, however. Also, do not save the returned pointer or any of the string + * pointers, because the contained strings may be freed at any time. If you + * need to save the array, copy it. */ +const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n); + +#endif -- cgit From 9a77d2f81d693d64b63d38be7214626dedf91e11 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 21 Jul 2009 00:04:52 +0300 Subject: Add the forgotten src/modules/dbus directory to git. --- src/modules/dbus/iface-card.c | 61 + src/modules/dbus/iface-card.h | 40 + src/modules/dbus/iface-client.c | 61 + src/modules/dbus/iface-client.h | 40 + src/modules/dbus/iface-core.c | 1968 +++++++++++++++++++++++++++++++ src/modules/dbus/iface-core.h | 38 + src/modules/dbus/iface-device.c | 105 ++ src/modules/dbus/iface-device.h | 46 + src/modules/dbus/iface-module.c | 61 + src/modules/dbus/iface-module.h | 40 + src/modules/dbus/iface-sample.c | 61 + src/modules/dbus/iface-sample.h | 40 + src/modules/dbus/iface-stream.c | 91 ++ src/modules/dbus/iface-stream.h | 42 + src/modules/dbus/module-dbus-protocol.c | 580 +++++++++ 15 files changed, 3274 insertions(+) create mode 100644 src/modules/dbus/iface-card.c create mode 100644 src/modules/dbus/iface-card.h create mode 100644 src/modules/dbus/iface-client.c create mode 100644 src/modules/dbus/iface-client.h create mode 100644 src/modules/dbus/iface-core.c create mode 100644 src/modules/dbus/iface-core.h create mode 100644 src/modules/dbus/iface-device.c create mode 100644 src/modules/dbus/iface-device.h create mode 100644 src/modules/dbus/iface-module.c create mode 100644 src/modules/dbus/iface-module.h create mode 100644 src/modules/dbus/iface-sample.c create mode 100644 src/modules/dbus/iface-sample.h create mode 100644 src/modules/dbus/iface-stream.c create mode 100644 src/modules/dbus/iface-stream.h create mode 100644 src/modules/dbus/module-dbus-protocol.c (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c new file mode 100644 index 00000000..db6aa26f --- /dev/null +++ b/src/modules/dbus/iface-card.c @@ -0,0 +1,61 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-card.h" + +#define OBJECT_NAME "card" + +struct pa_dbusiface_card { + pa_card *card; + char *path; +}; + +pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) { + pa_dbusiface_card *c; + + pa_assert(card); + pa_assert(path_prefix); + + c = pa_xnew(pa_dbusiface_card, 1); + c->card = card; + c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index); + + return c; +} + +void pa_dbusiface_card_free(pa_dbusiface_card *c) { + pa_assert(c); + + pa_xfree(c->path); + pa_xfree(c); +} + +const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) { + pa_assert(c); + + return c->path; +} diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h new file mode 100644 index 00000000..54db610c --- /dev/null +++ b/src/modules/dbus/iface-card.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacecardhfoo +#define foodbusifacecardhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Card. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_card pa_dbusiface_card; + +pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix); +void pa_dbusiface_card_free(pa_dbusiface_card *c); + +const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c); + +#endif diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c new file mode 100644 index 00000000..cfa36d0c --- /dev/null +++ b/src/modules/dbus/iface-client.c @@ -0,0 +1,61 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-client.h" + +#define OBJECT_NAME "client" + +struct pa_dbusiface_client { + pa_client *client; + char *path; +}; + +pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) { + pa_dbusiface_client *c; + + pa_assert(client); + pa_assert(path_prefix); + + c = pa_xnew(pa_dbusiface_client, 1); + c->client = client; + c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index); + + return c; +} + +void pa_dbusiface_client_free(pa_dbusiface_client *c) { + pa_assert(c); + + pa_xfree(c->path); + pa_xfree(c); +} + +const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) { + pa_assert(c); + + return c->path; +} diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h new file mode 100644 index 00000000..62cca7f8 --- /dev/null +++ b/src/modules/dbus/iface-client.h @@ -0,0 +1,40 @@ +#ifndef foodbusifaceclienthfoo +#define foodbusifaceclienthfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Card. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_client pa_dbusiface_client; + +pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix); +void pa_dbusiface_client_free(pa_dbusiface_client *c); + +const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c); + +#endif diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c new file mode 100644 index 00000000..31f12603 --- /dev/null +++ b/src/modules/dbus/iface-core.c @@ -0,0 +1,1968 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "iface-card.h" +#include "iface-client.h" +#include "iface-device.h" +#include "iface-module.h" +#include "iface-sample.h" +#include "iface-stream.h" + +#include "iface-core.h" + +#define OBJECT_PATH "/org/pulseaudio/core1" +#define INTERFACE_CORE "org.PulseAudio.Core1" + +#define INTERFACE_REVISION 0 + + + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_core { + pa_core *core; + pa_subscription *subscription; + + pa_dbus_protocol *dbus_protocol; + + pa_hashmap *cards; + pa_hashmap *sinks_by_index; + pa_hashmap *sinks_by_path; + pa_hashmap *sources_by_index; + pa_hashmap *sources_by_path; + pa_hashmap *playback_streams; + pa_hashmap *record_streams; + pa_hashmap *samples; + pa_hashmap *modules; + pa_hashmap *clients; + + pa_sink *fallback_sink; + pa_source *fallback_source; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INTERFACE_REVISION, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_VERSION, + PROPERTY_HANDLER_IS_LOCAL, + PROPERTY_HANDLER_USERNAME, + PROPERTY_HANDLER_HOSTNAME, + PROPERTY_HANDLER_DEFAULT_CHANNELS, + PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT, + PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE, + PROPERTY_HANDLER_CARDS, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_FALLBACK_SINK, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_FALLBACK_SOURCE, + PROPERTY_HANDLER_PLAYBACK_STREAMS, + PROPERTY_HANDLER_RECORD_STREAMS, + PROPERTY_HANDLER_SAMPLES, + PROPERTY_HANDLER_MODULES, + PROPERTY_HANDLER_CLIENTS, + PROPERTY_HANDLER_MY_CLIENT, + PROPERTY_HANDLER_EXTENSIONS, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u", .get_cb = handle_get_interface_revision, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_VERSION] = { .property_name = "Version", .type = "s", .get_cb = handle_get_version, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_LOCAL] = { .property_name = "IsLocal", .type = "b", .get_cb = handle_get_is_local, .set_cb = NULL }, + [PROPERTY_HANDLER_USERNAME] = { .property_name = "Username", .type = "s", .get_cb = handle_get_username, .set_cb = NULL }, + [PROPERTY_HANDLER_HOSTNAME] = { .property_name = "Hostname", .type = "s", .get_cb = handle_get_hostname, .set_cb = NULL }, + [PROPERTY_HANDLER_DEFAULT_CHANNELS] = { .property_name = "DefaultChannels", .type = "au", .get_cb = handle_get_default_channels, .set_cb = handle_set_default_channels }, + [PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT] = { .property_name = "DefaultSampleFormat", .type = "u", .get_cb = handle_get_default_sample_format, .set_cb = handle_set_default_sample_format }, + [PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE] = { .property_name = "DefaultSampleRate", .type = "u", .get_cb = handle_get_default_sample_rate, .set_cb = handle_set_default_sample_rate }, + [PROPERTY_HANDLER_CARDS] = { .property_name = "Cards", .type = "ao", .get_cb = handle_get_cards, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_FALLBACK_SINK] = { .property_name = "FallbackSink", .type = "o", .get_cb = handle_get_fallback_sink, .set_cb = handle_set_fallback_sink }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_FALLBACK_SOURCE] = { .property_name = "FallbackSource", .type = "o", .get_cb = handle_get_fallback_source, .set_cb = handle_set_fallback_source }, + [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLES] = { .property_name = "Samples", .type = "ao", .get_cb = handle_get_samples, .set_cb = NULL }, + [PROPERTY_HANDLER_MODULES] = { .property_name = "Modules", .type = "ao", .get_cb = handle_get_modules, .set_cb = NULL }, + [PROPERTY_HANDLER_CLIENTS] = { .property_name = "Clients", .type = "ao", .get_cb = handle_get_clients, .set_cb = NULL }, + [PROPERTY_HANDLER_MY_CLIENT] = { .property_name = "MyClient", .type = "o", .get_cb = handle_get_my_client, .set_cb = NULL }, + [PROPERTY_HANDLER_EXTENSIONS] = { .property_name = "Extensions", .type = "as", .get_cb = handle_get_extensions, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_GET_CARD_BY_NAME, + METHOD_HANDLER_GET_SINK_BY_NAME, + METHOD_HANDLER_GET_SOURCE_BY_NAME, + METHOD_HANDLER_GET_SAMPLE_BY_NAME, + METHOD_HANDLER_UPLOAD_SAMPLE, + METHOD_HANDLER_LOAD_MODULE, + METHOD_HANDLER_EXIT, + METHOD_HANDLER_LISTEN_FOR_SIGNAL, + METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info get_card_by_name_args[] = { { "name", "s", "in" }, { "card", "o", "out" } }; +static pa_dbus_arg_info get_sink_by_name_args[] = { { "name", "s", "in" }, { "sink", "o", "out" } }; +static pa_dbus_arg_info get_source_by_name_args[] = { { "name", "s", "in" }, { "source", "o", "out" } }; +static pa_dbus_arg_info get_sample_by_name_args[] = { { "name", "s", "in" }, { "sample", "o", "out" } }; +static pa_dbus_arg_info upload_sample_args[] = { { "name", "s", "in" }, + { "sample_format", "u", "in" }, + { "sample_rate", "u", "in" }, + { "channels", "au", "in" }, + { "default_volume", "au", "in" }, + { "property_list", "a{say}", "in" }, + { "data", "ay", "in" }, + { "sample", "o", "out" } }; +static pa_dbus_arg_info load_module_args[] = { { "name", "s", "in" }, { "arguments", "a{ss}", "in" }, { "module", "o", "out" } }; +static pa_dbus_arg_info listen_for_signal_args[] = { { "signal", "s", "in" }, { "objects", "ao", "in" } }; +static pa_dbus_arg_info stop_listening_for_signal_args[] = { { "signal", "s", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_GET_CARD_BY_NAME] = { + .method_name = "GetCardByName", + .arguments = get_card_by_name_args, + .n_arguments = sizeof(get_card_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_card_by_name }, + [METHOD_HANDLER_GET_SINK_BY_NAME] = { + .method_name = "GetSinkByName", + .arguments = get_sink_by_name_args, + .n_arguments = sizeof(get_sink_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_sink_by_name }, + [METHOD_HANDLER_GET_SOURCE_BY_NAME] = { + .method_name = "GetSourceByName", + .arguments = get_source_by_name_args, + .n_arguments = sizeof(get_source_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_source_by_name }, + [METHOD_HANDLER_GET_SAMPLE_BY_NAME] = { + .method_name = "GetSampleByName", + .arguments = get_sample_by_name_args, + .n_arguments = sizeof(get_sample_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_sample_by_name }, + [METHOD_HANDLER_UPLOAD_SAMPLE] = { + .method_name = "UploadSample", + .arguments = upload_sample_args, + .n_arguments = sizeof(upload_sample_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_upload_sample }, + [METHOD_HANDLER_LOAD_MODULE] = { + .method_name = "LoadModule", + .arguments = upload_sample_args, + .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_load_module }, + [METHOD_HANDLER_EXIT] = { + .method_name = "Exit", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_exit }, + [METHOD_HANDLER_LISTEN_FOR_SIGNAL] = { + .method_name = "ListenForSignal", + .arguments = listen_for_signal_args, + .n_arguments = sizeof(listen_for_signal_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_listen_for_signal }, + [METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL] = { + .method_name = "StopListeningForSignal", + .arguments = stop_listening_for_signal_args, + .n_arguments = sizeof(stop_listening_for_signal_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_stop_listening_for_signal } +}; + +enum signal_index { + SIGNAL_NEW_CARD, + SIGNAL_CARD_REMOVED, + SIGNAL_NEW_SINK, + SIGNAL_SINK_REMOVED, + SIGNAL_FALLBACK_SINK_UPDATED, + SIGNAL_NEW_SOURCE, + SIGNAL_SOURCE_REMOVED, + SIGNAL_FALLBACK_SOURCE_UPDATED, + SIGNAL_NEW_PLAYBACK_STREAM, + SIGNAL_PLAYBACK_STREAM_REMOVED, + SIGNAL_NEW_RECORD_STREAM, + SIGNAL_RECORD_STREAM_REMOVED, + SIGNAL_NEW_SAMPLE, + SIGNAL_SAMPLE_REMOVED, + SIGNAL_NEW_MODULE, + SIGNAL_MODULE_REMOVED, + SIGNAL_NEW_CLIENT, + SIGNAL_CLIENT_REMOVED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info new_card_args[] = { { "card", "o", NULL } }; +static pa_dbus_arg_info card_removed_args[] = { { "card", "o", NULL } }; +static pa_dbus_arg_info new_sink_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info sink_removed_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info fallback_sink_updated_args[] = { { "sink", "o", NULL } }; +static pa_dbus_arg_info new_source_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info source_removed_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info fallback_source_updated_args[] = { { "source", "o", NULL } }; +static pa_dbus_arg_info new_playback_stream_args[] = { { "playback_stream", "o", NULL } }; +static pa_dbus_arg_info playback_stream_removed_args[] = { { "playback_stream", "o", NULL } }; +static pa_dbus_arg_info new_record_stream_args[] = { { "record_stream", "o", NULL } }; +static pa_dbus_arg_info record_stream_removed_args[] = { { "record_stream", "o", NULL } }; +static pa_dbus_arg_info new_sample_args[] = { { "sample", "o", NULL } }; +static pa_dbus_arg_info sample_removed_args[] = { { "sample", "o", NULL } }; +static pa_dbus_arg_info new_module_args[] = { { "module", "o", NULL } }; +static pa_dbus_arg_info module_removed_args[] = { { "module", "o", NULL } }; +static pa_dbus_arg_info new_client_args[] = { { "client", "o", NULL } }; +static pa_dbus_arg_info client_removed_args[] = { { "client", "o", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_NEW_CARD] = { .name = "NewCard", .arguments = new_card_args, .n_arguments = 1 }, + [SIGNAL_CARD_REMOVED] = { .name = "CardRemoved", .arguments = card_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_SINK] = { .name = "NewSink", .arguments = new_sink_args, .n_arguments = 1 }, + [SIGNAL_SINK_REMOVED] = { .name = "SinkRemoved", .arguments = sink_removed_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SINK_UPDATED] = { .name = "FallbackSinkUpdated", .arguments = fallback_sink_updated_args, .n_arguments = 1 }, + [SIGNAL_NEW_SOURCE] = { .name = "NewSource", .arguments = new_source_args, .n_arguments = 1 }, + [SIGNAL_SOURCE_REMOVED] = { .name = "SourceRemoved", .arguments = source_removed_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 }, + [SIGNAL_NEW_PLAYBACK_STREAM] = { .name = "NewPlaybackStream", .arguments = new_playback_stream_args, .n_arguments = 1 }, + [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_RECORD_STREAM] = { .name = "NewRecordStream", .arguments = new_record_stream_args, .n_arguments = 1 }, + [SIGNAL_RECORD_STREAM_REMOVED] = { .name = "RecordStreamRemoved", .arguments = record_stream_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_SAMPLE] = { .name = "NewSample", .arguments = new_sample_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_REMOVED] = { .name = "SampleRemoved", .arguments = sample_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_MODULE] = { .name = "NewModule", .arguments = new_module_args, .n_arguments = 1 }, + [SIGNAL_MODULE_REMOVED] = { .name = "ModuleRemoved", .arguments = module_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_CLIENT] = { .name = "NewClient", .arguments = new_client_args, .n_arguments = 1 }, + [SIGNAL_CLIENT_REMOVED] = { .name = "ClientRemoved", .arguments = client_removed_args, .n_arguments = 1 }, +}; + +static pa_dbus_interface_info core_interface_info = { + .name = INTERFACE_CORE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_uint32_t interface_revision = INTERFACE_REVISION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *server_name = PACKAGE_NAME; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &server_name); +} + +static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata) { + const char *version = PACKAGE_VERSION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &version); +} + +static dbus_bool_t get_is_local(DBusConnection *conn) { + int conn_fd; + + pa_assert(conn); + + if (!dbus_connection_get_socket(conn, &conn_fd)) + return FALSE; + + return pa_socket_is_local(conn_fd); +} + +static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_bool_t is_local; + + pa_assert(conn); + pa_assert(msg); + + is_local = get_is_local(conn); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_local); +} + +static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata) { + char *username = NULL; + + pa_assert(conn); + pa_assert(msg); + + username = pa_get_user_name_malloc(); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &username); + + pa_xfree(username); +} + +static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata) { + char *hostname = NULL; + + pa_assert(conn); + pa_assert(msg); + + hostname = pa_get_host_name_malloc(); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &hostname); + + pa_xfree(hostname); +} + +/* Caller frees the returned array. */ +static dbus_uint32_t *get_default_channels(pa_dbusiface_core *c, unsigned *n) { + dbus_uint32_t *default_channels = NULL; + unsigned i; + + pa_assert(c); + pa_assert(n); + + *n = c->core->default_channel_map.channels; + default_channels = pa_xnew(dbus_uint32_t, *n); + + for (i = 0; i < *n; ++i) + default_channels[i] = c->core->default_channel_map.map[i]; + + return default_channels; +} + +static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t *default_channels = NULL; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_channels = get_default_channels(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_channels, n); + + pa_xfree(default_channels); +} + +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_channel_map new_channel_map; + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + DBusMessageIter array_iter; + dbus_uint32_t *default_channels; + int n_channels; + unsigned i; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_channel_map_init(&new_channel_map); + + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array."); + return; + } + + dbus_message_iter_recurse(&variant_iter, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32."); + return; + } + + dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); + + if (n_channels <= 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); + return; + } + + new_channel_map.channels = n_channels; + + for (i = 0; i < new_channel_map.channels; ++i) { + if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + return; + } + + new_channel_map.map[i] = default_channels[i]; + } + + c->core->default_channel_map = new_channel_map; + c->core->default_sample_spec.channels = n_channels; + + pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_format; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_sample_format = c->core->default_sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format); +} + +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_format; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0) + return; + + if (default_sample_format >= PA_SAMPLE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + return; + } + + c->core->default_sample_spec.format = default_sample_format; + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_rate; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + default_sample_rate = c->core->default_sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate); +} + +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + dbus_uint32_t default_sample_rate; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0) + return; + + if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + return; + } + + c->core->default_sample_spec.rate = default_sample_rate; + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { + const char **cards; + unsigned i; + void *state = NULL; + pa_dbusiface_card *card; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->cards); + + if (*n == 0) + return NULL; + + cards = pa_xnew(const char *, *n); + + for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL)) + cards[i] = pa_dbusiface_card_get_path(card); + + pa_assert(i == *n); + + return cards; +} + +static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **cards; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + cards = get_cards(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, cards, n); + + pa_xfree(cards); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { + const char **sinks; + unsigned i; + void *state = NULL; + pa_dbusiface_device *sink; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->sinks_by_index); + + if (*n == 0) + return NULL; + + sinks = pa_xnew(const char *, *n); + + for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL)) + sinks[i] = pa_dbusiface_device_get_path(sink); + + pa_assert(i == *n); + + return sinks; +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **sinks; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sinks = get_sinks(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n); + + pa_xfree(sinks); +} + +static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_sink; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + return; + } + + pa_assert_se((fallback_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))); + object_path = pa_dbusiface_device_get_path(fallback_sink); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_sink; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + return; + } + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) + return; + + if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + return; + } + + pa_namereg_set_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)); + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { + const char **sources; + unsigned i; + void *state = NULL; + pa_dbusiface_device *source; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->sources_by_index); + + if (*n == 0) + return NULL; + + sources = pa_xnew(const char *, *n); + + for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL)) + sources[i] = pa_dbusiface_device_get_path(source); + + pa_assert(i == *n); + + return sources; +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **sources; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sources = get_sources(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n); + + pa_xfree(sources); +} + +static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_source; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + return; + } + + pa_assert_se((fallback_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index)))); + object_path = pa_dbusiface_device_get_path(fallback_source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_device *fallback_source; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->fallback_source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + return; + } + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) + return; + + if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + return; + } + + pa_namereg_set_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)); + + pa_dbus_send_empty_reply(conn, msg); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { + const char **streams; + unsigned i; + void *state = NULL; + pa_dbusiface_stream *stream; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->playback_streams); + + if (*n == 0) + return NULL; + + streams = pa_xnew(const char *, *n); + + for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL)) + streams[i] = pa_dbusiface_stream_get_path(stream); + + pa_assert(i == *n); + + return streams; +} + +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **playback_streams; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + playback_streams = get_playback_streams(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n); + + pa_xfree(playback_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { + const char **streams; + unsigned i; + void *state = NULL; + pa_dbusiface_stream *stream; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->record_streams); + + if (*n == 0) + return NULL; + + streams = pa_xnew(const char *, *n); + + for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL)) + streams[i] = pa_dbusiface_stream_get_path(stream); + + pa_assert(i == *n); + + return streams; +} + +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **record_streams; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + record_streams = get_record_streams(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n); + + pa_xfree(record_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { + const char **samples; + unsigned i; + void *state = NULL; + pa_dbusiface_sample *sample; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->samples); + + if (*n == 0) + return NULL; + + samples = pa_xnew(const char *, *n); + + for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL)) + samples[i] = pa_dbusiface_sample_get_path(sample); + + pa_assert(i == *n); + + return samples; +} + +static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **samples; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + samples = get_samples(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, samples, n); + + pa_xfree(samples); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { + const char **modules; + unsigned i; + void *state = NULL; + pa_dbusiface_module *module; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->modules); + + if (*n == 0) + return NULL; + + modules = pa_xnew(const char *, *n); + + for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL)) + modules[i] = pa_dbusiface_module_get_path(module); + + pa_assert(i == *n); + + return modules; +} + +static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **modules; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + modules = get_modules(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, modules, n); + + pa_xfree(modules); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { + const char **clients; + unsigned i; + void *state = NULL; + pa_dbusiface_client *client; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->clients); + + if (*n == 0) + return NULL; + + clients = pa_xnew(const char *, *n); + + for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL)) + clients[i] = pa_dbusiface_client_get_path(client); + + pa_assert(i == *n); + + return clients; +} + +static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **clients; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + clients = get_clients(c, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, clients, n); + + pa_xfree(clients); +} + +static const char *get_my_client(pa_dbusiface_core *c, DBusConnection *conn) { + pa_client *my_client; + + pa_assert(c); + pa_assert(conn); + + pa_assert_se((my_client = pa_dbus_protocol_get_client(c->dbus_protocol, conn))); + + return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(my_client->index))); +} + +static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *my_client; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + my_client = get_my_client(c, conn); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &my_client); +} + +static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char **extensions; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, extensions, n); + + pa_xfree(extensions); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t interface_revision; + const char *server_name; + const char *version; + dbus_bool_t is_local; + char *username; + char *hostname; + dbus_uint32_t *default_channels; + unsigned n_default_channels; + dbus_uint32_t default_sample_format; + dbus_uint32_t default_sample_rate; + const char **cards; + unsigned n_cards; + const char **sinks; + unsigned n_sinks; + const char *fallback_sink; + const char **sources; + unsigned n_sources; + const char *fallback_source; + const char **playback_streams; + unsigned n_playback_streams; + const char **record_streams; + unsigned n_record_streams; + const char **samples; + unsigned n_samples; + const char **modules; + unsigned n_modules; + const char **clients; + unsigned n_clients; + const char *my_client; + const char **extensions; + unsigned n_extensions; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + interface_revision = INTERFACE_REVISION; + server_name = PACKAGE_NAME; + version = PACKAGE_VERSION; + is_local = get_is_local(conn); + username = pa_get_user_name_malloc(); + hostname = pa_get_host_name_malloc(); + default_channels = get_default_channels(c, &n_default_channels); + default_sample_format = c->core->default_sample_spec.format; + default_sample_rate = c->core->default_sample_spec.rate; + cards = get_cards(c, &n_cards); + sinks = get_sinks(c, &n_sinks); + fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL; + sources = get_sources(c, &n_sources); + fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL; + playback_streams = get_playback_streams(c, &n_playback_streams); + record_streams = get_record_streams(c, &n_record_streams); + samples = get_samples(c, &n_samples); + modules = get_modules(c, &n_modules); + clients = get_clients(c, &n_clients); + my_client = get_my_client(c, conn); + extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n_extensions); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &server_name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VERSION].property_name, DBUS_TYPE_STRING, &version); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_LOCAL].property_name, DBUS_TYPE_BOOLEAN, &is_local); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_USERNAME].property_name, DBUS_TYPE_STRING, &username); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HOSTNAME].property_name, DBUS_TYPE_STRING, &hostname); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_CHANNELS].property_name, DBUS_TYPE_UINT32, default_channels, n_default_channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &default_sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &default_sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARDS].property_name, DBUS_TYPE_OBJECT_PATH, cards, n_cards); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + + if (fallback_sink) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_sink); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + + if (fallback_source) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_source); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLES].property_name, DBUS_TYPE_OBJECT_PATH, samples, n_samples); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MODULES].property_name, DBUS_TYPE_OBJECT_PATH, modules, n_modules); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENTS].property_name, DBUS_TYPE_OBJECT_PATH, clients, n_clients); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MY_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &my_client); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_EXTENSIONS].property_name, DBUS_TYPE_STRING, extensions, n_extensions); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(username); + pa_xfree(hostname); + pa_xfree(default_channels); + pa_xfree(cards); + pa_xfree(sinks); + pa_xfree(sources); + pa_xfree(playback_streams); + pa_xfree(record_streams); + pa_xfree(samples); + pa_xfree(modules); + pa_xfree(clients); + pa_xfree(extensions); +} + +static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *card_name; + pa_card *card; + pa_dbusiface_card *dbus_card; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card."); + return; + } + + pa_assert_se((dbus_card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)))); + + object_path = pa_dbusiface_card_get_path(dbus_card); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *sink_name; + pa_sink *sink; + pa_dbusiface_device *dbus_sink; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + return; + } + + pa_assert_se((dbus_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)))); + + object_path = pa_dbusiface_device_get_path(dbus_sink); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *source_name; + pa_source *source; + pa_dbusiface_device *dbus_source; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + return; + } + + pa_assert_se((dbus_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)))); + + object_path = pa_dbusiface_device_get_path(dbus_source); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + char *sample_name; + pa_scache_entry *sample; + pa_dbusiface_sample *dbus_sample; + const char *object_path; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample."); + return; + } + + pa_assert_se((dbus_sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(sample->index)))); + + object_path = pa_dbusiface_sample_get_path(dbus_sample); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessageIter msg_iter; + const char *name; + dbus_uint32_t sample_format; + dbus_uint32_t sample_rate; + const dbus_uint32_t *channels; + unsigned n_channels; + const dbus_uint32_t *default_volume; + unsigned n_volume_entries; + pa_proplist *property_list; + const uint8_t *data; + unsigned data_length; + unsigned i; + pa_sample_spec ss; + pa_channel_map map; + pa_memchunk chunk; + uint32_t idx; + pa_dbusiface_sample *dbus_sample = NULL; + pa_scache_entry *sample = NULL; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + chunk.memblock = NULL; + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0) + goto finish; + + if (sample_format >= PA_SAMPLE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + goto finish; + } + + if (sample_rate <= 0 || sample_rate > PA_RATE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); + goto finish; + } + + if (n_channels == 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map."); + goto finish; + } + + if (n_channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels."); + goto finish; + } + + for (i = 0; i < n_channels; ++i) { + if (channels[i] >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + goto finish; + } + } + + if (n_volume_entries != 0 && n_volume_entries != n_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements."); + goto finish; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (default_volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + } + + if (data_length == 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty data."); + goto finish; + } + + if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample."); + goto finish; + } + + ss.format = sample_format; + ss.rate = sample_rate; + ss.channels = n_channels; + + if (!pa_frame_aligned(data_length, &ss)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels."); + goto finish; + } + + map.channels = n_channels; + for (i = 0; i < n_channels; ++i) + map.map[i] = channels[i]; + + chunk.memblock = pa_memblock_new(c->core->mempool, data_length); + chunk.index = 0; + chunk.length = data_length; + + memcpy(pa_memblock_acquire(chunk.memblock), data, data_length); + pa_memblock_release(chunk.memblock); + + if (pa_scache_add_item(c->core, name, &ss, &map, &chunk, property_list, &idx) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Adding the sample failed."); + goto finish; + } + + sample = pa_idxset_get_by_index(c->core->scache, idx); + + if (n_volume_entries > 0) { + sample->volume.channels = n_channels; + for (i = 0; i < n_volume_entries; ++i) + sample->volume.values[i] = default_volume[i]; + sample->volume_is_set = TRUE; + } else { + sample->volume_is_set = FALSE; + } + + dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample); + + object_path = pa_dbusiface_sample_get_path(dbus_sample); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); + +finish: + if (property_list) + pa_proplist_free(property_list); + + if (chunk.memblock) + pa_memblock_unref(chunk.memblock); +} + +static pa_bool_t contains_space(const char *string) { + const char *p; + + pa_assert(string); + + for (p = string; *p; ++p) { + if (isspace(*p)) + return TRUE; + } + + return FALSE; +} + +static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + char *name; + const char *key; + const char *value; + char *escaped_value; + pa_strbuf *arg_buffer = NULL; + pa_module *module; + pa_dbusiface_module *dbus_module; + const char *object_path; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (c->core->disallow_module_loading) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module loading."); + return; + } + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + return; + } + + if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + return; + } + + arg_buffer = pa_strbuf_new(); + + dbus_message_iter_recurse(&msg_iter, &dict_iter); + + while (dbus_message_iter_has_next(&dict_iter)) { + if (!pa_strbuf_isempty(arg_buffer)) + pa_strbuf_putc(arg_buffer, ' '); + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + goto finish; + } + + dbus_message_iter_get_basic(&dict_iter, &key); + + if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name."); + goto finish; + } + + if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected."); + goto finish; + } + + dbus_message_iter_get_basic(&dict_iter, &value); + + escaped_value = pa_escape(value, "\""); + pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); + pa_xfree(escaped_value); + } + + if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module."); + goto finish; + } + + dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); + + object_path = pa_dbusiface_module_get_path(dbus_module); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); + +finish: + if (arg_buffer) + pa_strbuf_free(arg_buffer); +} + +static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (c->core->disallow_exit) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow exiting."); + return; + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_core_exit(c->core, FALSE, 0); +} + +static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *signal; + char **objects = NULL; + int n_objects; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + goto finish; + } + + pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects); + + pa_dbus_send_empty_reply(conn, msg); + +finish: + dbus_free_string_array(objects); +} + +static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_core *c = userdata; + const char *signal; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + dbus_error_free(&error); + return; + } + + pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_core *c = userdata; + pa_dbusiface_card *card = NULL; + pa_dbusiface_device *device = NULL; + pa_dbusiface_stream *stream = NULL; + pa_dbusiface_sample *sample = NULL; + pa_dbusiface_module *module = NULL; + pa_dbusiface_client *client = NULL; + DBusMessage *signal = NULL; + const char *object_path = NULL; + pa_sink *new_fallback_sink = NULL; + pa_source *new_fallback_source = NULL; + + pa_assert(c); + + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SERVER: + new_fallback_sink = pa_namereg_get_default_sink(core); + new_fallback_source = pa_namereg_get_default_source(core); + + if (c->fallback_sink != new_fallback_sink) { + c->fallback_sink = new_fallback_sink; + + if (new_fallback_sink) { + pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))); + + object_path = pa_dbusiface_device_get_path(device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + + if (c->fallback_source != new_fallback_source) { + c->fallback_source = new_fallback_source; + + if (new_fallback_source) { + pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))); + + object_path = pa_dbusiface_device_get_path(device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + break; + + case PA_SUBSCRIPTION_EVENT_CARD: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + + object_path = pa_dbusiface_card_get_path(card); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_card_get_path(card); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_card_free(card); + } + break; + + case PA_SUBSCRIPTION_EVENT_SINK: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + object_path = pa_dbusiface_device_get_path(device); + + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, object_path, device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); + object_path = pa_dbusiface_device_get_path(device); + pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_device_free(device); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + object_path = pa_dbusiface_device_get_path(device); + + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, object_path, device); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); + object_path = pa_dbusiface_device_get_path(device); + pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_device_free(device); + } + break; + + case PA_SUBSCRIPTION_EVENT_SINK_INPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_stream_free(stream); + } + break; + + case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_stream_get_path(stream); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_stream_free(stream); + } + break; + + case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + /* We may have created the pa_dbusiface_sample object already + * in handle_upload_sample. */ + if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { + sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); + } + + object_path = pa_dbusiface_sample_get_path(sample); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_sample_get_path(sample); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_sample_free(sample); + } + break; + + case PA_SUBSCRIPTION_EVENT_MODULE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + /* We may have created the pa_dbusiface_module object already + * in handle_load_module. */ + if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { + module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); + } + + object_path = pa_dbusiface_module_get_path(module); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_module_get_path(module); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_module_free(module); + } + break; + + case PA_SUBSCRIPTION_EVENT_CLIENT: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + + object_path = pa_dbusiface_client_get_path(client); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))); + + object_path = pa_dbusiface_client_get_path(client); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbusiface_client_free(client); + } + break; + } + + if (signal) { + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + } +} + +pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { + pa_dbusiface_core *c; + pa_card *card; + pa_sink *sink; + pa_source *source; + pa_dbusiface_device *device; + pa_sink_input *sink_input; + pa_source_output *source_output; + pa_scache_entry *sample; + pa_module *module; + pa_client *client; + uint32_t idx; + + pa_assert(core); + + c = pa_xnew(pa_dbusiface_core, 1); + c->core = core; + c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c); + c->dbus_protocol = pa_dbus_protocol_get(core); + c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sinks_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sinks_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->sources_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->sources_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->playback_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->record_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->samples = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->modules = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + c->fallback_sink = pa_namereg_get_default_sink(core); + c->fallback_source = pa_namereg_get_default_source(core); + + for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); + + for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { + device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + } + + for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { + device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + } + + for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH)); + + for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH)); + + for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH)); + + for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH)); + + for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH)); + + pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c); + + return c; +} + +static void free_card_cb(void *p, void *userdata) { + pa_dbusiface_card *c = p; + + pa_assert(c); + + pa_dbusiface_card_free(c); +} + +static void free_device_cb(void *p, void *userdata) { + pa_dbusiface_device *d = p; + + pa_assert(d); + + pa_dbusiface_device_free(d); +} + +static void free_stream_cb(void *p, void *userdata) { + pa_dbusiface_stream *s = p; + + pa_assert(s); + + pa_dbusiface_stream_free(s); +} + +static void free_sample_cb(void *p, void *userdata) { + pa_dbusiface_sample *s = p; + + pa_assert(s); + + pa_dbusiface_sample_free(s); +} + +static void free_module_cb(void *p, void *userdata) { + pa_dbusiface_module *m = p; + + pa_assert(m); + + pa_dbusiface_module_free(m); +} + +static void free_client_cb(void *p, void *userdata) { + pa_dbusiface_client *c = p; + + pa_assert(c); + + pa_dbusiface_client_free(c); +} + +void pa_dbusiface_core_free(pa_dbusiface_core *c) { + pa_assert(c); + + pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE); + + pa_subscription_free(c->subscription); + pa_hashmap_free(c->cards, free_card_cb, NULL); + pa_hashmap_free(c->sinks_by_index, free_device_cb, NULL); + pa_hashmap_free(c->sinks_by_path, NULL, NULL); + pa_hashmap_free(c->sources_by_index, free_device_cb, NULL); + pa_hashmap_free(c->sources_by_path, NULL, NULL); + pa_hashmap_free(c->playback_streams, free_stream_cb, NULL); + pa_hashmap_free(c->record_streams, free_stream_cb, NULL); + pa_hashmap_free(c->samples, free_sample_cb, NULL); + pa_hashmap_free(c->modules, free_module_cb, NULL); + pa_hashmap_free(c->clients, free_client_cb, NULL); + + pa_dbus_protocol_unref(c->dbus_protocol); + + pa_xfree(c); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h new file mode 100644 index 00000000..964a37bd --- /dev/null +++ b/src/modules/dbus/iface-core.h @@ -0,0 +1,38 @@ +#ifndef foodbusifacecorehfoo +#define foodbusifacecorehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Core interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_core pa_dbusiface_core; + +pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); +void pa_dbusiface_core_free(pa_dbusiface_core *c); + +#endif diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c new file mode 100644 index 00000000..3b3795eb --- /dev/null +++ b/src/modules/dbus/iface-device.c @@ -0,0 +1,105 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-device.h" + +#define SINK_OBJECT_NAME "sink" +#define SOURCE_OBJECT_NAME "source" + +enum device_type { + DEVICE_TYPE_SINK, + DEVICE_TYPE_SOURCE +}; + +struct pa_dbusiface_device { + union { + pa_sink *sink; + pa_source *source; + }; + enum device_type type; + char *path; +}; + +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) { + pa_dbusiface_device *d; + + pa_assert(sink); + pa_assert(path_prefix); + + d = pa_xnew(pa_dbusiface_device, 1); + d->sink = pa_sink_ref(sink); + d->type = DEVICE_TYPE_SINK; + d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index); + + return d; +} + +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) { + pa_dbusiface_device *d; + + pa_assert(source); + pa_assert(path_prefix); + + d = pa_xnew(pa_dbusiface_device, 1); + d->source = pa_source_ref(source); + d->type = DEVICE_TYPE_SOURCE; + d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index); + + return d; +} + +void pa_dbusiface_device_free(pa_dbusiface_device *d) { + pa_assert(d); + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_unref(d->sink); + else + pa_source_unref(d->source); + + pa_xfree(d->path); + pa_xfree(d); +} + +const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d) { + pa_assert(d); + + return d->path; +} + +pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d) { + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + return d->sink; +} + +pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d) { + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + return d->source; +} diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h new file mode 100644 index 00000000..81ad1d84 --- /dev/null +++ b/src/modules/dbus/iface-device.h @@ -0,0 +1,46 @@ +#ifndef foodbusifacedevicehfoo +#define foodbusifacedevicehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device, + * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source. + * + * See http://pulseaudio.org/wiki/DBusInterface for the interface + * documentation. + */ + +#include +#include + +typedef struct pa_dbusiface_device pa_dbusiface_device; + +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix); +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix); +void pa_dbusiface_device_free(pa_dbusiface_device *d); + +const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d); + +pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d); +pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d); + +#endif diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c new file mode 100644 index 00000000..1c95f9e6 --- /dev/null +++ b/src/modules/dbus/iface-module.c @@ -0,0 +1,61 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-module.h" + +#define OBJECT_NAME "module" + +struct pa_dbusiface_module { + pa_module *module; + char *path; +}; + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) { + pa_dbusiface_module *m; + + pa_assert(module); + pa_assert(path_prefix); + + m = pa_xnew(pa_dbusiface_module, 1); + m->module = module; + m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index); + + return m; +} + +void pa_dbusiface_module_free(pa_dbusiface_module *m) { + pa_assert(m); + + pa_xfree(m->path); + pa_xfree(m); +} + +const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) { + pa_assert(m); + + return m->path; +} diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h new file mode 100644 index 00000000..7f683e6a --- /dev/null +++ b/src/modules/dbus/iface-module.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacemodulehfoo +#define foodbusifacemodulehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Module. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Module interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_module pa_dbusiface_module; + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix); +void pa_dbusiface_module_free(pa_dbusiface_module *m); + +const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); + +#endif diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c new file mode 100644 index 00000000..b4a308a2 --- /dev/null +++ b/src/modules/dbus/iface-sample.c @@ -0,0 +1,61 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-sample.h" + +#define OBJECT_NAME "sample" + +struct pa_dbusiface_sample { + pa_scache_entry *sample; + char *path; +}; + +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) { + pa_dbusiface_sample *s; + + pa_assert(sample); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_sample, 1); + s->sample = sample; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index); + + return s; +} + +void pa_dbusiface_sample_free(pa_dbusiface_sample *s) { + pa_assert(s); + + pa_xfree(s->path); + pa_xfree(s); +} + +const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) { + pa_assert(s); + + return s->path; +} diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h new file mode 100644 index 00000000..1b85404e --- /dev/null +++ b/src/modules/dbus/iface-sample.h @@ -0,0 +1,40 @@ +#ifndef foodbusifacesamplehfoo +#define foodbusifacesamplehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Sample. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Sample interface + * documentation. + */ + +#include + +typedef struct pa_dbusiface_sample pa_dbusiface_sample; + +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix); +void pa_dbusiface_sample_free(pa_dbusiface_sample *c); + +const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c); + +#endif diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c new file mode 100644 index 00000000..1d9ffee6 --- /dev/null +++ b/src/modules/dbus/iface-stream.c @@ -0,0 +1,91 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-stream.h" + +#define PLAYBACK_OBJECT_NAME "playback_stream" +#define RECORD_OBJECT_NAME "record_stream" + +enum stream_type { + STREAM_TYPE_PLAYBACK, + STREAM_TYPE_RECORD +}; + +struct pa_dbusiface_stream { + union { + pa_sink_input *sink_input; + pa_source_output *source_output; + }; + enum stream_type type; + char *path; +}; + +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) { + pa_dbusiface_stream *s; + + pa_assert(sink_input); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_stream, 1); + s->sink_input = pa_sink_input_ref(sink_input); + s->type = STREAM_TYPE_PLAYBACK; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index); + + return s; +} + +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) { + pa_dbusiface_stream *s; + + pa_assert(source_output); + pa_assert(path_prefix); + + s = pa_xnew(pa_dbusiface_stream, 1); + s->source_output = pa_source_output_ref(source_output); + s->type = STREAM_TYPE_RECORD; + s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index); + + return s; +} + +void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_unref(s->sink_input); + else + pa_source_output_unref(s->source_output); + + pa_xfree(s->path); + pa_xfree(s); +} + +const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) { + pa_assert(s); + + return s->path; +} diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h new file mode 100644 index 00000000..cc2f3d61 --- /dev/null +++ b/src/modules/dbus/iface-stream.h @@ -0,0 +1,42 @@ +#ifndef foodbusifacestreamhfoo +#define foodbusifacestreamhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Stream. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Stream interface + * documentation. + */ + +#include +#include + +typedef struct pa_dbusiface_stream pa_dbusiface_stream; + +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix); +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix); +void pa_dbusiface_stream_free(pa_dbusiface_stream *s); + +const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s); + +#endif diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c new file mode 100644 index 00000000..807d32da --- /dev/null +++ b/src/modules/dbus/module-dbus-protocol.c @@ -0,0 +1,580 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + Copyright 2006 Lennart Poettering + Copyright 2006 Shams E. King + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iface-core.h" + +#include "module-dbus-protocol-symdef.h" + +PA_MODULE_DESCRIPTION("D-Bus interface"); +PA_MODULE_USAGE( + "access=local|remote|local,remote " + "tcp_port="); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_AUTHOR("Tanu Kaskinen"); +PA_MODULE_VERSION(PACKAGE_VERSION); + +#define CLEANUP_INTERVAL 10 /* seconds */ + +enum server_type { + SERVER_TYPE_LOCAL, + SERVER_TYPE_TCP +}; + +struct server; +struct connection; + +struct userdata { + pa_module *module; + pa_bool_t local_access; + pa_bool_t remote_access; + uint32_t tcp_port; + + struct server *local_server; + struct server *tcp_server; + + pa_idxset *connections; + + pa_time_event *cleanup_event; + + pa_dbus_protocol *dbus_protocol; + pa_dbusiface_core *core_iface; +}; + +struct server { + struct userdata *userdata; + enum server_type type; + DBusServer *dbus_server; +}; + +struct connection { + struct server *server; + pa_dbus_wrap_connection *wrap_conn; + pa_client *client; +}; + +static const char* const valid_modargs[] = { + "access", + "tcp_port", + NULL +}; + +static void connection_free(struct connection *c) { + pa_assert(c); + + pa_assert_se(pa_dbus_protocol_unregister_connection(c->server->userdata->dbus_protocol, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0); + + pa_client_free(c->client); + pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL)); + pa_dbus_wrap_connection_free(c->wrap_conn); + pa_xfree(c); +} + +/* Called from pa_client_kill(). */ +static void client_kill_cb(pa_client *c) { + struct connection *conn; + + pa_assert(c); + pa_assert(c->userdata); + + conn = c->userdata; + connection_free(conn); + + pa_log_info("Connection killed."); +} + +static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { + pa_log_debug("Allowing connection by user %lu.", uid); + + return TRUE; +} + +/* Called by D-Bus when a new client connection is received. */ +static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) { + struct server *s = data; + struct connection *c; + pa_client_new_data new_data; + pa_client *client; + + pa_assert(new_connection); + pa_assert(s); + + pa_client_new_data_init(&new_data); + new_data.module = s->userdata->module; + new_data.driver = __FILE__; + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ + client = pa_client_new(s->userdata->module->core, &new_data); + pa_client_new_data_done(&new_data); + + if (!client) { + dbus_connection_close(new_connection); + return; + } + + if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) { + /* FIXME: Here we allow anyone from anywhere to access the server, + * anonymously. Access control should be configurable. */ + dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL); + dbus_connection_set_allow_anonymous(new_connection, TRUE); + } + + c = pa_xnew(struct connection, 1); + c->server = s; + c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection); + c->client = client; + + c->client->kill = client_kill_cb; + c->client->send_event = NULL; /* TODO: Implement this. */ + c->client->userdata = c; + + pa_idxset_put(s->userdata->connections, c, NULL); + + pa_assert_se(pa_dbus_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 0); +} + +/* Called by PA mainloop when a D-Bus fd watch event needs handling. */ +static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { + unsigned int flags = 0; + DBusWatch *watch = userdata; + +#if HAVE_DBUS_WATCH_GET_UNIX_FD + pa_assert(fd == dbus_watch_get_unix_fd(watch)); +#else + pa_assert(fd == dbus_watch_get_fd(watch)); +#endif + + if (!dbus_watch_get_enabled(watch)) { + pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd); + return; + } + + if (events & PA_IO_EVENT_INPUT) + flags |= DBUS_WATCH_READABLE; + if (events & PA_IO_EVENT_OUTPUT) + flags |= DBUS_WATCH_WRITABLE; + if (events & PA_IO_EVENT_HANGUP) + flags |= DBUS_WATCH_HANGUP; + if (events & PA_IO_EVENT_ERROR) + flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(watch, flags); +} + +/* Called by PA mainloop when a D-Bus timer event needs handling. */ +static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) { + DBusTimeout *timeout = userdata; + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval next = *tv; + dbus_timeout_handle(timeout); + + /* restart it for the next scheduled time */ + pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + mainloop->time_restart(e, &next); + } +} + +/* Translates D-Bus fd watch event flags to PA IO event flags. */ +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) { + unsigned int flags; + pa_io_event_flags_t events = 0; + + pa_assert(watch); + + flags = dbus_watch_get_flags(watch); + + /* no watch flags for disabled watches */ + if (!dbus_watch_get_enabled(watch)) + return PA_IO_EVENT_NULL; + + if (flags & DBUS_WATCH_READABLE) + events |= PA_IO_EVENT_INPUT; + if (flags & DBUS_WATCH_WRITABLE) + events |= PA_IO_EVENT_OUTPUT; + + return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR; +} + +/* Called by D-Bus when a D-Bus fd watch event is added. */ +static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + mainloop = s->userdata->module->core->mainloop; + + ev = mainloop->io_new( + mainloop, +#if HAVE_DBUS_WATCH_GET_UNIX_FD + dbus_watch_get_unix_fd(watch), +#else + dbus_watch_get_fd(watch), +#endif + get_watch_flags(watch), io_event_cb, watch); + + dbus_watch_set_data(watch, ev, NULL); + + return TRUE; +} + +/* Called by D-Bus when a D-Bus fd watch event is removed. */ +static void watch_remove_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + if ((ev = dbus_watch_get_data(watch))) + s->userdata->module->core->mainloop->io_free(ev); +} + +/* Called by D-Bus when a D-Bus fd watch event is toggled. */ +static void watch_toggled_cb(DBusWatch *watch, void *data) { + struct server *s = data; + pa_io_event *ev; + + pa_assert(watch); + pa_assert(s); + + pa_assert_se(ev = dbus_watch_get_data(watch)); + + /* get_watch_flags() checks if the watch is enabled */ + s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch)); +} + +/* Called by D-Bus when a D-Bus timer event is added. */ +static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_time_event *ev; + struct timeval tv; + + pa_assert(timeout); + pa_assert(s); + + if (!dbus_timeout_get_enabled(timeout)) + return FALSE; + + mainloop = s->userdata->module->core->mainloop; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout); + + dbus_timeout_set_data(timeout, ev, NULL); + + return TRUE; +} + +/* Called by D-Bus when a D-Bus timer event is removed. */ +static void timeout_remove_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(s); + + if ((ev = dbus_timeout_get_data(timeout))) + s->userdata->module->core->mainloop->time_free(ev); +} + +/* Called by D-Bus when a D-Bus timer event is toggled. */ +static void timeout_toggled_cb(DBusTimeout *timeout, void *data) { + struct server *s = data; + pa_mainloop_api *mainloop; + pa_time_event *ev; + + pa_assert(timeout); + pa_assert(s); + + mainloop = s->userdata->module->core->mainloop; + + pa_assert_se(ev = dbus_timeout_get_data(timeout)); + + if (dbus_timeout_get_enabled(timeout)) { + struct timeval tv; + + pa_gettimeofday(&tv); + pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000); + + mainloop->time_restart(ev, &tv); + } else + mainloop->time_restart(ev, NULL); +} + +static void server_free(struct server *s) { + pa_assert(s); + + if (s->dbus_server) { + dbus_server_disconnect(s->dbus_server); + dbus_server_unref(s->dbus_server); + } + + pa_xfree(s); +} + +static struct server *start_server(struct userdata *u, const char *address, enum server_type type) { + /* XXX: We assume that when we unref the DBusServer instance at module + * shutdown, nobody else holds any references to it. If we stop assuming + * that someday, dbus_server_set_new_connection_function, + * dbus_server_set_watch_functions and dbus_server_set_timeout_functions + * calls should probably register free callbacks, instead of providing NULL + * as they do now. */ + + struct server *s = NULL; + DBusError error; + + pa_assert(u); + pa_assert(address); + + dbus_error_init(&error); + + s = pa_xnew0(struct server, 1); + s->userdata = u; + s->dbus_server = dbus_server_listen(address, &error); + + if (dbus_error_is_set(&error)) { + pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message); + goto fail; + } + + dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL); + + if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) { + pa_log("dbus_server_set_watch_functions() ran out of memory."); + goto fail; + } + + if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) { + pa_log("dbus_server_set_timeout_functions() ran out of memory."); + goto fail; + } + + return s; + +fail: + if (s) + server_free(s); + + dbus_error_free(&error); + + return NULL; +} + +static struct server *start_local_server(struct userdata *u) { + struct server *s = NULL; + char *address = NULL; + + pa_assert(u); + + address = pa_get_dbus_address_from_server_type(u->module->core->server_type); + + s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */ + + pa_xfree(address); + + return s; +} + +static struct server *start_tcp_server(struct userdata *u) { + struct server *s = NULL; + char *address = NULL; + + pa_assert(u); + + address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port); + + s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */ + + pa_xfree(address); + + return s; +} + +static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) { + const char *value = NULL; + + pa_assert(ma); + pa_assert(local_access); + pa_assert(remote_access); + + if (!(value = pa_modargs_get_value(ma, "access", NULL))) + return 0; + + if (!strcmp(value, "local")) { + *local_access = TRUE; + *remote_access = FALSE; + } else if (!strcmp(value, "remote")) { + *local_access = FALSE; + *remote_access = TRUE; + } else if (!strcmp(value, "local,remote")) { + *local_access = TRUE; + *remote_access = TRUE; + } else + return -1; + + return 0; +} + +/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */ +static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct connection *conn = NULL; + uint32_t idx; + struct timeval cleanup_timeval; + unsigned free_count = 0; + + for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) { + if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) { + connection_free(conn); + ++free_count; + } + } + + if (free_count > 0) + pa_log_debug("Freed %u dead D-Bus client connections.", free_count); + + pa_gettimeofday(&cleanup_timeval); + cleanup_timeval.tv_sec += CLEANUP_INTERVAL; + u->module->core->mainloop->time_restart(e, &cleanup_timeval); +} + +int pa__init(pa_module *m) { + struct userdata *u = NULL; + pa_modargs *ma = NULL; + struct timeval cleanup_timeval; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->module = m; + u->local_access = TRUE; + u->remote_access = FALSE; + u->tcp_port = PA_DBUS_DEFAULT_PORT; + + if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) { + pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL)); + goto fail; + } + + if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) { + pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL)); + goto fail; + } + + if (u->local_access && !(u->local_server = start_local_server(u))) { + pa_log("Starting the local D-Bus server failed."); + goto fail; + } + + if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) { + pa_log("Starting the D-Bus server for remote connections failed."); + goto fail; + } + + u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + pa_gettimeofday(&cleanup_timeval); + cleanup_timeval.tv_sec += CLEANUP_INTERVAL; + u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u); + + u->dbus_protocol = pa_dbus_protocol_get(m->core); + u->core_iface = pa_dbusiface_core_new(m->core); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa__done(m); + + return -1; +} + +/* Called by idxset when the connection set is freed. */ +static void connection_free_cb(void *p, void *userdata) { + struct connection *conn = p; + + pa_assert(conn); + + connection_free(conn); +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->core_iface) + pa_dbusiface_core_free(u->core_iface); + + if (u->cleanup_event) + m->core->mainloop->time_free(u->cleanup_event); + + if (u->connections) + pa_idxset_free(u->connections, connection_free_cb, NULL); + + if (u->tcp_server) + server_free(u->tcp_server); + + if (u->local_server) + server_free(u->local_server); + + if (u->dbus_protocol) + pa_dbus_protocol_unref(u->dbus_protocol); + + pa_xfree(u); + m->userdata = NULL; +} -- cgit From 018810ec9a96d63446bdc9a82d6c931779f7a97d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 27 Jul 2009 20:01:39 +0300 Subject: Bug fixing and minor cleanups. --- src/modules/dbus/iface-core.c | 95 ++++++++++++++++++++----------------------- src/pulsecore/dbus-util.c | 73 ++++++++++++++++++++++++++++----- src/pulsecore/dbus-util.h | 9 ++-- 3 files changed, 111 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 31f12603..d17499c1 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -220,7 +220,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .receive_cb = handle_upload_sample }, [METHOD_HANDLER_LOAD_MODULE] = { .method_name = "LoadModule", - .arguments = upload_sample_args, + .arguments = load_module_args, .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_load_module }, [METHOD_HANDLER_EXIT] = { @@ -424,11 +424,8 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; pa_channel_map new_channel_map; - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - DBusMessageIter array_iter; dbus_uint32_t *default_channels; - int n_channels; + unsigned n_channels; unsigned i; pa_assert(conn); @@ -437,37 +434,16 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, pa_channel_map_init(&new_channel_map); - pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0) return; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array."); - return; - } - dbus_message_iter_recurse(&variant_iter, &array_iter); - - if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32."); + if (n_channels <= 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); return; } - dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); - - if (n_channels <= 0) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); + if (n_channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -475,7 +451,7 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, for (i = 0; i < new_channel_map.channels; ++i) { if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u.", default_channels[i]); return; } @@ -547,7 +523,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms return; if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); return; } @@ -1149,7 +1125,7 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1181,7 +1157,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1213,7 +1189,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1245,7 +1221,7 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } @@ -1433,6 +1409,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use pa_dbusiface_core *c = userdata; DBusMessageIter msg_iter; DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; char *name; const char *key; const char *value; @@ -1441,6 +1418,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use pa_module *module; pa_dbusiface_module *dbus_module; const char *object_path; + int arg_type; pa_assert(conn); pa_assert(msg); @@ -1459,13 +1437,18 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) return; - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(&msg_iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type); return; } - if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + arg_type = dbus_message_iter_get_element_type(&msg_iter); + if (arg_type != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); return; } @@ -1473,28 +1456,38 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use dbus_message_iter_recurse(&msg_iter, &dict_iter); - while (dbus_message_iter_has_next(&dict_iter)) { + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { if (!pa_strbuf_isempty(arg_buffer)) pa_strbuf_putc(arg_buffer, ' '); - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); + + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto finish; } - dbus_message_iter_get_basic(&dict_iter, &key); + dbus_message_iter_get_basic(&dict_entry_iter, &key); + dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key); goto finish; } - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected."); + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); goto finish; } - dbus_message_iter_get_basic(&dict_iter, &value); + dbus_message_iter_get_basic(&dict_entry_iter, &value); + + dbus_message_iter_next(&dict_iter); escaped_value = pa_escape(value, "\""); pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); @@ -1549,7 +1542,7 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); goto finish; } @@ -1574,7 +1567,7 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage * dbus_error_init(&error); if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index cfc3e8cb..43dbd6b2 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -446,18 +446,25 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) { } } -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) { +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) { + va_list ap; + char *message; DBusMessage *reply = NULL; pa_assert(c); pa_assert(in_reply_to); pa_assert(name); - pa_assert(message); + pa_assert(format); + va_start(ap, format); + message = pa_vsprintf_malloc(format, ap); + va_end(ap); pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message))); pa_assert_se(dbus_connection_send(c, reply, NULL)); dbus_message_unref(reply); + + pa_xfree(message); } void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) { @@ -655,36 +662,71 @@ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int dbus_message_iter_recurse(&msg_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type."); + if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) + return -1; + + return 0; +} + +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { + DBusMessageIter msg_iter; + DBusMessageIter variant_iter; + + pa_assert(c); + pa_assert(msg); + pa_assert(dbus_type_is_fixed(item_type)); + pa_assert(data); + pa_assert(n); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); return -1; } - dbus_message_iter_get_basic(&variant_iter, data); + if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); + return -1; + } + + dbus_message_iter_recurse(&msg_iter, &variant_iter); + + if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) + return -1; return 0; } int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { + int arg_type; + pa_assert(c); pa_assert(msg); pa_assert(iter); pa_assert(dbus_type_is_basic(type)); pa_assert(data); - if (dbus_message_iter_get_arg_type(iter) != type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != type) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); return -1; } dbus_message_iter_get_basic(iter, data); + dbus_message_iter_next(iter); + return 0; } int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { DBusMessageIter array_iter; int signed_n; + int arg_type; + int element_type; pa_assert(c); pa_assert(msg); @@ -693,13 +735,18 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_assert(array); pa_assert(n); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); return -1; } - if (dbus_message_iter_get_element_type(iter) != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type."); + element_type = dbus_message_iter_get_element_type(iter); + if (element_type != item_type) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); return -1; } @@ -707,6 +754,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); + dbus_message_iter_next(iter); + pa_assert(signed_n >= 0); *n = signed_n; @@ -769,6 +818,8 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); } + dbus_message_iter_next(iter); + return proplist; fail: diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 1a8aeac9..97aae372 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -63,7 +63,7 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message); +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); @@ -76,10 +76,11 @@ void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); -/* Helper function for extracting the value argument of a Set call for a - * property with a basic type. If the message is invalid, an error reply is - * sent and a negative number is returned. */ +/* Helper functions for extracting the value argument of a Set call. If the + * message is invalid, an error reply is sent and a negative number is + * returned. */ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); /* If the arguments can't be read from the iterator, an error reply is sent and * a negative number is returned. */ -- cgit From b061957e57f74d7aa51bde9d24dd5e5c75af9497 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Thu, 30 Jul 2009 13:11:32 +0300 Subject: dbus/iface-core.c: Make sure D-Bus objects are created only once. --- src/modules/dbus/iface-core.c | 48 +++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index d17499c1..cdfd2a3c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1632,8 +1632,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { + card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + } object_path = pa_dbusiface_card_get_path(card); @@ -1654,11 +1656,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); - object_path = pa_dbusiface_device_get_path(device); + if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { + device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + } - pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sinks_by_path, object_path, device); + object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); @@ -1677,11 +1681,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); - object_path = pa_dbusiface_device_get_path(device); + if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { + device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + } - pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sources_by_path, object_path, device); + object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); @@ -1700,8 +1706,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { + stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + } object_path = pa_dbusiface_stream_get_path(stream); @@ -1722,8 +1730,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { + stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + } object_path = pa_dbusiface_stream_get_path(stream); @@ -1744,8 +1754,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - /* We may have created the pa_dbusiface_sample object already - * in handle_upload_sample. */ if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); @@ -1770,8 +1778,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - /* We may have created the pa_dbusiface_module object already - * in handle_load_module. */ if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); @@ -1796,8 +1802,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { + client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + } object_path = pa_dbusiface_client_get_path(client); -- cgit From c354a08fe3cf2468688264124b763efb1bf1df85 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 31 Jul 2009 12:05:49 +0300 Subject: dbus-protocol: Implement extension registration. --- src/pulsecore/protocol-dbus.c | 91 ++++++++++++++++++++++++++++++++++--------- src/pulsecore/protocol-dbus.h | 39 +++++++++++++++++-- 2 files changed, 108 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index fb7d168d..81a6c024 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -41,7 +41,9 @@ struct pa_dbus_protocol { pa_core *core; pa_hashmap *objects; /* Object path -> struct object_entry */ pa_hashmap *connections; /* DBusConnection -> struct connection_entry */ - pa_hashmap *extensions; /* String -> anything */ + pa_idxset *extensions; /* Strings */ + + pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX]; }; struct object_entry { @@ -109,6 +111,7 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) { static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { pa_dbus_protocol *p; + unsigned i; pa_assert(c); @@ -117,7 +120,10 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p->core = c; p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_init(&p->hooks[i], p); pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0); @@ -143,6 +149,8 @@ pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) { } void pa_dbus_protocol_unref(pa_dbus_protocol *p) { + unsigned i; + pa_assert(p); pa_assert(PA_REFCNT_VALUE(p) >= 1); @@ -151,11 +159,14 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert(pa_hashmap_isempty(p->objects)); pa_assert(pa_hashmap_isempty(p->connections)); - pa_assert(pa_hashmap_isempty(p->extensions)); + pa_assert(pa_idxset_isempty(p->extensions)); pa_hashmap_free(p->objects, NULL, NULL); pa_hashmap_free(p->connections, NULL, NULL); - pa_hashmap_free(p->extensions, NULL, NULL); + pa_idxset_free(p->extensions, NULL, NULL); + + for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i) + pa_hook_done(&p->hooks[i]); pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); @@ -660,6 +671,8 @@ static void property_handler_free_cb(void *p, void *userdata) { pa_xfree((char *) h->property_name); pa_xfree((char *) h->type); + + pa_xfree(h); } int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) { @@ -792,6 +805,18 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * return 0; } +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { + struct connection_entry *conn_entry; + + pa_assert(p); + pa_assert(conn); + + if (!(conn_entry = pa_hashmap_get(p->connections, conn))) + return NULL; + + return conn_entry->client; +} + void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; @@ -893,18 +918,6 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { } } -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) { - struct connection_entry *conn_entry; - - pa_assert(p); - pa_assert(conn); - - if (!(conn_entry = pa_hashmap_get(p->connections, conn))) - return NULL; - - return conn_entry->client; -} - const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { const char **extensions; const char *ext_name; @@ -914,17 +927,59 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { pa_assert(p); pa_assert(n); - *n = pa_hashmap_size(p->extensions); + *n = pa_idxset_size(p->extensions); if (*n <= 0) return NULL; extensions = pa_xnew(const char *, *n); - while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) { + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { extensions[i] = ext_name; ++i; } return extensions; } + +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + internal_name = pa_xstrdup(name); + + if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) { + pa_xfree(internal_name); + return -1; + } + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name); + + return 0; +} + +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) { + char *internal_name; + + pa_assert(p); + pa_assert(name); + + if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL))) + return -1; + + pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name); + + pa_xfree(internal_name); + + return 0; +} + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { + pa_assert(p); + pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); + pa_assert(cb); + + return pa_hook_connect(&p->hooks[hook], prio, cb, data); +} diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 27198f48..f2b1b50b 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -119,9 +119,12 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con * registered. */ int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client); -/* Returns a negative number if the connection wasn't registered. */ +/* Returns a negative number if the connection isn't registered. */ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn); +/* Returns NULL if the connection isn't registered. */ +pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); + /* Enables signal receiving for the given connection. The connection must have * been registered earlier. * @@ -145,9 +148,6 @@ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); -/* Returns NULL if the connection isn't registered. */ -pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); - /* Returns an array of extension identifier strings. The strings pointers point * to the internal copies, so don't free the strings. The caller must free the * array, however. Also, do not save the returned pointer or any of the string @@ -155,4 +155,35 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * need to save the array, copy it. */ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n); +/* Modules that want to provide a D-Bus interface for clients should register + * an identifier that the clients can use to check whether the additional + * functionality is available. + * + * This function registers the extension with the given name. It is recommended + * that the name follows the D-Bus interface naming convention, so that the + * names remain unique in case there will be at some point in the future + * extensions that aren't included with the main PulseAudio source tree. For + * in-tree extensions the convention is to use the org.PulseAudio.Ext + * namespace. + * + * It is suggested that the name contains a version number, and whenever the + * extension interface is modified in non-backwards compatible way, the version + * number is incremented. + * + * Fails and returns a negative number if the extension is already registered. + */ +int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name); + +/* Returns a negative number if the extension isn't registered. */ +int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name); + +/* All hooks have the pa_dbus_protocol object as hook data. */ +typedef enum pa_dbus_protocol_hook { + PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */ + PA_DBUS_PROTOCOL_HOOK_MAX +} pa_dbus_protocol_hook_t; + +pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); + #endif -- cgit From 68cb63c0d9b1493ebc4274f9ea1fb05a8c273574 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 31 Jul 2009 12:06:53 +0300 Subject: dbusiface-core: Send signals whenever extensions are registered and unregistered. --- src/modules/dbus/iface-core.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index cdfd2a3c..e2e3be2c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -112,6 +112,9 @@ struct pa_dbusiface_core { pa_sink *fallback_sink; pa_source *fallback_source; + + pa_hook_slot *extension_registered_slot; + pa_hook_slot *extension_unregistered_slot; }; enum property_handler_index { @@ -259,6 +262,8 @@ enum signal_index { SIGNAL_MODULE_REMOVED, SIGNAL_NEW_CLIENT, SIGNAL_CLIENT_REMOVED, + SIGNAL_NEW_EXTENSION, + SIGNAL_EXTENSION_REMOVED, SIGNAL_MAX }; @@ -280,6 +285,8 @@ static pa_dbus_arg_info new_module_args[] = { { "module", static pa_dbus_arg_info module_removed_args[] = { { "module", "o", NULL } }; static pa_dbus_arg_info new_client_args[] = { { "client", "o", NULL } }; static pa_dbus_arg_info client_removed_args[] = { { "client", "o", NULL } }; +static pa_dbus_arg_info new_extension_args[] = { { "extension", "s", NULL } }; +static pa_dbus_arg_info extension_removed_args[] = { { "extension", "s", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_NEW_CARD] = { .name = "NewCard", .arguments = new_card_args, .n_arguments = 1 }, @@ -300,6 +307,8 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_MODULE_REMOVED] = { .name = "ModuleRemoved", .arguments = module_removed_args, .n_arguments = 1 }, [SIGNAL_NEW_CLIENT] = { .name = "NewClient", .arguments = new_client_args, .n_arguments = 1 }, [SIGNAL_CLIENT_REMOVED] = { .name = "ClientRemoved", .arguments = client_removed_args, .n_arguments = 1 }, + [SIGNAL_NEW_EXTENSION] = { .name = "NewExtension", .arguments = new_extension_args, .n_arguments = 1 }, + [SIGNAL_EXTENSION_REMOVED] = { .name = "ExtensionRemoved", .arguments = extension_removed_args, .n_arguments = 1 } }; static pa_dbus_interface_info core_interface_info = { @@ -1831,6 +1840,40 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } +static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_core *c = slot_data; + const char *ext_name = call_data; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(ext_name); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; +} + +static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_core *c = slot_data; + const char *ext_name = call_data; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(ext_name); + + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; +} + pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_dbusiface_core *c; pa_card *card; @@ -1862,6 +1905,8 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); c->fallback_sink = pa_namereg_get_default_sink(core); c->fallback_source = pa_namereg_get_default_source(core); + c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); + c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); @@ -1962,6 +2007,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hashmap_free(c->samples, free_sample_cb, NULL); pa_hashmap_free(c->modules, free_module_cb, NULL); pa_hashmap_free(c->clients, free_client_cb, NULL); + pa_hook_slot_free(c->extension_registered_slot); + pa_hook_slot_free(c->extension_unregistered_slot); pa_dbus_protocol_unref(c->dbus_protocol); -- cgit From a1ba80bc4e715b8ce77f55676bc63b02c1a82d3c Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 1 Aug 2009 08:26:51 +0300 Subject: dbusiface-core: Don't die if we get a default sink/source change event before the new default device is actually created. --- src/modules/dbus/iface-core.c | 50 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e2e3be2c..69c1bd25 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1609,9 +1609,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_sink != new_fallback_sink) { c->fallback_sink = new_fallback_sink; - if (new_fallback_sink) { - pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))); - + if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); @@ -1625,9 +1623,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_source != new_fallback_source) { c->fallback_source = new_fallback_source; - if (new_fallback_source) { - pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))); - + if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); @@ -1665,8 +1661,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); + if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH); + device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } @@ -1676,6 +1674,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + + if (c->fallback_sink && pa_streq(c->fallback_sink->name, sink->name)) { + /* We have got default sink change event, but at that point + * the D-Bus sink object wasn't created yet. Now that the + * object is created, let's send the fallback sink change + * signal. */ + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); object_path = pa_dbusiface_device_get_path(device); @@ -1690,8 +1705,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + pa_source *source = pa_idxset_get_by_index(core->sources, idx); + if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH); + device = pa_dbusiface_device_new_source(source, OBJECT_PATH); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } @@ -1701,6 +1718,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + + if (c->fallback_source && pa_streq(c->fallback_source->name, source->name)) { + /* We have got default source change event, but at that + * point the D-Bus source object wasn't created yet. Now + * that the object is created, let's send the fallback + * source change signal. */ + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); object_path = pa_dbusiface_device_get_path(device); -- cgit From 8c840572c7e2e560efcefe66707527a1dc4d16a4 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 2 Aug 2009 11:12:21 +0300 Subject: dbus-protocol: Add debugging output (temporary change). --- src/pulsecore/protocol-dbus.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 81a6c024..475b952f 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -306,6 +306,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } + pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -327,8 +328,10 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if (*interface) { if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; - else + else { + pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ + } } else { pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); return FOUND_GET_ALL; @@ -378,8 +381,10 @@ static enum find_result_t find_handler(struct object_entry *obj_entry, if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) return FOUND_METHOD; - else + else { + pa_log("Message has unknown interface or there's no method handler."); return NO_SUCH_METHOD; + } } else { /* The method call doesn't contain an interface. */ if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { @@ -411,6 +416,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || @@ -624,6 +631,8 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); + pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + return 0; fail: -- cgit From 805af5e8010228bee7521fbd1148e167cfb21e77 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 3 Aug 2009 19:36:19 +0300 Subject: dbus-util: Fix broken proplist reading logic. --- src/pulsecore/dbus-util.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 43dbd6b2..5db7f218 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -765,6 +765,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + int arg_type; pa_proplist *proplist = NULL; const char *key; const uint8_t *value; @@ -774,13 +776,18 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM pa_assert(msg); pa_assert(iter); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); return NULL; } - if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected."); + arg_type = dbus_message_iter_get_element_type(iter); + if (arg_type != DBUS_TYPE_DICT_ENTRY) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); return NULL; } @@ -788,34 +795,45 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM dbus_message_iter_recurse(iter, &dict_iter); - while (dbus_message_iter_has_next(&dict_iter)) { - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected."); + while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); + + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_STRING) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_basic(&dict_iter, &key); + dbus_message_iter_get_basic(&dict_entry_iter, &key); + dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); goto fail; } - if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected."); + arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); + else + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); goto fail; } - if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected."); + arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); + if (arg_type != DBUS_TYPE_BYTE) { + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); goto fail; } - dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length); + dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length); pa_assert(value_length >= 0); pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0); + + dbus_message_iter_next(&dict_iter); } dbus_message_iter_next(iter); -- cgit From d9d166a691cd5ecc786b73f1377244b8d51b4327 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 3 Aug 2009 19:38:02 +0300 Subject: stream-restore: Expose module to D-Bus. --- src/Makefile.am | 5 + src/modules/module-stream-restore.c | 1055 ++++++++++++++++++++++++++++++++++- 2 files changed, 1055 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 47eb6479..07f81a6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1536,6 +1536,11 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS) module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_stream_restore_la_CFLAGS = $(AM_CFLAGS) +if HAVE_DBUS +module_stream_restore_la_LIBADD += $(DBUS_LIBS) +module_stream_restore_la_CFLAGS += $(DBUS_CFLAGS) +endif + # Card profile restore module module_card_restore_la_SOURCES = modules/module-card-restore.c module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index e60cc735..bccdf938 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2008 Lennart Poettering + Copyright 2009 Tanu Kaskinen PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -51,6 +52,11 @@ #include #include +#ifdef HAVE_DBUS +#include +#include +#endif + #include "module-stream-restore-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering"); @@ -100,6 +106,12 @@ struct userdata { pa_native_protocol *protocol; pa_idxset *subscribed; + +#ifdef HAVE_DBUS + pa_dbus_protocol *dbus_protocol; + pa_hashmap *dbus_entries; + uint32_t next_index; /* For generating object paths for entries. */ +#endif }; #define ENTRY_VERSION 2 @@ -122,6 +134,883 @@ enum { SUBCOMMAND_EVENT }; +static struct entry *read_entry(struct userdata *u, const char *name); +static void apply_entry(struct userdata *u, const char *name, struct entry *e); +static void trigger_save(struct userdata *u); + +#ifdef HAVE_DBUS + +#define OBJECT_PATH "/org/pulseaudio/stream_restore1" +#define ENTRY_OBJECT_NAME "entry" +#define INTERFACE_STREAM_RESTORE "org.PulseAudio.Ext.StreamRestore1" +#define INTERFACE_ENTRY INTERFACE_STREAM_RESTORE ".RestoreEntry" + +#define DBUS_INTERFACE_REVISION 0 + +struct dbus_entry { + struct userdata *userdata; + + char *entry_name; + uint32_t index; + char *object_path; +}; + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INTERFACE_REVISION, + PROPERTY_HANDLER_ENTRIES, + PROPERTY_HANDLER_MAX +}; + +enum entry_property_handler_index { + ENTRY_PROPERTY_HANDLER_INDEX, + ENTRY_PROPERTY_HANDLER_NAME, + ENTRY_PROPERTY_HANDLER_DEVICE, + ENTRY_PROPERTY_HANDLER_VOLUME, + ENTRY_PROPERTY_HANDLER_IS_MUTED, + ENTRY_PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u", .get_cb = handle_get_interface_revision, .set_cb = NULL }, + [PROPERTY_HANDLER_ENTRIES] = { .property_name = "Entries", .type = "ao", .get_cb = handle_get_entries, .set_cb = NULL } +}; + +static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_MAX] = { + [ENTRY_PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_entry_get_index, .set_cb = NULL }, + [ENTRY_PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_entry_get_name, .set_cb = NULL }, + [ENTRY_PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "s", .get_cb = handle_entry_get_device, .set_cb = handle_entry_set_device }, + [ENTRY_PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "a(uu)", .get_cb = handle_entry_get_volume, .set_cb = handle_entry_set_volume }, + [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted } +}; + +enum method_handler_index { + METHOD_HANDLER_ADD_ENTRY, + METHOD_HANDLER_GET_ENTRY_BY_NAME, + METHOD_HANDLER_MAX +}; + +enum entry_method_handler_index { + ENTRY_METHOD_HANDLER_REMOVE, + ENTRY_METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, + { "device", "s", "in" }, + { "volume", "a(uu)", "in" }, + { "is_muted", "b", "in" }, + { "entry", "o", "out" } }; +static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_ADD_ENTRY] = { + .method_name = "AddEntry", + .arguments = add_entry_args, + .n_arguments = sizeof(add_entry_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_add_entry }, + [METHOD_HANDLER_GET_ENTRY_BY_NAME] = { + .method_name = "GetEntryByName", + .arguments = get_entry_by_name_args, + .n_arguments = sizeof(get_entry_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_entry_by_name } +}; + +static pa_dbus_method_handler entry_method_handlers[ENTRY_METHOD_HANDLER_MAX] = { + [ENTRY_METHOD_HANDLER_REMOVE] = { + .method_name = "Remove", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_entry_remove } +}; + +enum signal_index { + SIGNAL_NEW_ENTRY, + SIGNAL_ENTRY_REMOVED, + SIGNAL_MAX +}; + +enum entry_signal_index { + ENTRY_SIGNAL_DEVICE_UPDATED, + ENTRY_SIGNAL_VOLUME_UPDATED, + ENTRY_SIGNAL_MUTE_UPDATED, + ENTRY_SIGNAL_MAX +}; + +static pa_dbus_arg_info new_entry_args[] = { { "entry", "o", NULL } }; +static pa_dbus_arg_info entry_removed_args[] = { { "entry", "o", NULL } }; + +static pa_dbus_arg_info entry_device_updated_args[] = { { "device", "s", NULL } }; +static pa_dbus_arg_info entry_volume_updated_args[] = { { "volume", "a(uu)", NULL } }; +static pa_dbus_arg_info entry_mute_updated_args[] = { { "muted", "b", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_NEW_ENTRY] = { .name = "NewEntry", .arguments = new_entry_args, .n_arguments = 1 }, + [SIGNAL_ENTRY_REMOVED] = { .name = "EntryRemoved", .arguments = entry_removed_args, .n_arguments = 1 } +}; + +static pa_dbus_signal_info entry_signals[ENTRY_SIGNAL_MAX] = { + [ENTRY_SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = entry_device_updated_args, .n_arguments = 1 }, + [ENTRY_SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = entry_volume_updated_args, .n_arguments = 1 }, + [ENTRY_SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = entry_mute_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info stream_restore_interface_info = { + .name = INTERFACE_STREAM_RESTORE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static pa_dbus_interface_info entry_interface_info = { + .name = INTERFACE_ENTRY, + .method_handlers = entry_method_handlers, + .n_method_handlers = ENTRY_METHOD_HANDLER_MAX, + .property_handlers = entry_property_handlers, + .n_property_handlers = ENTRY_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_entry_get_all, + .signals = entry_signals, + .n_signals = ENTRY_SIGNAL_MAX +}; + +static struct dbus_entry *dbus_entry_new(struct userdata *u, const char *entry_name) { + struct dbus_entry *de; + + pa_assert(u); + pa_assert(entry_name); + pa_assert(*entry_name); + + de = pa_xnew(struct dbus_entry, 1); + de->userdata = u; + de->entry_name = pa_xstrdup(entry_name); + de->index = u->next_index++; + de->object_path = pa_sprintf_malloc("%s/%s%u", OBJECT_PATH, ENTRY_OBJECT_NAME, de->index); + + pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, de->object_path, &entry_interface_info, u) >= 0); + + return de; +} + +static void dbus_entry_free(struct dbus_entry *de) { + pa_assert(de); + + pa_assert_se(pa_dbus_protocol_remove_interface(de->userdata->dbus_protocol, de->object_path, entry_interface_info.name) >= 0); + + pa_xfree(de->entry_name); + pa_xfree(de->object_path); +} + +/* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are + * are a channel position and a volume value, respectively. The result is + * stored in the map and vol arguments. If the volume can't be read from the + * iterator, an error reply is sent and a negative number is returned. In case + * of a failure we make no guarantees about the state of map and vol. In case + * of an empty array the channels field of both map and vol are set to 0. */ +static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) { + DBusMessageIter array_iter; + DBusMessageIter struct_iter; + int arg_type; + + pa_assert(conn); + pa_assert(msg); + pa_assert(iter); + pa_assert(map); + pa_assert(vol); + + pa_channel_map_init(map); + pa_cvolume_init(vol); + + map->channels = 0; + vol->channels = 0; + + arg_type = dbus_message_iter_get_arg_type(iter); + if (arg_type != DBUS_TYPE_ARRAY) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); + return -1; + } + + arg_type = dbus_message_iter_get_element_type(iter); + if (arg_type != DBUS_TYPE_STRUCT) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_recurse(iter, &array_iter); + + while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) { + dbus_uint32_t chan_pos; + dbus_uint32_t chan_vol; + + dbus_message_iter_recurse(&array_iter, &struct_iter); + + arg_type = dbus_message_iter_get_arg_type(&struct_iter); + if (arg_type != DBUS_TYPE_UINT32) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_get_basic(&struct_iter, &chan_pos); + dbus_message_iter_next(&struct_iter); + + if (chan_pos >= PA_CHANNEL_POSITION_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos); + return -1; + } + + arg_type = dbus_message_iter_get_arg_type(&struct_iter); + if (arg_type != DBUS_TYPE_UINT32) { + if (arg_type == DBUS_TYPE_INVALID) + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing."); + else + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); + return -1; + } + + dbus_message_iter_get_basic(&struct_iter, &chan_vol); + + if (chan_vol > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", chan_vol); + return -1; + } + + if (map->channels < PA_CHANNELS_MAX) { + map->map[map->channels] = chan_pos; + vol->values[map->channels] = chan_vol; + } + ++map->channels; + ++vol->channels; + + dbus_message_iter_next(&array_iter); + } + + if (map->channels > PA_CHANNELS_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum is %u.", map->channels, PA_CHANNELS_MAX); + return -1; + } + + dbus_message_iter_next(iter); + + return 0; +} + +static void append_volume(DBusMessageIter *iter, struct entry *e) { + DBusMessageIter array_iter; + DBusMessageIter struct_iter; + unsigned i; + + pa_assert(iter); + pa_assert(e); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(uu)", &array_iter)); + + if (!e->volume_valid) { + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); + return; + } + + for (i = 0; i < e->channel_map.channels; ++i) { + pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->channel_map.map[i])); + pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->volume.values[i])); + + pa_assert_se(dbus_message_iter_close_container(&array_iter, &struct_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(iter, &array_iter)); +} + +static void append_volume_variant(DBusMessageIter *iter, struct entry *e) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(e); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(uu)", &variant_iter)); + + append_volume(&variant_iter, e); + + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +static void send_new_entry_signal(struct dbus_entry *entry) { + DBusMessage *signal; + + pa_assert(entry); + + pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_NEW_ENTRY].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_entry_removed_signal(struct dbus_entry *entry) { + DBusMessage *signal; + + pa_assert(entry); + + pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_ENTRY_REMOVED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_device_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + const char *device; + + pa_assert(de); + pa_assert(e); + + device = e->device_valid ? e->device : ""; + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_volume_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + DBusMessageIter msg_iter; + + pa_assert(de); + pa_assert(e); + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_VOLUME_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + append_volume(&msg_iter, e); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void send_mute_updated_signal(struct dbus_entry *de, struct entry *e) { + DBusMessage *signal; + dbus_bool_t muted; + + pa_assert(de); + pa_assert(e); + + pa_assert(e->muted_valid); + + muted = e->muted; + + pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &muted, DBUS_TYPE_INVALID)); + pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal); + dbus_message_unref(signal); +} + +static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) { + dbus_uint32_t interface_revision = DBUS_INTERFACE_REVISION; + + pa_assert(conn); + pa_assert(msg); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_entries(struct userdata *u, unsigned *n) { + const char **entries; + unsigned i = 0; + void *state = NULL; + struct dbus_entry *de; + + pa_assert(u); + pa_assert(n); + + *n = pa_hashmap_size(u->dbus_entries); + + if (*n == 0) + return NULL; + + entries = pa_xnew(const char *, *n); + + while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + entries[i] = de->object_path; + ++i; + } + + return entries; +} + +static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + const char **entries; + unsigned n; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + entries = get_entries(u, &n); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, entries, n); + + pa_xfree(entries); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t interface_revision; + const char **entries; + unsigned n_entries; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + interface_revision = DBUS_INTERFACE_REVISION; + entries = get_entries(u, &n_entries); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ENTRIES].property_name, DBUS_TYPE_OBJECT_PATH, entries, n_entries); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(entries); +} + +static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + DBusMessageIter msg_iter; + const char *name; + const char *device; + pa_channel_map map; + pa_cvolume vol; + dbus_bool_t muted; + dbus_bool_t apply_immediately; + pa_datum key; + pa_datum value; + struct dbus_entry *dbus_entry; + struct entry *e; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0) + return; + + if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0) + return; + + if (!*name) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name."); + return; + } + + if ((dbus_entry = pa_hashmap_get(u->dbus_entries, name))) { + pa_bool_t mute_updated = FALSE; + pa_bool_t volume_updated = FALSE; + pa_bool_t device_updated = FALSE; + + pa_assert_se(e = read_entry(u, name)); + mute_updated = e->muted != muted; + e->muted = muted; + e->muted_valid = TRUE; + + volume_updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol); + e->volume = vol; + e->channel_map = map; + e->volume_valid = !!map.channels; + + device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device); + pa_strlcpy(e->device, device, sizeof(e->device)); + e->device_valid = !!device[0]; + + if (mute_updated) + send_mute_updated_signal(dbus_entry, e); + if (volume_updated) + send_volume_updated_signal(dbus_entry, e); + if (device_updated) + send_device_updated_signal(dbus_entry, e); + + } else { + dbus_entry = dbus_entry_new(u, name); + pa_assert(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) >= 0); + + e->muted_valid = TRUE; + e->volume_valid = !!map.channels; + e->device_valid = !!device[0]; + e->muted = muted; + e->volume = vol; + e->channel_map = map; + pa_strlcpy(e->device, device, sizeof(e->device)); + + send_new_entry_signal(dbus_entry); + } + + key.data = (char *) name; + key.size = strlen(name); + + value.data = e; + value.size = sizeof(struct entry); + + pa_assert_se(pa_database_set(u->database, &key, &value, TRUE) == 0); + if (apply_immediately) + apply_entry(u, name, e); + + trigger_save(u); + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct userdata *u = userdata; + const char *name; + struct dbus_entry *de; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(de = pa_hashmap_get(u->dbus_entries, name))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry."); + return; + } + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &de->object_path); +} + +static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &de->index); +} + +static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &de->entry_name); +} + +static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + const char *device; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + device = e->device_valid ? e->device : ""; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device); + + pa_xfree(e); +} + +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + const char *device; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device); + + if (updated) { + pa_datum key; + pa_datum value; + + pa_strlcpy(e->device, device, sizeof(e->device)); + e->device_valid = !!device[0]; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_device_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + DBusMessage *reply; + DBusMessageIter msg_iter; + struct entry *e; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + pa_assert_se(reply = dbus_message_new_method_return(msg)); + + dbus_message_iter_init_append(reply, &msg_iter); + append_volume_variant(&msg_iter, e); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + pa_xfree(e); +} + +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + DBusMessageIter msg_iter; + pa_channel_map map; + pa_cvolume vol; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + /* Skip the interface and property name arguments. */ + if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol); + + if (updated) { + pa_datum key; + pa_datum value; + + e->volume = vol; + e->channel_map = map; + e->volume_valid = !!map.channels; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_volume_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + dbus_bool_t muted; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + muted = e->muted_valid ? e->muted : FALSE; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted); + + pa_xfree(e); +} + +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + pa_bool_t muted; + struct entry *e; + pa_bool_t updated; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0) + return; + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + updated = !e->muted_valid || e->muted != muted; + + if (updated) { + pa_datum key; + pa_datum value; + + e->muted = muted; + e->muted_valid = TRUE; + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + value.data = e; + value.size = sizeof(struct entry); + pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0); + + send_mute_updated_signal(de, e); + trigger_save(de->userdata); + } + + pa_dbus_send_empty_reply(conn, msg); + + pa_xfree(e); +} + +static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + struct entry *e; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + const char *device; + dbus_bool_t muted; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + pa_assert_se(e = read_entry(de->userdata, de->entry_name)); + + device = e->device_valid ? e->device : ""; + muted = e->muted_valid ? e->muted : FALSE; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &de->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &de->entry_name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_STRING, &device); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &entry_property_handlers[ENTRY_PROPERTY_HANDLER_VOLUME].property_name)); + append_volume_variant(&dict_entry_iter, e); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(e); +} + +static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) { + struct dbus_entry *de = userdata; + pa_datum key; + + pa_assert(conn); + pa_assert(msg); + pa_assert(de); + + key.data = de->entry_name; + key.size = strlen(de->entry_name); + + pa_assert_se(pa_database_unset(de->userdata->database, &key) == 0); + + send_entry_removed_signal(de); + trigger_save(de->userdata); + + pa_assert_se(pa_hashmap_remove(de->userdata->dbus_entries, de->entry_name)); + dbus_entry_free(de); + + pa_dbus_send_empty_reply(conn, msg); +} + +#endif /* HAVE_DBUS */ + static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; @@ -162,7 +1051,7 @@ static char *get_name(pa_proplist *p, const char *prefix) { return t; } -static struct entry* read_entry(struct userdata *u, const char *name) { +static struct entry *read_entry(struct userdata *u, const char *name) { pa_datum key, data; struct entry *e; @@ -270,6 +1159,17 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 char *name; pa_datum key, data; + /* These are only used when D-Bus is enabled, but in order to reduce ifdef + * clutter these are defined here unconditionally. */ + pa_bool_t created_new_entry = TRUE; + pa_bool_t device_updated = FALSE; + pa_bool_t volume_updated = FALSE; + pa_bool_t mute_updated = FALSE; + +#ifdef HAVE_DBUS + struct dbus_entry *de = NULL; +#endif + pa_assert(c); pa_assert(u); @@ -291,23 +1191,34 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (!(name = get_name(sink_input->proplist, "sink-input"))) return; - if ((old = read_entry(u, name))) + if ((old = read_entry(u, name))) { entry = *old; + created_new_entry = FALSE; + } if (sink_input->save_volume) { entry.channel_map = sink_input->channel_map; pa_sink_input_get_volume(sink_input, &entry.volume, FALSE); entry.volume_valid = TRUE; + + volume_updated = !created_new_entry + && (!old->volume_valid + || !pa_channel_map_equal(&entry.channel_map, &old->channel_map) + || !pa_cvolume_equal(&entry.volume, &old->volume)); } if (sink_input->save_muted) { entry.muted = pa_sink_input_get_mute(sink_input); entry.muted_valid = TRUE; + + mute_updated = !created_new_entry && (!old->muted_valid || entry.muted != old->muted); } if (sink_input->save_sink) { pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device)); entry.device_valid = TRUE; + + device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device)); } } else { @@ -321,12 +1232,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (!(name = get_name(source_output->proplist, "source-output"))) return; - if ((old = read_entry(u, name))) + if ((old = read_entry(u, name))) { entry = *old; + created_new_entry = FALSE; + } if (source_output->save_source) { pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device)); entry.device_valid = source_output->save_source; + + device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device)); } } @@ -351,6 +1266,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 pa_database_set(u->database, &key, &data, TRUE); +#ifdef HAVE_DBUS + if (created_new_entry) { + de = dbus_entry_new(u, name); + pa_hashmap_put(u->dbus_entries, de->entry_name, de); + send_new_entry_signal(de); + } else { + pa_assert((de = pa_hashmap_get(u->dbus_entries, name))); + + if (device_updated) + send_device_updated_signal(de, &entry); + if (volume_updated) + send_volume_updated_signal(de, &entry); + if (mute_updated) + send_mute_updated_signal(de, &entry); + } +#endif + pa_xfree(name); trigger_save(u); @@ -810,14 +1742,27 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio mode != PA_UPDATE_SET) goto fail; - if (mode == PA_UPDATE_SET) + if (mode == PA_UPDATE_SET) { +#ifdef HAVE_DBUS + struct dbus_entry *de; + void *state = NULL; + + while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + send_entry_removed_signal(de); + dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name)); + } +#endif pa_database_clear(u->database); + } while (!pa_tagstruct_eof(t)) { const char *name, *device; pa_bool_t muted; struct entry entry; pa_datum key, data; +#ifdef HAVE_DBUS + struct entry *old; +#endif pa_zero(entry); entry.version = ENTRY_VERSION; @@ -849,15 +1794,50 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio !pa_namereg_is_valid_name(entry.device)) goto fail; +#ifdef HAVE_DBUS + old = read_entry(u, name); +#endif + key.data = (char*) name; key.size = strlen(name); data.data = &entry; data.size = sizeof(entry); - if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) + if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) { +#ifdef HAVE_DBUS + struct dbus_entry *de; + + if (old) { + pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name))); + + if ((old->device_valid != entry.device_valid) + || (entry.device_valid && !pa_streq(entry.device, old->device))) + send_device_updated_signal(de, &entry); + + if ((old->volume_valid != entry.volume_valid) + || (entry.volume_valid + && (!pa_cvolume_equal(&entry.volume, &old->volume) || !pa_channel_map_equal(&entry.channel_map, &old->channel_map)))) + send_volume_updated_signal(de, &entry); + + if (!old->muted_valid || (entry.muted != old->muted)) + send_mute_updated_signal(de, &entry); + + } else { + de = dbus_entry_new(u, name); + pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de)); + send_new_entry_signal(de); + } +#endif + if (apply_immediately) apply_entry(u, name, &entry); + } + +#ifdef HAVE_DBUS + if (old) + pa_xfree(old); +#endif } trigger_save(u); @@ -870,10 +1850,20 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio while (!pa_tagstruct_eof(t)) { const char *name; pa_datum key; +#ifdef HAVE_DBUS + struct dbus_entry *de; +#endif if (pa_tagstruct_gets(t, &name) < 0) goto fail; +#ifdef HAVE_DBUS + if ((de = pa_hashmap_get(u->dbus_entries, name))) { + send_entry_removed_signal(de); + dbus_entry_free(pa_hashmap_remove(u->dbus_entries, name)); + } +#endif + key.data = (char*) name; key.size = strlen(name); @@ -932,6 +1922,10 @@ int pa__init(pa_module*m) { pa_source_output *so; uint32_t idx; pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE; +#ifdef HAVE_DBUS + pa_datum key; + pa_bool_t done; +#endif pa_assert(m); @@ -1002,6 +1996,34 @@ int pa__init(pa_module*m) { pa_log_info("Sucessfully opened database file '%s'.", fname); pa_xfree(fname); +#ifdef HAVE_DBUS + u->dbus_protocol = pa_dbus_protocol_get(u->core); + u->dbus_entries = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, OBJECT_PATH, &stream_restore_interface_info, u) >= 0); + pa_assert_se(pa_dbus_protocol_register_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0); + + /* Create the initial dbus entries. */ + done = !pa_database_first(u->database, &key, NULL); + while (!done) { + pa_datum next_key; + char *name; + struct dbus_entry *de; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + name = pa_xstrndup(key.data, key.size); + pa_datum_free(&key); + + de = dbus_entry_new(u, name); + pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) >= 0); + + pa_xfree(name); + + key = next_key; + } +#endif + PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); @@ -1020,6 +2042,16 @@ fail: return -1; } +#ifdef HAVE_DBUS +static void free_dbus_entry_cb(void *p, void *userdata) { + struct dbus_entry *de = p; + + pa_assert(de); + + dbus_entry_free(de); +} +#endif + void pa__done(pa_module*m) { struct userdata* u; @@ -1028,6 +2060,19 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; +#ifdef HAVE_DBUS + if (u->dbus_protocol) { + pa_assert(u->dbus_entries); + + pa_assert_se(pa_dbus_protocol_unregister_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0); + pa_assert_se(pa_dbus_protocol_remove_interface(u->dbus_protocol, OBJECT_PATH, stream_restore_interface_info.name) >= 0); + + pa_hashmap_free(u->dbus_entries, free_dbus_entry_cb, NULL); + + pa_dbus_protocol_unref(u->dbus_protocol); + } +#endif + if (u->subscription) pa_subscription_free(u->subscription); -- cgit From 8966c61d3343e13502cfa210bb7123b7d7e7b27e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:50:18 +0300 Subject: dbusiface-core: Make the interface string a public constant. --- src/modules/dbus/iface-core.c | 49 +++++++++++++++++++++---------------------- src/modules/dbus/iface-core.h | 2 ++ 2 files changed, 26 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 69c1bd25..695e4a3c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -48,7 +48,6 @@ #include "iface-core.h" #define OBJECT_PATH "/org/pulseaudio/core1" -#define INTERFACE_CORE "org.PulseAudio.Core1" #define INTERFACE_REVISION 0 @@ -312,7 +311,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { }; static pa_dbus_interface_info core_interface_info = { - .name = INTERFACE_CORE, + .name = PA_DBUSIFACE_CORE_INTERFACE, .method_handlers = method_handlers, .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, @@ -1612,7 +1611,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1626,7 +1625,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1644,7 +1643,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1652,7 +1651,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1671,7 +1670,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1683,7 +1682,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1696,7 +1695,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1715,7 +1714,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1727,7 +1726,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1740,7 +1739,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1756,7 +1755,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1764,7 +1763,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1780,7 +1779,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1788,7 +1787,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1804,7 +1803,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1812,7 +1811,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1828,7 +1827,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1836,7 +1835,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1852,7 +1851,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1860,7 +1859,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1882,7 +1881,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1899,7 +1898,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -2028,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE); + pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 964a37bd..6c5191b9 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -30,6 +30,8 @@ #include +#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1" + typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); -- cgit From b1578e27b62e7332111bb706f79858b0866029e3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:55:10 +0300 Subject: dbus-protocol, dbusiface-core: Take a reference when storing the core pointer. --- src/modules/dbus/iface-core.c | 3 ++- src/pulsecore/protocol-dbus.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 695e4a3c..ca9ba582 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1923,7 +1923,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_assert(core); c = pa_xnew(pa_dbusiface_core, 1); - c->core = core; + c->core = pa_core_ref(core); c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c); c->dbus_protocol = pa_dbus_protocol_get(core); c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); @@ -2044,6 +2044,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hook_slot_free(c->extension_unregistered_slot); pa_dbus_protocol_unref(c->dbus_protocol); + pa_core_unref(c->core); pa_xfree(c); } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 475b952f..8fc08032 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -117,7 +117,7 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) { p = pa_xnew(pa_dbus_protocol, 1); PA_REFCNT_INIT(p); - p->core = c; + p->core = pa_core_ref(c); p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); @@ -170,6 +170,8 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) { pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0); + pa_core_unref(p->core); + pa_xfree(p); } -- cgit From 9eeb8eb2729cdc77fa98c704eb8fc7fcca15336b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 17:57:44 +0300 Subject: dbus-protocol: Make debug logging saner. --- src/pulsecore/protocol-dbus.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 8fc08032..c82bb5fa 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -308,7 +308,6 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, return FOUND_METHOD; } - pa_log("find_handler_by_method() failed."); return NO_SUCH_METHOD; } @@ -331,7 +330,6 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) return FOUND_GET_ALL; else { - pa_log("GetAll message has unknown interface: %s", interface); return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ } } else { @@ -418,7 +416,10 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message)); + pa_log_debug("Received message: destination = %s, interface = %s, member = %s", + dbus_message_get_path(message), + dbus_message_get_interface(message), + dbus_message_get_member(message)); pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); @@ -428,8 +429,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); pa_assert_se(dbus_connection_send(connection, reply, NULL)); - pa_log_debug("%s.Introspect handled.", obj_entry->path); - goto finish; } @@ -633,7 +632,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, if (obj_entry_created) register_object(p, obj_entry); - pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no"); + pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path); return 0; -- cgit From 0fc055226c60fa7429abf80e38f40a565f9e7922 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 18:00:08 +0300 Subject: dbus-protocol: Remove erroneous protocol object unref. --- src/pulsecore/protocol-dbus.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index c82bb5fa..06098673 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -639,7 +639,6 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, fail: if (obj_entry_created) { pa_hashmap_remove(p->objects, path); - pa_dbus_protocol_unref(p); pa_xfree(obj_entry); } -- cgit From 44770c59e92f49288341afe8646d8bc39eb9f589 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Tue, 4 Aug 2009 18:01:26 +0300 Subject: dbusiface-memstats: Implement the Memstats D-Bus interface. --- src/Makefile.am | 1 + src/modules/dbus/iface-core.c | 5 + src/modules/dbus/iface-memstats.c | 231 ++++++++++++++++++++++++++++++++++++++ src/modules/dbus/iface-memstats.h | 44 ++++++++ 4 files changed, 281 insertions(+) create mode 100644 src/modules/dbus/iface-memstats.c create mode 100644 src/modules/dbus/iface-memstats.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 07f81a6b..e10b4ab6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1281,6 +1281,7 @@ module_dbus_protocol_la_SOURCES = \ modules/dbus/iface-client.c modules/dbus/iface-client.h \ modules/dbus/iface-core.c modules/dbus/iface-core.h \ modules/dbus/iface-device.c modules/dbus/iface-device.h \ + modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \ modules/dbus/iface-module.c modules/dbus/iface-module.h \ modules/dbus/iface-sample.c modules/dbus/iface-sample.h \ modules/dbus/iface-stream.c modules/dbus/iface-stream.h \ diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ca9ba582..ad729f93 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -41,6 +41,7 @@ #include "iface-card.h" #include "iface-client.h" #include "iface-device.h" +#include "iface-memstats.h" #include "iface-module.h" #include "iface-sample.h" #include "iface-stream.h" @@ -114,6 +115,8 @@ struct pa_dbusiface_core { pa_hook_slot *extension_registered_slot; pa_hook_slot *extension_unregistered_slot; + + pa_dbusiface_memstats *memstats; }; enum property_handler_index { @@ -1940,6 +1943,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->fallback_source = pa_namereg_get_default_source(core); c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); + c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); @@ -2042,6 +2046,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hashmap_free(c->clients, free_client_cb, NULL); pa_hook_slot_free(c->extension_registered_slot); pa_hook_slot_free(c->extension_unregistered_slot); + pa_dbusiface_memstats_free(c->memstats); pa_dbus_protocol_unref(c->dbus_protocol); pa_core_unref(c->core); diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c new file mode 100644 index 00000000..d3412a25 --- /dev/null +++ b/src/modules/dbus/iface-memstats.c @@ -0,0 +1,231 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include + +#include "iface-memstats.h" + +#define OBJECT_NAME "memstats" + +static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_memstats { + pa_core *core; + char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_CURRENT_MEMBLOCKS, + PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE, + PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS, + PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE, + PROPERTY_HANDLER_SAMPLE_CACHE_SIZE, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_CURRENT_MEMBLOCKS] = { .property_name = "CurrentMemblocks", .type = "u", .get_cb = handle_get_current_memblocks, .set_cb = NULL }, + [PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE] = { .property_name = "CurrentMemblocksSize", .type = "u", .get_cb = handle_get_current_memblocks_size, .set_cb = NULL }, + [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS] = { .property_name = "AccumulatedMemblocks", .type = "u", .get_cb = handle_get_accumulated_memblocks, .set_cb = NULL }, + [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE] = { .property_name = "AccumulatedMemblocksSize", .type = "u", .get_cb = handle_get_accumulated_memblocks_size, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_CACHE_SIZE] = { .property_name = "SampleCacheSize", .type = "u", .get_cb = handle_get_sample_cache_size, .set_cb = NULL } +}; + +static pa_dbus_interface_info memstats_interface_info = { + .name = PA_DBUSIFACE_MEMSTATS_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks = pa_atomic_load(&stat->n_allocated); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, ¤t_memblocks); +} + +static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks_size = pa_atomic_load(&stat->allocated_size); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, ¤t_memblocks_size); +} + +static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t accumulated_memblocks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + accumulated_memblocks = pa_atomic_load(&stat->n_accumulated); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks); +} + +static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t accumulated_memblocks_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks_size); +} + +static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + dbus_uint32_t sample_cache_size; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + sample_cache_size = pa_scache_total_size(m->core); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_cache_size); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_memstats *m = userdata; + const pa_mempool_stat *stat; + dbus_uint32_t current_memblocks; + dbus_uint32_t current_memblocks_size; + dbus_uint32_t accumulated_memblocks; + dbus_uint32_t accumulated_memblocks_size; + dbus_uint32_t sample_cache_size; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + stat = pa_mempool_get_stat(m->core->mempool); + + current_memblocks = pa_atomic_load(&stat->n_allocated); + current_memblocks_size = pa_atomic_load(&stat->allocated_size); + accumulated_memblocks = pa_atomic_load(&stat->n_accumulated); + accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size); + sample_cache_size = pa_scache_total_size(m->core); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, ¤t_memblocks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, ¤t_memblocks_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_CACHE_SIZE].property_name, DBUS_TYPE_UINT32, &sample_cache_size); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) { + pa_dbusiface_memstats *m; + + pa_assert(core); + pa_assert(path_prefix); + + m = pa_xnew(pa_dbusiface_memstats, 1); + m->core = pa_core_ref(core); + m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME); + m->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0); + + return m; +} + +void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m) { + pa_assert(m); + + pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, memstats_interface_info.name) >= 0); + + pa_xfree(m->path); + + pa_dbus_protocol_unref(m->dbus_protocol); + pa_core_unref(m->core); + + pa_xfree(m); +} + +const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) { + pa_assert(m); + + return m->path; +} diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h new file mode 100644 index 00000000..d7773ee0 --- /dev/null +++ b/src/modules/dbus/iface-memstats.h @@ -0,0 +1,44 @@ +#ifndef foodbusifacememstatshfoo +#define foodbusifacememstatshfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.Memstats. + * + * See http://pulseaudio.org/wiki/DBusInterface for the Memstats interface + * documentation. + */ + +#include + +#include "iface-core.h" + +#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats" + +typedef struct pa_dbusiface_memstats pa_dbusiface_memstats; + +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix); +void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m); + +const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m); + +#endif -- cgit From 1457df40eee692834d1c5faf95ca0057d74f86d1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 07:59:06 +0300 Subject: proplist: New function: pa_proplist_equal() --- src/map-file | 1 + src/pulse/proplist.c | 26 ++++++++++++++++++++++++++ src/pulse/proplist.h | 3 +++ 3 files changed, 30 insertions(+) (limited to 'src') diff --git a/src/map-file b/src/map-file index a1d0a061..3db3a2d7 100644 --- a/src/map-file +++ b/src/map-file @@ -180,6 +180,7 @@ pa_path_get_filename; pa_proplist_clear; pa_proplist_contains; pa_proplist_copy; +pa_proplist_equal; pa_proplist_free; pa_proplist_from_string; pa_proplist_get; diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index c904f533..4f0d6a6d 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -680,3 +680,29 @@ int pa_proplist_isempty(pa_proplist *p) { return pa_hashmap_isempty(MAKE_HASHMAP(p)); } + +int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { + const void *key = NULL; + struct property *a_prop = NULL; + struct property *b_prop = NULL; + void *state = NULL; + + pa_assert(a); + pa_assert(b); + + if (pa_proplist_size(a) != pa_proplist_size(b)) + return 0; + + while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP(a), &state, &key))) { + if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP(b), key))) + return 0; + + if (a_prop->nbytes != b_prop->nbytes) + return 0; + + if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0) + return 0; + } + + return 1; +} diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index bc4dbd8a..a585944a 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -354,6 +354,9 @@ unsigned pa_proplist_size(pa_proplist *t); /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */ int pa_proplist_isempty(pa_proplist *t); +/** Return non-zero when a and b have the same keys and values. */ +int pa_proplist_equal(pa_proplist *a, pa_proplist *b); + PA_C_DECL_END #endif -- cgit From fcf68752e687123a171b1144f78f8eba1625a204 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 08:37:33 +0300 Subject: dbus: Three entangled changes: * Make the dbus object constructors take a pa_dbusiface_core pointer as an argument. Remove the path_prefix argument. * Expose the core object path as a constant in protocol-dbus.h. * Move the core interface name constant from iface-core.h to protocol-dbus.h. --- src/modules/dbus/iface-card.c | 15 ++++--- src/modules/dbus/iface-card.h | 6 ++- src/modules/dbus/iface-client.c | 7 +-- src/modules/dbus/iface-client.h | 4 +- src/modules/dbus/iface-core.c | 92 +++++++++++++++++++-------------------- src/modules/dbus/iface-core.h | 2 - src/modules/dbus/iface-device.c | 13 +++--- src/modules/dbus/iface-device.h | 6 ++- src/modules/dbus/iface-memstats.c | 6 +-- src/modules/dbus/iface-memstats.h | 5 ++- src/modules/dbus/iface-module.c | 7 +-- src/modules/dbus/iface-module.h | 4 +- src/modules/dbus/iface-sample.c | 7 +-- src/modules/dbus/iface-sample.h | 4 +- src/modules/dbus/iface-stream.c | 13 +++--- src/modules/dbus/iface-stream.h | 6 ++- src/pulsecore/protocol-dbus.h | 7 ++- 17 files changed, 113 insertions(+), 91 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index db6aa26f..e203c395 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -24,25 +24,30 @@ #endif #include +#include #include "iface-card.h" #define OBJECT_NAME "card" struct pa_dbusiface_card { + pa_dbusiface_core *core; + pa_card *card; char *path; }; -pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) { - pa_dbusiface_card *c; +pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) { + pa_dbusiface_card *c = NULL; + + pa_assert(core); pa_assert(card); - pa_assert(path_prefix); - c = pa_xnew(pa_dbusiface_card, 1); + c = pa_xnew0(pa_dbusiface_card, 1); + c->core = core; c->card = card; - c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index); + c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index); return c; } diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h index 54db610c..57ad4e31 100644 --- a/src/modules/dbus/iface-card.h +++ b/src/modules/dbus/iface-card.h @@ -30,9 +30,13 @@ #include +#include "iface-core.h" + +#define PA_DBUSIFACE_CARD_INTERFACE PA_DBUS_CORE_INTERFACE ".Card" + typedef struct pa_dbusiface_card pa_dbusiface_card; -pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix); +pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card); void pa_dbusiface_card_free(pa_dbusiface_card *c); const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c); diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index cfa36d0c..d9c8653f 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-client.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_client { char *path; }; -pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) { +pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { pa_dbusiface_client *c; + pa_assert(core); pa_assert(client); - pa_assert(path_prefix); c = pa_xnew(pa_dbusiface_client, 1); c->client = client; - c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index); + c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); return c; } diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h index 62cca7f8..ff906256 100644 --- a/src/modules/dbus/iface-client.h +++ b/src/modules/dbus/iface-client.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_client pa_dbusiface_client; -pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix); +pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client); void pa_dbusiface_client_free(pa_dbusiface_client *c); const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c); diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ad729f93..e40cb979 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -48,12 +48,8 @@ #include "iface-core.h" -#define OBJECT_PATH "/org/pulseaudio/core1" - #define INTERFACE_REVISION 0 - - static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -314,7 +310,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { }; static pa_dbus_interface_info core_interface_info = { - .name = PA_DBUSIFACE_CORE_INTERFACE, + .name = PA_DBUS_CORE_INTERFACE, .method_handlers = method_handlers, .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, @@ -1388,7 +1384,7 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u sample->volume_is_set = FALSE; } - dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH); + dbus_sample = pa_dbusiface_sample_new(c, sample); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample); object_path = pa_dbusiface_sample_get_path(dbus_sample); @@ -1510,7 +1506,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use goto finish; } - dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH); + dbus_module = pa_dbusiface_module_new(c, module); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); object_path = pa_dbusiface_module_get_path(dbus_module); @@ -1614,7 +1610,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1628,7 +1624,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1640,13 +1636,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { - card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH); + card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx)); pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); } object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1654,7 +1650,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1666,14 +1662,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1685,7 +1681,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1698,7 +1694,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1710,14 +1706,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_source *source = pa_idxset_get_by_index(core->sources, idx); if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1729,7 +1725,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1742,7 +1738,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1752,13 +1748,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH); + stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx)); pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); } object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1766,7 +1762,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1776,13 +1772,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH); + stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx)); pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); } object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1790,7 +1786,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1800,13 +1796,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { - sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH); + sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx)); pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); } object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1814,7 +1810,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1824,13 +1820,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH); + module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx)); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); } object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1838,7 +1834,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1848,13 +1844,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { - client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH); + client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx)); pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); } object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1862,7 +1858,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1884,7 +1880,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1901,7 +1897,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1943,39 +1939,39 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->fallback_source = pa_namereg_get_default_source(core); c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); - c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH); + c->memstats = pa_dbusiface_memstats_new(c, core); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH)); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { - device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH); + device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { - device = pa_dbusiface_device_new_source(source, OBJECT_PATH); + device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH)); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input)); for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH)); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output)); for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) - pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH)); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH)); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH)); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); - pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c); + pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); return c; } @@ -2031,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name); + pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 6c5191b9..964a37bd 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -30,8 +30,6 @@ #include -#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1" - typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 3b3795eb..1a64f43c 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-device.h" @@ -44,30 +45,30 @@ struct pa_dbusiface_device { char *path; }; -pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) { +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) { pa_dbusiface_device *d; + pa_assert(core); pa_assert(sink); - pa_assert(path_prefix); d = pa_xnew(pa_dbusiface_device, 1); d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; - d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index); + d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); return d; } -pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) { +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) { pa_dbusiface_device *d; + pa_assert(core); pa_assert(source); - pa_assert(path_prefix); d = pa_xnew(pa_dbusiface_device, 1); d->source = pa_source_ref(source); d->type = DEVICE_TYPE_SOURCE; - d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index); + d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); return d; } diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h index 81ad1d84..1e9af83a 100644 --- a/src/modules/dbus/iface-device.h +++ b/src/modules/dbus/iface-device.h @@ -32,10 +32,12 @@ #include #include +#include "iface-core.h" + typedef struct pa_dbusiface_device pa_dbusiface_device; -pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix); -pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix); +pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink); +pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source); void pa_dbusiface_device_free(pa_dbusiface_device *d); const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d); diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c index d3412a25..73a84be8 100644 --- a/src/modules/dbus/iface-memstats.c +++ b/src/modules/dbus/iface-memstats.c @@ -195,15 +195,15 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } -pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) { +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core) { pa_dbusiface_memstats *m; + pa_assert(dbus_core); pa_assert(core); - pa_assert(path_prefix); m = pa_xnew(pa_dbusiface_memstats, 1); m->core = pa_core_ref(core); - m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME); + m->path = pa_sprintf_malloc("%s/%s", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME); m->dbus_protocol = pa_dbus_protocol_get(core); pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0); diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h index d7773ee0..0820e8fe 100644 --- a/src/modules/dbus/iface-memstats.h +++ b/src/modules/dbus/iface-memstats.h @@ -29,14 +29,15 @@ */ #include +#include #include "iface-core.h" -#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats" +#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUS_CORE_INTERFACE ".Memstats" typedef struct pa_dbusiface_memstats pa_dbusiface_memstats; -pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix); +pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core); void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m); const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 1c95f9e6..788d104b 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-module.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_module { char *path; }; -pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) { +pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) { pa_dbusiface_module *m; + pa_assert(core); pa_assert(module); - pa_assert(path_prefix); m = pa_xnew(pa_dbusiface_module, 1); m->module = module; - m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index); + m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index); return m; } diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h index 7f683e6a..c4f29210 100644 --- a/src/modules/dbus/iface-module.h +++ b/src/modules/dbus/iface-module.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_module pa_dbusiface_module; -pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix); +pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module); void pa_dbusiface_module_free(pa_dbusiface_module *m); const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index b4a308a2..44cfb031 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-sample.h" @@ -34,15 +35,15 @@ struct pa_dbusiface_sample { char *path; }; -pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) { +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) { pa_dbusiface_sample *s; + pa_assert(core); pa_assert(sample); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_sample, 1); s->sample = sample; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index); return s; } diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h index 1b85404e..1b82648d 100644 --- a/src/modules/dbus/iface-sample.h +++ b/src/modules/dbus/iface-sample.h @@ -30,9 +30,11 @@ #include +#include "iface-core.h" + typedef struct pa_dbusiface_sample pa_dbusiface_sample; -pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix); +pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample); void pa_dbusiface_sample_free(pa_dbusiface_sample *c); const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c); diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 1d9ffee6..b5a17894 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -24,6 +24,7 @@ #endif #include +#include #include "iface-stream.h" @@ -44,30 +45,30 @@ struct pa_dbusiface_stream { char *path; }; -pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) { +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { pa_dbusiface_stream *s; + pa_assert(core); pa_assert(sink_input); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_stream, 1); s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); return s; } -pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) { +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) { pa_dbusiface_stream *s; + pa_assert(core); pa_assert(source_output); - pa_assert(path_prefix); s = pa_xnew(pa_dbusiface_stream, 1); s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; - s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index); + s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); return s; } diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h index cc2f3d61..b1b1854b 100644 --- a/src/modules/dbus/iface-stream.h +++ b/src/modules/dbus/iface-stream.h @@ -31,10 +31,12 @@ #include #include +#include "iface-core.h" + typedef struct pa_dbusiface_stream pa_dbusiface_stream; -pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix); -pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix); +pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input); +pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output); void pa_dbusiface_stream_free(pa_dbusiface_stream *s); const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s); diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index f2b1b50b..c6b630a1 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -32,8 +32,11 @@ #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME -#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError" -#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError" +#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1" +#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1" + +#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError" +#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError" /* Returns the default address of the server type in the escaped form. For * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the -- cgit From 06232e2965ee02d62ca566fcbf5e805c571b574a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:04:15 +0300 Subject: dbus: Take advantage of the PA_HASHMAP_FOREACH macro. --- src/modules/dbus/iface-core.c | 64 ++++++++++++++----------------------- src/modules/module-stream-restore.c | 8 ++--- src/pulsecore/protocol-dbus.c | 30 +++++++---------- 3 files changed, 38 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e40cb979..685ba63b 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -542,7 +542,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms /* The caller frees the array, but not the strings. */ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { const char **cards; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_card *card; @@ -556,10 +556,8 @@ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) { cards = pa_xnew(const char *, *n); - for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL)) - cards[i] = pa_dbusiface_card_get_path(card); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(card, c->cards, state) + cards[i++] = pa_dbusiface_card_get_path(card); return cards; } @@ -583,7 +581,7 @@ static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userd /* The caller frees the array, but not the strings. */ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { const char **sinks; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_device *sink; @@ -597,10 +595,8 @@ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) { sinks = pa_xnew(const char *, *n); - for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL)) - sinks[i] = pa_dbusiface_device_get_path(sink); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state) + sinks[i++] = pa_dbusiface_device_get_path(sink); return sinks; } @@ -671,7 +667,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi /* The caller frees the array, but not the strings. */ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { const char **sources; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_device *source; @@ -685,10 +681,8 @@ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) { sources = pa_xnew(const char *, *n); - for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL)) - sources[i] = pa_dbusiface_device_get_path(source); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(source, c->sources_by_index, state) + sources[i++] = pa_dbusiface_device_get_path(source); return sources; } @@ -759,7 +753,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v /* The caller frees the array, but not the strings. */ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { const char **streams; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_stream *stream; @@ -773,10 +767,8 @@ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) { streams = pa_xnew(const char *, *n); - for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL)) - streams[i] = pa_dbusiface_stream_get_path(stream); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(stream, c->playback_streams, state) + streams[i++] = pa_dbusiface_stream_get_path(stream); return streams; } @@ -800,7 +792,7 @@ static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, /* The caller frees the array, but not the strings. */ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { const char **streams; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_stream *stream; @@ -814,10 +806,8 @@ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) { streams = pa_xnew(const char *, *n); - for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL)) - streams[i] = pa_dbusiface_stream_get_path(stream); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(stream, c->record_streams, state) + streams[i++] = pa_dbusiface_stream_get_path(stream); return streams; } @@ -841,7 +831,7 @@ static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, vo /* The caller frees the array, but not the strings. */ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { const char **samples; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_sample *sample; @@ -855,10 +845,8 @@ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) { samples = pa_xnew(const char *, *n); - for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL)) - samples[i] = pa_dbusiface_sample_get_path(sample); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(sample, c->samples, state) + samples[i++] = pa_dbusiface_sample_get_path(sample); return samples; } @@ -882,7 +870,7 @@ static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *use /* The caller frees the array, but not the strings. */ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { const char **modules; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_module *module; @@ -896,10 +884,8 @@ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) { modules = pa_xnew(const char *, *n); - for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL)) - modules[i] = pa_dbusiface_module_get_path(module); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(module, c->modules, state) + modules[i++] = pa_dbusiface_module_get_path(module); return modules; } @@ -923,7 +909,7 @@ static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *use /* The caller frees the array, but not the strings. */ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { const char **clients; - unsigned i; + unsigned i = 0; void *state = NULL; pa_dbusiface_client *client; @@ -937,10 +923,8 @@ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) { clients = pa_xnew(const char *, *n); - for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL)) - clients[i] = pa_dbusiface_client_get_path(client); - - pa_assert(i == *n); + PA_HASHMAP_FOREACH(client, c->clients, state) + clients[i++] = pa_dbusiface_client_get_path(client); return clients; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index bccdf938..e9063031 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -553,10 +553,8 @@ static const char **get_entries(struct userdata *u, unsigned *n) { entries = pa_xnew(const char *, *n); - while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { - entries[i] = de->object_path; - ++i; - } + PA_HASHMAP_FOREACH(de, u->dbus_entries, state) + entries[i++] = de->object_path; return entries; } @@ -1747,7 +1745,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio struct dbus_entry *de; void *state = NULL; - while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) { + PA_HASHMAP_FOREACH(de, u->dbus_entries, state) { send_entry_removed_signal(de); dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name)); } diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 06098673..5cbd0d91 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -186,7 +186,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); pa_strbuf_puts(buf, "\n"); - while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) { + PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) { pa_dbus_method_handler *method_handler; pa_dbus_property_handler *property_handler; void *handlers_state = NULL; @@ -195,7 +195,7 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", iface_entry->name); - while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) { + PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) @@ -278,7 +278,7 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr pa_assert(iface_entry); pa_assert(property_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { if (dbus_message_has_member(msg, "Get")) return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; @@ -303,7 +303,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, pa_assert(iface_entry); pa_assert(method_handler); - while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) { + PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) return FOUND_METHOD; } @@ -497,7 +497,7 @@ static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p)); } @@ -652,7 +652,7 @@ static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entr pa_assert(p); pa_assert(obj_entry); - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path)); } @@ -742,7 +742,7 @@ static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p)); } @@ -777,7 +777,7 @@ static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) { pa_assert(p); pa_assert(conn); - while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL))) + PA_HASHMAP_FOREACH(obj_entry, p->objects, state) pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path)); } @@ -904,13 +904,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); - /* XXX: We have to do some linear searching to find connections that want - * to receive the signal. This shouldn't be very significant performance - * problem, and adding an (object path, signal name) -> connection mapping - * would be likely to create substantial complexity. */ - - while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) { - + PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(conn_entry->all_signals_objects))) @@ -943,10 +937,8 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { extensions = pa_xnew(const char *, *n); - while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) { - extensions[i] = ext_name; - ++i; - } + while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) + extensions[i++] = ext_name; return extensions; } -- cgit From 0b6662023bfb121b0e553ce00b4229705c8e7aef Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:06:21 +0300 Subject: dbusiface-core: Generate more informative error messages. --- src/modules/dbus/iface-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 685ba63b..be07648c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -655,7 +655,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi return; if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path); return; } @@ -741,7 +741,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v return; if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path); return; } @@ -1154,7 +1154,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void } if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name); return; } @@ -1186,7 +1186,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo } if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name); return; } -- cgit From 5ece8e8833ff40a4c535fc7d6705377fcf7fd0fd Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:10:05 +0300 Subject: dbusiface-core: Add functions for getting various object paths. --- src/modules/dbus/iface-core.c | 21 +++++++++++++++++++++ src/modules/dbus/iface-core.h | 4 ++++ 2 files changed, 25 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index be07648c..e8ea50ba 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2033,3 +2033,24 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_xfree(c); } + +const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) { + pa_assert(c); + pa_assert(sink); + + return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index))); +} + +const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) { + pa_assert(c); + pa_assert(source); + + return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))); +} + +const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) { + pa_assert(c); + pa_assert(module); + + return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index))); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 964a37bd..1b73782f 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -35,4 +35,8 @@ typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); void pa_dbusiface_core_free(pa_dbusiface_core *c); +const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); +const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); +const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); + #endif -- cgit From 3e9de1a36c3f85f558f08167cd82163b0cbf3484 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:12:31 +0300 Subject: dbus-util: Add helpers for proplist handling. --- src/pulsecore/dbus-util.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/dbus-util.h | 4 +++ 2 files changed, 75 insertions(+) (limited to 'src') diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index 5db7f218..903acadb 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -564,6 +564,21 @@ void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_r dbus_message_unref(reply); } +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) { + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(in_reply_to); + pa_assert(proplist); + + pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_dbus_append_proplist_variant(&msg_iter, proplist); + pa_assert_se(dbus_connection_send(c, reply, NULL)); + dbus_message_unref(reply); +} + void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) { DBusMessageIter array_iter; unsigned i; @@ -640,6 +655,62 @@ void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + DBusMessageIter array_iter; + void *state = NULL; + const char *key; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter)); + + while ((key = pa_proplist_iterate(proplist, state))) { + const void *value = NULL; + size_t nbytes; + + pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + + pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter)); + pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes)); + pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter)); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter)); +} + +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) { + DBusMessageIter variant_iter; + + pa_assert(iter); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter)); + pa_dbus_append_proplist(&variant_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); +} + +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) { + DBusMessageIter dict_entry_iter; + + pa_assert(dict_iter); + pa_assert(key); + pa_assert(proplist); + + pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_dbus_append_proplist_variant(&dict_entry_iter, proplist); + pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); +} + int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { DBusMessageIter msg_iter; DBusMessageIter variant_iter; diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 97aae372..5443a4c1 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -69,12 +69,16 @@ void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); /* Helper functions for extracting the value argument of a Set call. If the * message is invalid, an error reply is sent and a negative number is -- cgit From 76bd03bddb7967c56d68ead22e5d4eae5a82625a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:14:27 +0300 Subject: dbus-util: Trivial comment punctuation fix. --- src/pulsecore/dbus-util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 5443a4c1..9cee293c 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -91,8 +91,8 @@ int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); -/* Returns a new proplist, that the caller has to free. If the proplist can't - * be read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist can't be + * read from the iterator, an error reply is sent and NULL is returned. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif -- cgit From 7699cfd4c041eedd2dae124cac75da0879b87f1e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:18:03 +0300 Subject: dbus-protocol: Split some overly long lines. --- src/pulsecore/protocol-dbus.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 5cbd0d91..2461e4ba 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -199,19 +199,21 @@ static void update_introspection(struct object_entry *oe) { pa_strbuf_printf(buf, " \n", method_handler->method_name); for (i = 0; i < method_handler->n_arguments; ++i) - pa_strbuf_printf(buf, " \n", method_handler->arguments[i].name, - method_handler->arguments[i].type, - method_handler->arguments[i].direction); + pa_strbuf_printf(buf, " \n", + method_handler->arguments[i].name, + method_handler->arguments[i].type, + method_handler->arguments[i].direction); pa_strbuf_puts(buf, " \n"); } handlers_state = NULL; - while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL))) - pa_strbuf_printf(buf, " \n", property_handler->property_name, - property_handler->type, - property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); + PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state) + pa_strbuf_printf(buf, " \n", + property_handler->property_name, + property_handler->type, + property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write"); for (i = 0; i < iface_entry->n_signals; ++i) { pa_strbuf_printf(buf, " \n", iface_entry->signals[i].name); -- cgit From 16dce8d7cbe4dcad56c3fbd1f47af6b50d2581a7 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:19:33 +0300 Subject: dbus-protocol: Take advantage of the helpers in dbus-util. --- src/pulsecore/protocol-dbus.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 2461e4ba..62f74a56 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -408,7 +409,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa pa_dbus_method_handler *method_handler = NULL; pa_dbus_property_handler *property_handler = NULL; const char *attempted_property = NULL; - DBusMessage *reply = NULL; pa_assert(connection); pa_assert(message); @@ -427,10 +427,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_assert_se((reply = dbus_message_new_method_return(message))); - pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID)); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); - + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); goto finish; } @@ -450,26 +447,23 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa case FOUND_GET_ALL: if (iface_entry->get_all_properties_cb) iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); + /* TODO: Write an else branch where a dummy response is sent. */ break; case PROPERTY_ACCESS_DENIED: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); break; case NO_SUCH_METHOD: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); break; case NO_SUCH_PROPERTY: - pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); break; case INVALID_MESSAGE_ARGUMENTS: - pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)))); - pa_assert_se(dbus_connection_send(connection, reply, NULL)); + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); break; default: @@ -477,9 +471,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa } finish: - if (reply) - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; } -- cgit From acad5063284f37315c8208916f1ac151791d7220 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 09:20:22 +0300 Subject: dbusiface-card: Implement the Card D-Bus interface. --- src/Makefile.am | 1 + src/modules/dbus/iface-card-profile.c | 67 +++++ src/modules/dbus/iface-card-profile.h | 43 +++ src/modules/dbus/iface-card.c | 490 ++++++++++++++++++++++++++++++++++ 4 files changed, 601 insertions(+) create mode 100644 src/modules/dbus/iface-card-profile.c create mode 100644 src/modules/dbus/iface-card-profile.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index e10b4ab6..e605ad78 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1278,6 +1278,7 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMI module_dbus_protocol_la_SOURCES = \ modules/dbus/iface-card.c modules/dbus/iface-card.h \ + modules/dbus/iface-card-profile.c modules/dbus/iface-card-profile.h \ modules/dbus/iface-client.c modules/dbus/iface-client.h \ modules/dbus/iface-core.c modules/dbus/iface-core.h \ modules/dbus/iface-device.c modules/dbus/iface-device.h \ diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c new file mode 100644 index 00000000..79524945 --- /dev/null +++ b/src/modules/dbus/iface-card-profile.c @@ -0,0 +1,67 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "iface-card-profile.h" + +#define OBJECT_NAME "profile" + +struct pa_dbusiface_card_profile { + pa_card_profile *profile; + char *path; +}; + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) { + pa_dbusiface_card_profile *p = NULL; + + pa_assert(card); + pa_assert(profile); + + p = pa_xnew(pa_dbusiface_card_profile, 1); + p->profile = profile; + p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx); + + return p; +} + +void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) { + pa_assert(p); + + pa_xfree(p->path); + pa_xfree(p); +} + +const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p) { + pa_assert(p); + + return p->path; +} + +const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p) { + pa_assert(p); + + return p->profile->name; +} diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h new file mode 100644 index 00000000..e90313cd --- /dev/null +++ b/src/modules/dbus/iface-card-profile.h @@ -0,0 +1,43 @@ +#ifndef foodbusifacecardprofilehfoo +#define foodbusifacecardprofilehfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.CardProfile. + * + * See http://pulseaudio.org/wiki/DBusInterface for the CardProfile interface + * documentation. + */ + +#include + +#include "iface-card.h" + +typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx); +void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); + +const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); +const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p); + +#endif diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index e203c395..4ede718a 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -23,20 +23,476 @@ #include #endif +#include + #include +#include #include +#include "iface-card-profile.h" + #include "iface-card.h" #define OBJECT_NAME "card" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + struct pa_dbusiface_card { pa_dbusiface_core *core; pa_card *card; char *path; + pa_hashmap *profiles; + uint32_t next_profile_index; + pa_card_profile *active_profile; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_PROFILES, + PROPERTY_HANDLER_ACTIVE_PROFILE, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "ao", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "ao", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_PROFILES] = { .property_name = "Profiles", .type = "ao", .get_cb = handle_get_profiles, .set_cb = NULL }, + [PROPERTY_HANDLER_ACTIVE_PROFILE] = { .property_name = "ActiveProfile", .type = "o", .get_cb = handle_get_active_profile, .set_cb = handle_set_active_profile }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_GET_PROFILE_BY_NAME, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info get_profile_by_name_args[] = { { "name", "s", "in" }, { "profile", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_GET_PROFILE_BY_NAME] = { + .method_name = "GetProfileByName", + .arguments = get_profile_by_name_args, + .n_arguments = sizeof(get_profile_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_profile_by_name } +}; + +enum signal_index { + SIGNAL_ACTIVE_PROFILE_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info card_interface_info = { + .name = PA_DBUSIFACE_CARD_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + dbus_uint32_t idx; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->card->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->name); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *owner_module; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->card->module) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Card %s doesn't have an owner module.", c->card->name); + return; + } + + owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sinks(pa_dbusiface_card *c, unsigned *n) { + const char **sinks = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_sink *sink = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->card->sinks); + + if (*n == 0) + return NULL; + + sinks = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(sink, c->card->sinks, idx) { + sinks[i] = pa_dbusiface_core_get_sink_path(c->core, sink); + ++i; + } + + return sinks; +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **sinks; + unsigned n_sinks; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sinks = get_sinks(c, &n_sinks); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + + pa_xfree(sinks); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_sources(pa_dbusiface_card *c, unsigned *n) { + const char **sources = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_source *source = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->card->sources); + + if (*n == 0) + return NULL; + + sources = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(source, c->card->sinks, idx) { + sources[i] = pa_dbusiface_core_get_source_path(c->core, source); + ++i; + } + + return sources; +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **sources; + unsigned n_sources; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + sources = get_sources(c, &n_sources); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + + pa_xfree(sources); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) { + const char **profiles; + unsigned i = 0; + void *state = NULL; + pa_dbusiface_card_profile *profile; + + pa_assert(c); + pa_assert(n); + + *n = pa_hashmap_size(c->profiles); + + if (*n == 0) + return NULL; + + profiles = pa_xnew(const char *, *n); + + PA_HASHMAP_FOREACH(profile, c->profiles, state) { + profiles[i] = pa_dbusiface_card_profile_get_path(profile); + ++i; + } + + return profiles; +} + +static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char **profiles; + unsigned n_profiles; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + profiles = get_profiles(c, &n_profiles); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles); + + pa_xfree(profiles); +} + +static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *active_profile; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->active_profile) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); + return; + } + + active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile); +} + +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + const char *new_active_path; + pa_dbusiface_card_profile *new_active; + int r; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) + return; + + if (!c->active_profile) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The card %s has no profiles, and therefore there's no active profile either.", + c->card->name); + return; + } + + if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path); + return; + } + + if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_proplist_variant_reply(conn, msg, c->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx; + const char *owner_module = NULL; + const char **sinks = NULL; + unsigned n_sinks = 0; + const char **sources = NULL; + unsigned n_sources = 0; + const char **profiles = NULL; + unsigned n_profiles = 0; + const char *active_profile = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->card->index; + if (c->card->module) + owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module); + sinks = get_sinks(c, &n_sinks); + sources = get_sources(c, &n_sources); + profiles = get_profiles(c, &n_profiles); + if (c->active_profile) + active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &c->card->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROFILES].property_name, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles); + + if (active_profile) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PROFILE].property_name, DBUS_TYPE_OBJECT_PATH, &active_profile); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(sinks); + pa_xfree(sources); + pa_xfree(profiles); +} + +static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card *c = userdata; + DBusError error; + const char *profile_name = NULL; + pa_dbusiface_card_profile *profile = NULL; + const char *profile_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(profile = pa_hashmap_get(c->profiles, profile_name))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name); + return; + } + + profile_path = pa_dbusiface_card_profile_get_path(profile); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &profile_path); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_card *c = userdata; + + pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); + pa_assert(c); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + + if (c->active_profile != c->card->active_profile) { + const char *object_path; + + c->active_profile = c->card->active_profile; + object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + + pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (!pa_proplist_equal(c->proplist, c->card->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); + + pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) { pa_dbusiface_card *c = NULL; @@ -48,13 +504,47 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) c->core = core; c->card = card; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index); + c->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->next_profile_index = 0; + c->active_profile = NULL; + c->proplist = pa_proplist_copy(card->proplist); + c->dbus_protocol = pa_dbus_protocol_get(card->core); + c->subscription = pa_subscription_new(card->core, PA_SUBSCRIPTION_MASK_CARD, subscription_cb, c); + + if (card->profiles) { + pa_card_profile *profile; + void *state = NULL; + + PA_HASHMAP_FOREACH(profile, card->profiles, state) { + pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++); + pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p); + } + pa_assert_se(c->active_profile = card->active_profile); + } + + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0); return c; } +static void profile_free_cb(void *p, void *userdata) { + pa_dbusiface_card_profile *profile = p; + + pa_assert(profile); + + pa_dbusiface_card_profile_free(profile); +} + void pa_dbusiface_card_free(pa_dbusiface_card *c) { pa_assert(c); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0); + + pa_hashmap_free(c->profiles, profile_free_cb, NULL); + pa_proplist_free(c->proplist); + pa_dbus_protocol_unref(c->dbus_protocol); + pa_subscription_free(c->subscription); + pa_xfree(c->path); pa_xfree(c); } -- cgit From 8c8df77d2a774a65741539381496d02f04747974 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 9 Aug 2009 10:36:20 +0300 Subject: dbusiface-card-profile: Implement the CardProfile D-Bus interface. --- src/modules/dbus/iface-card-profile.c | 158 +++++++++++++++++++++++++++++++++- src/modules/dbus/iface-card-profile.h | 5 +- src/modules/dbus/iface-card.c | 2 +- src/modules/dbus/iface-card.h | 1 + 4 files changed, 163 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 79524945..2b85a5fc 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -23,26 +23,178 @@ #include #endif +#include + #include +#include #include "iface-card-profile.h" #define OBJECT_NAME "profile" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + struct pa_dbusiface_card_profile { + uint32_t index; pa_card_profile *profile; char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DESCRIPTION, + PROPERTY_HANDLER_SINKS, + PROPERTY_HANDLER_SOURCES, + PROPERTY_HANDLER_PRIORITY, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL }, + [PROPERTY_HANDLER_SINKS] = { .property_name = "Sinks", .type = "u", .get_cb = handle_get_sinks, .set_cb = NULL }, + [PROPERTY_HANDLER_SOURCES] = { .property_name = "Sources", .type = "u", .get_cb = handle_get_sources, .set_cb = NULL }, + [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL }, }; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) { +static pa_dbus_interface_info profile_interface_info = { + .name = PA_DBUSIFACE_CARD_PROFILE_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->name); +} + +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->description); +} + +static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t sinks = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sinks = p->profile->n_sinks; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sinks); +} + +static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t sources = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sources = p->profile->n_sources; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sources); +} + +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->profile->priority; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_card_profile *p = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t sinks = 0; + dbus_uint32_t sources = 0; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + sinks = p->profile->n_sinks; + sources = p->profile->n_sources; + priority = p->profile->priority; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->profile->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->profile->description); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_UINT32, &sinks); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_UINT32, &sources); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) { pa_dbusiface_card_profile *p = NULL; pa_assert(card); pa_assert(profile); p = pa_xnew(pa_dbusiface_card_profile, 1); + p->index = idx; p->profile = profile; p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx); + p->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &profile_interface_info, p) >= 0); return p; } @@ -50,6 +202,10 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) { pa_assert(p); + pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, profile_interface_info.name) >= 0); + + pa_dbus_protocol_unref(p->dbus_protocol); + pa_xfree(p->path); pa_xfree(p); } diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h index e90313cd..9edcde73 100644 --- a/src/modules/dbus/iface-card-profile.h +++ b/src/modules/dbus/iface-card-profile.h @@ -29,12 +29,15 @@ */ #include +#include #include "iface-card.h" +#define PA_DBUSIFACE_CARD_PROFILE_INTERFACE PA_DBUS_CORE_INTERFACE ".CardProfile" + typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx); +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx); void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 4ede718a..64ec12a9 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -516,7 +516,7 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) void *state = NULL; PA_HASHMAP_FOREACH(profile, card->profiles, state) { - pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++); + pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, card->core, profile, c->next_profile_index++); pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p); } pa_assert_se(c->active_profile = card->active_profile); diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h index 57ad4e31..e2c08a3b 100644 --- a/src/modules/dbus/iface-card.h +++ b/src/modules/dbus/iface-card.h @@ -29,6 +29,7 @@ */ #include +#include #include "iface-core.h" -- cgit From 7cfda56af99b6d6bbb92fd6de208edd4c6991240 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:38:01 +0300 Subject: dbus-protocol: Add a note for _send_signal that by default the signal isn't actually sent. --- src/pulsecore/protocol-dbus.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index c6b630a1..38ba8a18 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -149,6 +149,10 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c * do anything in that case either. */ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal); +/* Sends the given signal to all interested clients. By default no signals are + * sent - clients have to explicitly to request signals by calling + * .Core1.ListenForSignal. That method's handler then calls + * pa_dbus_protocol_add_signal_listener(). */ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal); /* Returns an array of extension identifier strings. The strings pointers point -- cgit From 31117fe99e07b11b55564a6df455211db712c2f3 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 10 Aug 2009 10:40:40 +0300 Subject: dbus-protocol: Fix signal sending for the case when the client doesn't listen for all signals. --- src/pulsecore/protocol-dbus.c | 11 ++++++++++- src/pulsecore/protocol-dbus.h | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 62f74a56..01285705 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -848,6 +848,8 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c for (i = 0; i < n_objects; ++i) pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL); + pa_hashmap_put(conn_entry->listening_signals, signal, object_set); + } else { conn_entry->listening_for_all_signals = TRUE; @@ -892,10 +894,15 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { void *state = NULL; pa_idxset *object_set; DBusMessage *signal_copy; + char *signal_string; pa_assert(p); pa_assert(signal); pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL); + pa_assert_se(dbus_message_get_interface(signal)); + pa_assert_se(dbus_message_get_member(signal)); + + signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal), dbus_message_get_member(signal)); PA_HASHMAP_FOREACH(conn_entry, p->connections, state) { if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */ @@ -903,7 +910,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { || pa_idxset_isempty(conn_entry->all_signals_objects))) || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */ - && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal)) + && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal_string)) && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL) || pa_idxset_isempty(object_set)))) { @@ -912,6 +919,8 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) { dbus_message_unref(signal_copy); } } + + pa_xfree(signal_string); } const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) { diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index 38ba8a18..d771b4fc 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -129,7 +129,8 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection * pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn); /* Enables signal receiving for the given connection. The connection must have - * been registered earlier. + * been registered earlier. The signal string must contain both the signal + * interface and the signal name, concatenated using a period as the separator. * * If the signal argument is NULL, all signals will be sent to the connection, * otherwise calling this function only adds the given signal to the list of -- cgit From 8b5550dba32857918fd8e70b93be9094c11cf0c8 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:51:55 +0300 Subject: dbusiface-card: Split some overly long lines. --- src/modules/dbus/iface-card-profile.c | 6 +++++- src/modules/dbus/iface-card-profile.h | 6 +++++- src/modules/dbus/iface-card.c | 13 ++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 2b85a5fc..4a1696c1 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -182,7 +182,11 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) { +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( + pa_dbusiface_card *card, + pa_core *core, + pa_card_profile *profile, + uint32_t idx) { pa_dbusiface_card_profile *p = NULL; pa_assert(card); diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h index 9edcde73..a09767f8 100644 --- a/src/modules/dbus/iface-card-profile.h +++ b/src/modules/dbus/iface-card-profile.h @@ -37,7 +37,11 @@ typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile; -pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx); +pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( + pa_dbusiface_card *card, + pa_core *core, + pa_card_profile *profile, + uint32_t idx); void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p); const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p); diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 64ec12a9..40031d2e 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -312,7 +312,10 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_assert(c); if (!c->active_profile) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); + pa_assert(pa_hashmap_isempty(c->profiles)); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); return; } @@ -470,7 +473,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 c->active_profile = c->card->active_profile; object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); - pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -483,7 +488,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); - pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); dbus_message_iter_init_append(signal, &msg_iter); pa_dbus_append_proplist(&msg_iter, c->proplist); -- cgit From afb79ee83e95b3c492abe55ea0c26a9fe902b075 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:53:36 +0300 Subject: dbusiface-card-profile: Assert the core argument isn't NULL. --- src/modules/dbus/iface-card-profile.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c index 4a1696c1..004e2e88 100644 --- a/src/modules/dbus/iface-card-profile.c +++ b/src/modules/dbus/iface-card-profile.c @@ -190,6 +190,7 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new( pa_dbusiface_card_profile *p = NULL; pa_assert(card); + pa_assert(core); pa_assert(profile); p = pa_xnew(pa_dbusiface_card_profile, 1); -- cgit From 18f9f1b5d17c83280205083760edd4dccb37ed6a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 05:58:24 +0300 Subject: dbusiface-card: Use the ++ operator like it's meant to be used. --- src/modules/dbus/iface-card.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 40031d2e..e3606c79 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -279,10 +279,8 @@ static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) { profiles = pa_xnew(const char *, *n); - PA_HASHMAP_FOREACH(profile, c->profiles, state) { - profiles[i] = pa_dbusiface_card_profile_get_path(profile); - ++i; - } + PA_HASHMAP_FOREACH(profile, c->profiles, state) + profiles[i++] = pa_dbusiface_card_profile_get_path(profile); return profiles; } -- cgit From 31c544d8439edee56ecc641c07012042ab38bb80 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:00:46 +0300 Subject: dbusiface-card: Assert that the profiles list is empty if there's no active profile. --- src/modules/dbus/iface-card.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index e3606c79..924bea9c 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -336,6 +336,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo return; if (!c->active_profile) { + pa_assert(pa_hashmap_isempty(c->profiles)); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name); -- cgit From 90c73db449e67cf999a354c7e3ccad3edd44a829 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:09:35 +0300 Subject: dbusiface-card: Fix the OwnerModule property type in handle_get_all(). --- src/modules/dbus/iface-card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 924bea9c..5a797948 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -406,7 +406,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver); if (owner_module) - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources); -- cgit From 1e65d8d35b23d3c76a30155c42bec282257579e5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:11:38 +0300 Subject: dbusiface-core: New function: pa_dbusiface_core_get_card_path(). --- src/modules/dbus/iface-core.c | 7 +++++++ src/modules/dbus/iface-core.h | 1 + 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e8ea50ba..81e709f5 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2034,6 +2034,13 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_xfree(c); } +const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) { + pa_assert(c); + pa_assert(card); + + return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index))); +} + const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) { pa_assert(c); pa_assert(sink); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 1b73782f..70102054 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -35,6 +35,7 @@ typedef struct pa_dbusiface_core pa_dbusiface_core; pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core); void pa_dbusiface_core_free(pa_dbusiface_core *c); +const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card); const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); -- cgit From 22ab1414507f84d6a42c5255d66ba15d8097d35f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:13:17 +0300 Subject: dbus-protocol: Use pa_hashmap_remove() instead of _get(). --- src/pulsecore/protocol-dbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 01285705..cf561c83 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -841,7 +841,7 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c conn_entry->listening_for_all_signals = FALSE; /* Replace the old object list with a new one. */ - if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal))) + if ((object_set = pa_hashmap_remove(conn_entry->listening_signals, signal))) pa_idxset_free(object_set, free_listened_object_name_cb, NULL); object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- cgit From 91f626f777402fa6485658b04cbcdf2a7aac3f01 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 06:18:41 +0300 Subject: dbusiface-device: Implement the Device and DevicePort D-Bus interfaces. --- src/Makefile.am | 1 + src/modules/dbus/iface-device-port.c | 190 ++++++ src/modules/dbus/iface-device-port.h | 50 ++ src/modules/dbus/iface-device.c | 1208 +++++++++++++++++++++++++++++++++- src/modules/dbus/iface-device.h | 5 + 5 files changed, 1448 insertions(+), 6 deletions(-) create mode 100644 src/modules/dbus/iface-device-port.c create mode 100644 src/modules/dbus/iface-device-port.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index e605ad78..8722f978 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1282,6 +1282,7 @@ module_dbus_protocol_la_SOURCES = \ modules/dbus/iface-client.c modules/dbus/iface-client.h \ modules/dbus/iface-core.c modules/dbus/iface-core.h \ modules/dbus/iface-device.c modules/dbus/iface-device.h \ + modules/dbus/iface-device-port.c modules/dbus/iface-device-port.h \ modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \ modules/dbus/iface-module.c modules/dbus/iface-module.h \ modules/dbus/iface-sample.c modules/dbus/iface-sample.h \ diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c new file mode 100644 index 00000000..d403b6a2 --- /dev/null +++ b/src/modules/dbus/iface-device-port.c @@ -0,0 +1,190 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include + +#include "iface-device-port.h" + +#define OBJECT_NAME "port" + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +struct pa_dbusiface_device_port { + uint32_t index; + pa_device_port *port; + char *path; + pa_dbus_protocol *dbus_protocol; +}; + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DESCRIPTION, + PROPERTY_HANDLER_PRIORITY, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL }, + [PROPERTY_HANDLER_PRIORITY] = { .property_name = "Priority", .type = "u", .get_cb = handle_get_priority, .set_cb = NULL }, +}; + +static pa_dbus_interface_info port_interface_info = { + .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name); +} + +static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description); +} + +static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->port->priority; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device_port *p = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t priority = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(p); + + priority = p->port->priority; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +pa_dbusiface_device_port *pa_dbusiface_device_port_new( + pa_dbusiface_device *device, + pa_core *core, + pa_device_port *port, + uint32_t idx) { + pa_dbusiface_device_port *p = NULL; + + pa_assert(device); + pa_assert(core); + pa_assert(port); + + p = pa_xnew(pa_dbusiface_device_port, 1); + p->index = idx; + p->port = port; + p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx); + p->dbus_protocol = pa_dbus_protocol_get(core); + + pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0); + + return p; +} + +void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) { + pa_assert(p); + + pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0); + + pa_dbus_protocol_unref(p->dbus_protocol); + + pa_xfree(p->path); + pa_xfree(p); +} + +const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) { + pa_assert(p); + + return p->path; +} + +const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) { + pa_assert(p); + + return p->port->name; +} diff --git a/src/modules/dbus/iface-device-port.h b/src/modules/dbus/iface-device-port.h new file mode 100644 index 00000000..0461e2ff --- /dev/null +++ b/src/modules/dbus/iface-device-port.h @@ -0,0 +1,50 @@ +#ifndef foodbusifacedeviceporthfoo +#define foodbusifacedeviceporthfoo + +/*** + This file is part of PulseAudio. + + Copyright 2009 Tanu Kaskinen + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +/* This object implements the D-Bus interface org.PulseAudio.Core1.DevicePort. + * + * See http://pulseaudio.org/wiki/DBusInterface for the DevicePort interface + * documentation. + */ + +#include +#include + +#include "iface-device.h" + +#define PA_DBUSIFACE_DEVICE_PORT_INTERFACE PA_DBUS_CORE_INTERFACE ".DevicePort" + +typedef struct pa_dbusiface_device_port pa_dbusiface_device_port; + +pa_dbusiface_device_port *pa_dbusiface_device_port_new( + pa_dbusiface_device *device, + pa_core *core, + pa_device_port *port, + uint32_t idx); +void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p); + +const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p); +const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p); + +#endif diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 1a64f43c..3cf9d19c 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -24,62 +24,1258 @@ #endif #include +#include #include +#include "iface-device-port.h" + #include "iface-device.h" #define SINK_OBJECT_NAME "sink" #define SOURCE_OBJECT_NAME "source" +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + enum device_type { DEVICE_TYPE_SINK, DEVICE_TYPE_SOURCE }; struct pa_dbusiface_device { + pa_dbusiface_core *core; + union { pa_sink *sink; pa_source *source; }; enum device_type type; char *path; + pa_cvolume volume; + pa_bool_t is_muted; + union { + pa_sink_state_t sink_state; + pa_source_state_t source_state; + }; + pa_hashmap *ports; + uint32_t next_port_index; + pa_device_port *active_port; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_CARD, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS, + PROPERTY_HANDLER_VOLUME, + PROPERTY_HANDLER_HAS_FLAT_VOLUME, + PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME, + PROPERTY_HANDLER_BASE_VOLUME, + PROPERTY_HANDLER_VOLUME_STEPS, + PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_HAS_HARDWARE_VOLUME, + PROPERTY_HANDLER_HAS_HARDWARE_MUTE, + PROPERTY_HANDLER_CONFIGURED_LATENCY, + PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY, + PROPERTY_HANDLER_LATENCY, + PROPERTY_HANDLER_IS_HARDWARE_DEVICE, + PROPERTY_HANDLER_IS_NETWORK_DEVICE, + PROPERTY_HANDLER_STATE, + PROPERTY_HANDLER_PORTS, + PROPERTY_HANDLER_ACTIVE_PORT, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +enum sink_property_handler_index { + SINK_PROPERTY_HANDLER_MONITOR_SOURCE, + SINK_PROPERTY_HANDLER_MAX +}; + +enum source_property_handler_index { + SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK, + SOURCE_PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_CARD] = { .property_name = "Card", .type = "o", .get_cb = handle_get_card, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, + [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, + [PROPERTY_HANDLER_HAS_FLAT_VOLUME] = { .property_name = "HasFlatVolume", .type = "b", .get_cb = handle_get_has_flat_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b", .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_BASE_VOLUME] = { .property_name = "BaseVolume", .type = "u", .get_cb = handle_get_base_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_VOLUME_STEPS] = { .property_name = "VolumeSteps", .type = "u", .get_cb = handle_get_volume_steps, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME] = { .property_name = "HasHardwareVolume", .type = "b", .get_cb = handle_get_has_hardware_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_HARDWARE_MUTE] = { .property_name = "HasHardwareMute", .type = "b", .get_cb = handle_get_has_hardware_mute, .set_cb = NULL }, + [PROPERTY_HANDLER_CONFIGURED_LATENCY] = { .property_name = "ConfiguredLatency", .type = "t", .get_cb = handle_get_configured_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY] = { .property_name = "HasDynamicLatency", .type = "b", .get_cb = handle_get_has_dynamic_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_LATENCY] = { .property_name = "Latency", .type = "t", .get_cb = handle_get_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_HARDWARE_DEVICE] = { .property_name = "IsHardwareDevice", .type = "b", .get_cb = handle_get_is_hardware_device, .set_cb = NULL }, + [PROPERTY_HANDLER_IS_NETWORK_DEVICE] = { .property_name = "IsNetworkDevice", .type = "b", .get_cb = handle_get_is_network_device, .set_cb = NULL }, + [PROPERTY_HANDLER_STATE] = { .property_name = "State", .type = "u", .get_cb = handle_get_state, .set_cb = NULL }, + [PROPERTY_HANDLER_PORTS] = { .property_name = "Ports", .type = "ao", .get_cb = handle_get_ports, .set_cb = NULL }, + [PROPERTY_HANDLER_ACTIVE_PORT] = { .property_name = "ActivePort", .type = "o", .get_cb = handle_get_active_port, .set_cb = handle_set_active_port }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +static pa_dbus_property_handler sink_property_handlers[SINK_PROPERTY_HANDLER_MAX] = { + [SINK_PROPERTY_HANDLER_MONITOR_SOURCE] = { .property_name = "MonitorSource", .type = "o", .get_cb = handle_sink_get_monitor_source, .set_cb = NULL } +}; + +static pa_dbus_property_handler source_property_handlers[SOURCE_PROPERTY_HANDLER_MAX] = { + [SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK] = { .property_name = "MonitorOfSink", .type = "o", .get_cb = handle_source_get_monitor_of_sink, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_SUSPEND, + METHOD_HANDLER_GET_PORT_BY_NAME, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info suspend_args[] = { { "suspend", "b", "in" } }; +static pa_dbus_arg_info get_port_by_name_args[] = { { "name", "s", "in" }, { "port", "o", "out" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_SUSPEND] = { + .method_name = "Suspend", + .arguments = suspend_args, + .n_arguments = sizeof(suspend_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_suspend }, + [METHOD_HANDLER_GET_PORT_BY_NAME] = { + .method_name = "GetPortByName", + .arguments = get_port_by_name_args, + .n_arguments = sizeof(get_port_by_name_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_get_port_by_name } +}; + +enum signal_index { + SIGNAL_VOLUME_UPDATED, + SIGNAL_MUTE_UPDATED, + SIGNAL_STATE_UPDATED, + SIGNAL_ACTIVE_PORT_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; +static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; +static pa_dbus_arg_info state_updated_args[] = { { "state", "u", NULL } }; +static pa_dbus_arg_info active_port_updated_args[] = { { "port", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, + [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, + [SIGNAL_STATE_UPDATED] = { .name = "StateUpdated", .arguments = state_updated_args, .n_arguments = 1 }, + [SIGNAL_ACTIVE_PORT_UPDATED] = { .name = "ActivePortUpdated", .arguments = active_port_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info device_interface_info = { + .name = PA_DBUSIFACE_DEVICE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static pa_dbus_interface_info sink_interface_info = { + .name = PA_DBUSIFACE_SINK_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = sink_property_handlers, + .n_property_handlers = SINK_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_sink_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static pa_dbus_interface_info source_interface_info = { + .name = PA_DBUSIFACE_SOURCE_INTERFACE, + .method_handlers = NULL, + .n_method_handlers = 0, + .property_handlers = source_property_handlers, + .n_property_handlers = SOURCE_PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_source_get_all, + .signals = NULL, + .n_signals = 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *name = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &name); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *driver = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_module *owner_module = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + + if (!owner_module) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name); + return; + } + + object_path = pa_dbusiface_core_get_module_path(d->core, owner_module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_card *card = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; + + if (!card) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name); + return; + } + + object_path = pa_dbusiface_core_get_card_path(d->core, card); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t sample_rate = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; + + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels); +} + +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels); +} + +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + unsigned device_channels = 0; + dbus_uint32_t *volume = NULL; + unsigned n_volume_entries = 0; + pa_cvolume new_vol; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_cvolume_init(&new_vol); + + device_channels = (d->type == DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels; + + new_vol.channels = device_channels; + + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) + return; + + if (n_volume_entries != device_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + return; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); + return; + } + new_vol.values[i] = volume[i]; + } + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE); + else + pa_source_set_volume(d->source, &new_vol, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_flat_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_flat_volume); +} + +static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_convertible_to_decibel_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) + : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); +} + +static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t base_volume; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &base_volume); +} + +static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t volume_steps; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps); +} + +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); +} + +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_muted = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) + return; + + if (d->type == DEVICE_TYPE_SINK) + pa_sink_set_mute(d->sink, is_muted, TRUE); + else + pa_source_set_mute(d->source, is_muted, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_hardware_volume = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_hardware_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) + : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_volume); +} + +static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_hardware_mute = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_hardware_mute = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) + : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_mute); +} + +static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint64_t configured_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + configured_latency = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_requested_latency(d->sink) + : pa_source_get_requested_latency(d->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &configured_latency); +} + +static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t has_dynamic_latency = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) + : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_dynamic_latency); +} + +static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint64_t latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name); + else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name); + return; + + latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &latency); +} + +static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_hardware_device = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + is_hardware_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HARDWARE) + : (d->source->flags & PA_SOURCE_HARDWARE); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_hardware_device); +} + +static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t is_network_device = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + is_network_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_NETWORK) + : (d->source->flags & PA_SOURCE_NETWORK); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_network_device); +} + +static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_uint32_t state; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &state); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_ports(pa_dbusiface_device *d, unsigned *n) { + const char **ports; + unsigned i = 0; + void *state = NULL; + pa_dbusiface_device_port *port = NULL; + + pa_assert(d); + pa_assert(n); + + *n = pa_hashmap_size(d->ports); + + if (*n == 0) + return NULL; + + ports = pa_xnew(const char *, *n); + + PA_HASHMAP_FOREACH(port, d->ports, state) + ports[i++] = pa_dbusiface_device_port_get_path(port); + + return ports; +} + +static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char **ports = NULL; + unsigned n_ports = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + ports = get_ports(d, &n_ports); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, ports, n_ports); + + pa_xfree(ports); +} + +static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *active_port; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (!d->active_port) { + pa_assert(pa_hashmap_isempty(d->ports)); + + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The sink %s has no ports, and therefore there's no active port either.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The source %s has no ports, and therefore there's no active port either.", d->source->name); + return; + } + + active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port); +} + +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *new_active_path; + pa_dbusiface_device_port *new_active; + int r; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) + return; + + if (!d->active_port) { + pa_assert(pa_hashmap_isempty(d->ports)); + + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The sink %s has no ports, and therefore there's no active port either.", d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "The source %s has no ports, and therefore there's no active port either.", d->source->name); + return; + } + + if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path); + return; + } + + if (d->type == DEVICE_TYPE_SINK) { + if ((r = pa_sink_set_port(d->sink, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_sink_set_port() failed with error code %i.", r); + return; + } + } else { + if ((r = pa_source_set_port(d->source, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Internal error in PulseAudio: pa_source_set_port() failed with error code %i.", r); + return; + } + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + pa_dbus_send_proplist_variant_reply(conn, msg, d->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + const char *name = NULL; + const char *driver = NULL; + pa_module *owner_module = NULL; + const char *owner_module_path = NULL; + pa_card *card = NULL; + const char *card_path = NULL; + dbus_uint32_t sample_format = 0; + dbus_uint32_t sample_rate = 0; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_bool_t has_flat_volume = FALSE; + dbus_bool_t has_convertible_to_decibel_volume = FALSE; + dbus_uint32_t base_volume = 0; + dbus_uint32_t volume_steps = 0; + dbus_bool_t has_hardware_volume = FALSE; + dbus_bool_t has_hardware_mute = FALSE; + dbus_uint64_t configured_latency = 0; + dbus_bool_t has_dynamic_latency = FALSE; + dbus_uint64_t latency = 0; + dbus_bool_t is_hardware_device = FALSE; + dbus_bool_t is_network_device = FALSE; + dbus_uint32_t state = 0; + const char **ports = NULL; + unsigned n_ports = 0; + const char *active_port = NULL; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; + name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; + driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; + owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + if (owner_module) + owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module); + card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; + if (card) + card_path = pa_dbusiface_core_get_card_path(d->core, card); + sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; + sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; + channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; + has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) + : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); + base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; + volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; + has_hardware_volume = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) + : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); + has_hardware_mute = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) + : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); + configured_latency = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_requested_latency(d->sink) + : pa_source_get_requested_latency(d->source); + has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) + : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); + latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); + is_hardware_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_HARDWARE) + : (d->source->flags & PA_SOURCE_HARDWARE); + is_network_device = (d->type == DEVICE_TYPE_SINK) + ? (d->sink->flags & PA_SINK_NETWORK) + : (d->source->flags & PA_SOURCE_NETWORK); + state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source); + ports = get_ports(d, &n_ports); + if (d->active_port) + active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &name); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path); + + if (card) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARD].property_name, DBUS_TYPE_OBJECT_PATH, &card_path); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, d->volume.channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_FLAT_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_flat_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY].property_name, DBUS_TYPE_BOOLEAN, &has_dynamic_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_LATENCY].property_name, DBUS_TYPE_UINT64, &latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_HARDWARE_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_hardware_device); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_NETWORK_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_network_device); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_STATE].property_name, DBUS_TYPE_UINT32, &state); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PORTS].property_name, DBUS_TYPE_OBJECT_PATH, ports, n_ports); + + if (active_port) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PORT].property_name, DBUS_TYPE_OBJECT_PATH, &active_port); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, d->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(ports); +} + +static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + dbus_bool_t suspend = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0) + return; + + if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); + return; + } else if ((d->type == DEVICE_TYPE_SOURCE) && (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed."); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusError error; + const char *port_name = NULL; + pa_dbusiface_device_port *port = NULL; + const char *port_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (!(port = pa_hashmap_get(d->ports, port_name))) { + if (d->type == DEVICE_TYPE_SINK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, + "%s: No such port on sink %s.", port_name, d->sink->name); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, + "%s: No such port on source %s.", port_name, d->source->name); + return; + } + + port_path = pa_dbusiface_device_port_get_path(port); + + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &port_path); +} + +static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *monitor_source = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_source); +} + +static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + const char *monitor_source = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SINK); + + monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SINK_PROPERTY_HANDLER_MONITOR_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_source); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + const char *monitor_of_sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + if (!d->source->monitor_of) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s is not a monitor source.", d->source->name); + return; + } + + monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink); +} + +static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_device *d = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + const char *monitor_of_sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(d); + pa_assert(d->type == DEVICE_TYPE_SOURCE); + + if (d->source->monitor_of) + monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + if (monitor_of_sink) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_device *d = userdata; + + pa_assert(c); + pa_assert(d); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + const pa_cvolume *new_volume = NULL; + pa_bool_t new_muted = FALSE; + pa_sink_state_t new_sink_state = 0; + pa_source_state_t new_source_state = 0; + pa_device_port *new_active_port = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; + + pa_assert(((d->type == DEVICE_TYPE_SINK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) + || ((d->type == DEVICE_TYPE_SOURCE) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); + + new_volume = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_volume(d->sink, FALSE, FALSE) + : pa_source_get_volume(d->source, FALSE); + + if (!pa_cvolume_equal(&d->volume, new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + d->volume = *new_volume; + + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + + if (d->is_muted != new_muted) { + d->is_muted = new_muted; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (d->type == DEVICE_TYPE_SINK) + new_sink_state = pa_sink_get_state(d->sink); + else + new_source_state = pa_source_get_state(d->source); + + if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) + || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { + dbus_uint32_t state = 0; + + if (d->type == DEVICE_TYPE_SINK) + d->sink_state = new_sink_state; + else + d->source_state = new_source_state; + + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_STATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; + + if (d->active_port != new_active_port) { + const char *object_path = NULL; + + d->active_port = new_active_port; + object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + + if (!pa_proplist_equal(d->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, d->proplist); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) { - pa_dbusiface_device *d; + pa_dbusiface_device *d = NULL; pa_assert(core); pa_assert(sink); - d = pa_xnew(pa_dbusiface_device, 1); + d = pa_xnew0(pa_dbusiface_device, 1); + d->core = core; d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); + d->volume = *pa_sink_get_volume(sink, FALSE, FALSE); + d->is_muted = pa_sink_get_mute(sink, FALSE); + d->sink_state = pa_sink_get_state(sink); + d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + d->next_port_index = 0; + d->active_port = NULL; + d->proplist = pa_proplist_copy(sink->proplist); + d->dbus_protocol = pa_dbus_protocol_get(sink->core); + d->subscription = pa_subscription_new(sink->core, PA_SUBSCRIPTION_MASK_SINK, subscription_cb, d); + + if (sink->ports) { + pa_device_port *port; + void *state = NULL; + + PA_HASHMAP_FOREACH(port, sink->ports, state) { + pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, sink->core, port, d->next_port_index++); + pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p); + } + pa_assert_se(d->active_port = sink->active_port); + } + + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0); + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &sink_interface_info, d) >= 0); return d; } pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) { - pa_dbusiface_device *d; + pa_dbusiface_device *d = NULL; pa_assert(core); pa_assert(source); - d = pa_xnew(pa_dbusiface_device, 1); + d = pa_xnew0(pa_dbusiface_device, 1); + d->core = core; d->source = pa_source_ref(source); d->type = DEVICE_TYPE_SOURCE; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); + d->volume = *pa_source_get_volume(source, FALSE); + d->is_muted = pa_source_get_mute(source, FALSE); + d->source_state = pa_source_get_state(source); + d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + d->next_port_index = 0; + d->active_port = NULL; + d->proplist = pa_proplist_copy(source->proplist); + d->dbus_protocol = pa_dbus_protocol_get(source->core); + d->subscription = pa_subscription_new(source->core, PA_SUBSCRIPTION_MASK_SOURCE, subscription_cb, d); + + if (source->ports) { + pa_device_port *port; + void *state = NULL; + + PA_HASHMAP_FOREACH(port, source->ports, state) { + pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, source->core, port, d->next_port_index++); + pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p); + } + pa_assert_se(d->active_port = source->active_port); + } + + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0); + pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &source_interface_info, d) >= 0); return d; } +static void port_free_cb(void *p, void *userdata) { + pa_dbusiface_device_port *port = p; + + pa_assert(port); + + pa_dbusiface_device_port_free(port); +} + void pa_dbusiface_device_free(pa_dbusiface_device *d) { pa_assert(d); - if (d->type == DEVICE_TYPE_SINK) + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0); + + if (d->type == DEVICE_TYPE_SINK) { + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0); pa_sink_unref(d->sink); - else + + } else { + pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0); pa_source_unref(d->source); + } + pa_hashmap_free(d->ports, port_free_cb, NULL); + pa_dbus_protocol_unref(d->dbus_protocol); + pa_subscription_free(d->subscription); pa_xfree(d->path); pa_xfree(d); diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h index 1e9af83a..62e05e9a 100644 --- a/src/modules/dbus/iface-device.h +++ b/src/modules/dbus/iface-device.h @@ -29,11 +29,16 @@ * documentation. */ +#include #include #include #include "iface-core.h" +#define PA_DBUSIFACE_DEVICE_INTERFACE PA_DBUS_CORE_INTERFACE ".Device" +#define PA_DBUSIFACE_SINK_INTERFACE PA_DBUS_CORE_INTERFACE ".Sink" +#define PA_DBUSIFACE_SOURCE_INTERFACE PA_DBUS_CORE_INTERFACE ".Source" + typedef struct pa_dbusiface_device pa_dbusiface_device; pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink); -- cgit From f663d13acd306feafe2526c6a9258f14fd5d2ffc Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 16:54:11 +0300 Subject: dbusiface-core: Two new functions: pa_dbusiface_core_get_playback/record_stream_path(). --- src/modules/dbus/iface-core.c | 14 ++++++++++++++ src/modules/dbus/iface-core.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 81e709f5..2b5cf0b9 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2055,6 +2055,20 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))); } +const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) { + pa_assert(c); + pa_assert(sink_input); + + return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index))); +} + +const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) { + pa_assert(c); + pa_assert(source_output); + + return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index))); +} + const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) { pa_assert(c); pa_assert(module); diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 70102054..14dff7eb 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -38,6 +38,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c); const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card); const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink); const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source); +const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input); +const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); #endif -- cgit From 9ed25d7388bacfb0948d21d9aefa4fe92dbea0b5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 16:55:29 +0300 Subject: dbusiface-client: Implement the properties of the Client D-Bus interface. Based on a patch from Vincent Filali-Ansary. --- src/modules/dbus/iface-client.c | 279 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index d9c8653f..dd4e57ae 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2009 Vincent Filali-Ansary PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -23,7 +24,10 @@ #include #endif +#include + #include +#include #include #include "iface-client.h" @@ -31,19 +35,288 @@ #define OBJECT_NAME "client" struct pa_dbusiface_client { + pa_dbusiface_core *core; + pa_client *client; char *path; + + pa_dbus_protocol *dbus_protocol; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_PLAYBACK_STREAMS, + PROPERTY_HANDLER_RECORD_STREAMS, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao", .get_cb = handle_get_playback_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_RECORD_STREAMS] = { .property_name = "RecordStreams", .type = "ao", .get_cb = handle_get_record_streams, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +/*enum method_handler_index { + METHOD_HANDLER_KILL, + METHOD_HANDLER_UPDATE_PROPERTIES, + METHOD_HANDLER_REMOVE_PROPERTIES, + METHOD_HANDLER_MAX }; +static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; +static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_KILL] = { + .method_name = "Kill", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_kill }, + [METHOD_HANDLER_UPDATE_PROPERTIES] = { + .method_name = "UpdateProperties", + .arguments = update_propertes_args, + .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_update_properties }, + [METHOD_HANDLER_REMOVE_PROPERTIES] = { + .method_name = "RemoveProperties", + .arguments = remove_propertes_args, + .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_update_properties } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_CLIENT_EVENT, + SIGNAL_MAX +}; + +static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +};*/ + +static pa_dbus_interface_info client_interface_info = { + .name = OBJECT_NAME, + .method_handlers = /*method_handlers*/ NULL, + .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = /*signals*/ NULL, + .n_signals = /*SIGNAL_MAX*/ 0 +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->client->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char *owner_module = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (!c->client->module) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index); + return; + } + + owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) { + const char **playback_streams = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_sink_input *sink_input = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->client->sink_inputs); + + if (*n == 0) + return NULL; + + playback_streams = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx) + playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input); + + return playback_streams; +} + +static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char **playback_streams = NULL; + unsigned n_playback_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + playback_streams = get_playback_streams(c, &n_playback_streams); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + + pa_xfree(playback_streams); +} + +/* The caller frees the array, but not the strings. */ +static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) { + const char **record_streams = NULL; + unsigned i = 0; + uint32_t idx = 0; + pa_source_output *source_output = NULL; + + pa_assert(c); + pa_assert(n); + + *n = pa_idxset_size(c->client->source_outputs); + + if (*n == 0) + return NULL; + + record_streams = pa_xnew(const char *, *n); + + PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx) + record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output); + + return record_streams; +} + +static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + const char **record_streams = NULL; + unsigned n_record_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + record_streams = get_record_streams(c, &n_record_streams); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + + pa_xfree(record_streams); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + const char *owner_module = NULL; + const char **playback_streams = NULL; + unsigned n_playback_streams = 0; + const char **record_streams = NULL; + unsigned n_record_streams = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + idx = c->client->index; + if (c->client->module) + owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module); + playback_streams = get_playback_streams(c, &n_playback_streams); + record_streams = get_record_streams(c, &n_record_streams); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module); + + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams); + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); + + pa_xfree(playback_streams); + pa_xfree(record_streams); +} + pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { - pa_dbusiface_client *c; + pa_dbusiface_client *c = NULL; pa_assert(core); pa_assert(client); c = pa_xnew(pa_dbusiface_client, 1); + c->core = core; c->client = client; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); + c->dbus_protocol = pa_dbus_protocol_get(client->core); + + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); return c; } @@ -51,6 +324,10 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client void pa_dbusiface_client_free(pa_dbusiface_client *c) { pa_assert(c); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0); + + pa_dbus_protocol_unref(c->dbus_protocol); + pa_xfree(c->path); pa_xfree(c); } -- cgit From c7f4ed3c7a0453ff29181fe03666e0c7d3822317 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sat, 15 Aug 2009 17:08:21 +0300 Subject: dbusiface-client: Fix the interface name. --- src/modules/dbus/iface-client.c | 2 +- src/modules/dbus/iface-client.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index dd4e57ae..186e2d35 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -118,7 +118,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { };*/ static pa_dbus_interface_info client_interface_info = { - .name = OBJECT_NAME, + .name = PA_DBUSIFACE_CLIENT_INTERFACE, .method_handlers = /*method_handlers*/ NULL, .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, .property_handlers = property_handlers, diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h index ff906256..e8f151cd 100644 --- a/src/modules/dbus/iface-client.h +++ b/src/modules/dbus/iface-client.h @@ -22,16 +22,19 @@ USA. ***/ -/* This object implements the D-Bus interface org.PulseAudio.Core1.Card. +/* This object implements the D-Bus interface org.PulseAudio.Core1.Client. * - * See http://pulseaudio.org/wiki/DBusInterface for the Card interface + * See http://pulseaudio.org/wiki/DBusInterface for the Client interface * documentation. */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_CLIENT_INTERFACE PA_DBUS_CORE_INTERFACE ".Client" + typedef struct pa_dbusiface_client pa_dbusiface_client; pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client); -- cgit From a72bba18ea4dd273371179844a0a964e706fd489 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:39:39 +0300 Subject: dbusiface-client: Fix indentation. --- src/modules/dbus/iface-client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 186e2d35..587d5c42 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -129,16 +129,16 @@ static pa_dbus_interface_info client_interface_info = { }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { - pa_dbusiface_client *c = userdata; - dbus_uint32_t idx = 0; + pa_dbusiface_client *c = userdata; + dbus_uint32_t idx = 0; - pa_assert(conn); - pa_assert(msg); - pa_assert(c); + pa_assert(conn); + pa_assert(msg); + pa_assert(c); - idx = c->client->index; + idx = c->client->index; - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { -- cgit From f0db081223dd056d6ddbc2a97c557254198a0eaf Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:41:43 +0300 Subject: dbusiface-device: Free the copied proplist. --- src/modules/dbus/iface-device.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 3cf9d19c..15ece83b 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -1274,6 +1274,7 @@ void pa_dbusiface_device_free(pa_dbusiface_device *d) { pa_source_unref(d->source); } pa_hashmap_free(d->ports, port_free_cb, NULL); + pa_proplist_free(d->proplist); pa_dbus_protocol_unref(d->dbus_protocol); pa_subscription_free(d->subscription); -- cgit From 2bb3eef414f80189cf6af6cd66c519630e4c0a43 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 16 Aug 2009 19:42:56 +0300 Subject: dbusiface-stream: Implement about a half of the Stream D-Bus interface. --- src/modules/dbus/iface-stream.c | 383 ++++++++++++++++++++++++++++++++++++++++ src/modules/dbus/iface-stream.h | 3 + 2 files changed, 386 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index b5a17894..4333c16f 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -2,6 +2,7 @@ This file is part of PulseAudio. Copyright 2009 Tanu Kaskinen + Copyright 2009 Vincent Filali-Ansary PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -24,6 +25,7 @@ #endif #include +#include #include #include "iface-stream.h" @@ -43,8 +45,369 @@ struct pa_dbusiface_stream { }; enum stream_type type; char *path; + pa_cvolume volume; + pa_bool_t is_muted; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, +/* PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_OWNER_MODULE, + PROPERTY_HANDLER_CLIENT, + PROPERTY_HANDLER_DEVICE, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS,*/ + PROPERTY_HANDLER_VOLUME, + PROPERTY_HANDLER_IS_MUTED, +/* PROPERTY_HANDLER_BUFFER_LATENCY, + PROPERTY_HANDLER_DEVICE_LATENCY, + PROPERTY_HANDLER_RESAMPLE_METHOD,*/ + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, +/* [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, + [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },*/ + [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, + [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, +/* [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },*/ + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +/*enum method_handler_index { + METHOD_HANDLER_MOVE, + METHOD_HANDLER_KILL, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_MOVE] = { + .method_name = "Move", + .arguments = move_args, + .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_move }, + [METHOD_HANDLER_KILL] = { + .method_name = "Kill", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_kill } +};*/ + +enum signal_index { +/* SIGNAL_DEVICE_UPDATED, + SIGNAL_SAMPLE_RATE_UPDATED,*/ + SIGNAL_VOLUME_UPDATED, + SIGNAL_MUTE_UPDATED, + SIGNAL_PROPERTY_LIST_UPDATED, +/* SIGNAL_STREAM_EVENT,*/ + SIGNAL_MAX +}; + +/*static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; +static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };*/ +static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; +static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +/*static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };*/ + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { +/* [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },*/ + [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, + [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*, + [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/ }; +static pa_dbus_interface_info stream_interface_info = { + .name = PA_DBUSIFACE_STREAM_INTERFACE, + .method_handlers = /*method_handlers*/ NULL, + .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t idx; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels); +} + +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + unsigned stream_channels = 0; + dbus_uint32_t *volume = NULL; + unsigned n_volume_entries = 0; + pa_cvolume new_vol; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + return; + } + + pa_cvolume_init(&new_vol); + + stream_channels = s->sink_input->channel_map.channels; + + new_vol.channels = stream_channels; + + if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) + return; + + if (n_volume_entries != stream_channels) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); + return; + } + + for (i = 0; i < n_volume_entries; ++i) { + if (volume[i] > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]); + return; + } + new_vol.values[i] = volume[i]; + } + + pa_sink_input_set_volume(s->sink_input, &new_vol, TRUE, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); +} + +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_bool_t is_muted = FALSE; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) + return; + + if (s->type == STREAM_TYPE_RECORD) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); + return; + } + + pa_sink_input_set_mute(s->sink_input, is_muted, TRUE); + + pa_dbus_send_empty_reply(conn, msg); +}; + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx; + dbus_uint32_t volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; + if (s->type == STREAM_TYPE_PLAYBACK) { + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + } + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); + } + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(c); + pa_assert(s); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; + + pa_assert(((s->type == STREAM_TYPE_PLAYBACK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) + || ((s->type == STREAM_TYPE_RECORD) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_cvolume new_volume; + pa_bool_t new_muted = FALSE; + + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + s->volume = new_volume; + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_muted = pa_sink_input_get_mute(s->sink_input); + + if (s->is_muted != new_muted) { + s->is_muted = new_muted; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } + + new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + + if (!pa_proplist_equal(s->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { pa_dbusiface_stream *s; @@ -55,6 +418,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); + pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + s->is_muted = pa_sink_input_get_mute(sink_input); + s->proplist = pa_proplist_copy(sink_input->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); + s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -69,6 +439,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); + pa_cvolume_init(&s->volume); + s->is_muted = FALSE; + s->proplist = pa_proplist_copy(source_output->proplist); + s->dbus_protocol = pa_dbus_protocol_get(source_output->core); + s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); return s; } @@ -76,11 +453,17 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { pa_assert(s); + pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0); + if (s->type == STREAM_TYPE_PLAYBACK) pa_sink_input_unref(s->sink_input); else pa_source_output_unref(s->source_output); + pa_proplist_free(s->proplist); + pa_dbus_protocol_unref(s->dbus_protocol); + pa_subscription_free(s->subscription); + pa_xfree(s->path); pa_xfree(s); } diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h index b1b1854b..036b4e7e 100644 --- a/src/modules/dbus/iface-stream.h +++ b/src/modules/dbus/iface-stream.h @@ -28,11 +28,14 @@ * documentation. */ +#include #include #include #include "iface-core.h" +#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream" + typedef struct pa_dbusiface_stream pa_dbusiface_stream; pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input); -- cgit From f48684e4dbc00d102ed17700fb693726a2676566 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 08:26:06 +0300 Subject: namereg: Revert default device handling back to the upstream version. --- src/pulsecore/namereg.c | 57 ++++++++----------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index d7d83c5e..e26923d4 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -149,55 +149,21 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); - if (type == PA_NAMEREG_SINK && !c->default_sink) - pa_namereg_set_default_sink(c, data); - else if (type == PA_NAMEREG_SOURCE && !c->default_source) - pa_namereg_set_default_source(c, data); - return e->name; } void pa_namereg_unregister(pa_core *c, const char *name) { struct namereg_entry *e; - uint32_t idx; pa_assert(c); pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - if (c->default_sink == e->data) { - pa_sink *new_default = NULL; - - /* FIXME: the selection here should be based priority values on - * the sinks */ - - PA_IDXSET_FOREACH(new_default, c->sinks, idx) { - if (new_default != e->data && PA_SINK_IS_LINKED(pa_sink_get_state(new_default))) - break; - } - - pa_namereg_set_default_sink(c, new_default); - - } else if (c->default_source == e->data) { - pa_source *new_default = NULL; - - /* First, try to find one that isn't a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && !new_default->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - - if (!new_default) { - /* Then, fallback to a monitor */ - PA_IDXSET_FOREACH(new_default, c->sources, idx) { - if (new_default != e->data && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default))) - break; - } - } - - pa_namereg_set_default_source(c, new_default); - } + if (c->default_sink == e->data) + pa_namereg_set_default_sink(c, NULL); + else if (c->default_source == e->data) + pa_namereg_set_default_source(c, NULL); pa_xfree(e->name); pa_xfree(e); @@ -225,6 +191,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) return s->monitor_source; + } if (!name) @@ -281,18 +248,15 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { return s; } -/* XXX: After removing old functionality, has this function become useless? */ pa_sink *pa_namereg_get_default_sink(pa_core *c) { pa_sink *s; uint32_t idx; pa_assert(c); - if (!c->default_sink || PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) + if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) return c->default_sink; - /* The old default sink has become unlinked, set a new one. */ - /* FIXME: the selection here should be based priority values on * the sinks */ @@ -300,21 +264,18 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c) { if (PA_SINK_IS_LINKED(pa_sink_get_state(s))) return pa_namereg_set_default_sink(c, s); - return pa_namereg_set_default_sink(c, NULL); + return NULL; } -/* XXX: After removing old functionality, has this function become useless? */ pa_source *pa_namereg_get_default_source(pa_core *c) { pa_source *s; uint32_t idx; pa_assert(c); - if (!c->default_source || PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) + if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) return c->default_source; - /* The old default source has become unlinked, set a new one. */ - /* First, try to find one that isn't a monitor */ PA_IDXSET_FOREACH(s, c->sources, idx) if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) @@ -325,5 +286,5 @@ pa_source *pa_namereg_get_default_source(pa_core *c) { if (PA_SOURCE_IS_LINKED(pa_source_get_state(s))) return pa_namereg_set_default_source(c, s); - return pa_namereg_set_default_source(c, NULL); + return NULL; } -- cgit From a10e8360d72626635de1242cfc2c77207f13d56f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:42:06 +0300 Subject: dbusiface-core: New function: pa_dbusiface_core_get_client_path(). --- src/modules/dbus/iface-core.c | 7 +++++++ src/modules/dbus/iface-core.h | 1 + 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 2b5cf0b9..ec87158f 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2075,3 +2075,10 @@ const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_mod return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index))); } + +const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) { + pa_assert(c); + pa_assert(client); + + return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index))); +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index 14dff7eb..cf2a3b20 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -41,5 +41,6 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input); const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output); const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); +const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client); #endif -- cgit From efec274b6dcf239f580713f889957c370ac7ffc7 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:42:58 +0300 Subject: dbusiface-core: Two new functions: pa_dbusiface_core_get_sink/source(). --- src/modules/dbus/iface-core.c | 28 ++++++++++++++++++++++++++++ src/modules/dbus/iface-core.h | 6 ++++++ 2 files changed, 34 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index ec87158f..86a8fc7b 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2082,3 +2082,31 @@ const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_cli return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index))); } + +pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) { + pa_dbusiface_device *device = NULL; + + pa_assert(c); + pa_assert(object_path); + + device = pa_hashmap_get(c->sinks_by_path, object_path); + + if (device) + return pa_dbusiface_device_get_sink(device); + else + return NULL; +} + +pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) { + pa_dbusiface_device *device = NULL; + + pa_assert(c); + pa_assert(object_path); + + device = pa_hashmap_get(c->sources_by_path, object_path); + + if (device) + return pa_dbusiface_device_get_source(device); + else + return NULL; +} diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h index cf2a3b20..900b6d1c 100644 --- a/src/modules/dbus/iface-core.h +++ b/src/modules/dbus/iface-core.h @@ -43,4 +43,10 @@ const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module); const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client); +/* Returns NULL if there's no sink with the given path. */ +pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path); + +/* Returns NULL if there's no source with the given path. */ +pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path); + #endif -- cgit From 150cd1684a1e67d3a3797b34b45256afb0fa7f53 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:50:29 +0300 Subject: dbusiface-device: Split some overly long lines. --- src/modules/dbus/iface-device.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 15ece83b..486a0946 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -310,9 +310,11 @@ static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void if (!owner_module) { if (d->type == DEVICE_TYPE_SINK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't have an owner module.", d->sink->name); else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't have an owner module.", d->source->name); return; } @@ -334,9 +336,11 @@ static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userda if (!card) { if (d->type == DEVICE_TYPE_SINK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't belong to any card.", d->sink->name); else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't belong to any card.", d->source->name); return; } @@ -426,7 +430,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user return; if (n_volume_entries != device_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Expected %u volume entries, got %u.", device_channels, n_volume_entries); return; } @@ -598,9 +603,11 @@ static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *use pa_assert(d); if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY)) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sink %s doesn't support latency querying.", d->sink->name); else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY)) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Source %s doesn't support latency querying.", d->source->name); return; latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); -- cgit From bce6af18a3cc761375acebe589c821fc2abf9d1e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:52:10 +0300 Subject: dbusiface-device: Use a single if-else section instead of ternary operator overuse. --- src/modules/dbus/iface-device.c | 77 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 486a0946..078ae74a 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -817,48 +817,57 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_assert(msg); pa_assert(d); - idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index; - name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name; - driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver; - owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module; + if (d->type == DEVICE_TYPE_SINK) { + idx = d->sink->index; + name = d->sink->name; + driver = d->sink->driver; + owner_module = d->sink->module; + card = d->sink->card; + sample_format = d->sink->sample_spec.format; + sample_rate = d->sink->sample_spec.rate; + channel_map = &d->sink->channel_map; + has_flat_volume = d->sink->flags & PA_SINK_FLAT_VOLUME; + has_convertible_to_decibel_volume = d->sink->flags & PA_SINK_DECIBEL_VOLUME; + base_volume = d->sink->base_volume; + volume_steps = d->sink->n_volume_steps; + has_hardware_volume = d->sink->flags & PA_SINK_HW_VOLUME_CTRL; + has_hardware_mute = d->sink->flags & PA_SINK_HW_MUTE_CTRL; + configured_latency = pa_sink_get_requested_latency(d->sink); + has_dynamic_latency = d->sink->flags & PA_SINK_DYNAMIC_LATENCY; + latency = pa_sink_get_latency(d->sink); + is_hardware_device = d->sink->flags & PA_SINK_HARDWARE; + is_network_device = d->sink->flags & PA_SINK_NETWORK; + state = pa_sink_get_state(d->sink); + } else { + idx = d->source->index; + name = d->source->name; + driver = d->source->driver; + owner_module = d->source->module; + card = d->source->card; + sample_format = d->source->sample_spec.format; + sample_rate = d->source->sample_spec.rate; + channel_map = &d->source->channel_map; + has_flat_volume = FALSE; + has_convertible_to_decibel_volume = d->source->flags & PA_SOURCE_DECIBEL_VOLUME; + base_volume = d->source->base_volume; + volume_steps = d->source->n_volume_steps; + has_hardware_volume = d->source->flags & PA_SOURCE_HW_VOLUME_CTRL; + has_hardware_mute = d->source->flags & PA_SOURCE_HW_MUTE_CTRL; + configured_latency = pa_source_get_requested_latency(d->source); + has_dynamic_latency = d->source->flags & PA_SOURCE_DYNAMIC_LATENCY; + latency = pa_source_get_latency(d->source); + is_hardware_device = d->source->flags & PA_SOURCE_HARDWARE; + is_network_device = d->source->flags & PA_SOURCE_NETWORK; + state = pa_source_get_state(d->source); + } if (owner_module) owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module); - card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card; if (card) card_path = pa_dbusiface_core_get_card_path(d->core, card); - sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format; - sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate; - channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map; for (i = 0; i < channel_map->channels; ++i) channels[i] = channel_map->map[i]; for (i = 0; i < d->volume.channels; ++i) volume[i] = d->volume.values[i]; - has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE; - has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME) - : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME); - base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume; - volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps; - has_hardware_volume = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL) - : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL); - has_hardware_mute = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL) - : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL); - configured_latency = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_requested_latency(d->sink) - : pa_source_get_requested_latency(d->source); - has_dynamic_latency = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY) - : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY); - latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source); - is_hardware_device = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_HARDWARE) - : (d->source->flags & PA_SOURCE_HARDWARE); - is_network_device = (d->type == DEVICE_TYPE_SINK) - ? (d->sink->flags & PA_SINK_NETWORK) - : (d->source->flags & PA_SOURCE_NETWORK); - state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source); ports = get_ports(d, &n_ports); if (d->active_port) active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); -- cgit From b52871517944d55cec549af362398a5012f0b8b8 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:53:58 +0300 Subject: dbusiface-device: Fix argument reading in handle_suspend(). --- src/modules/dbus/iface-device.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 078ae74a..2e5940c3 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -923,13 +923,19 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t suspend = FALSE; + DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0) + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); return; + } if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); -- cgit From 70ff96b8ab100abb4969b882f66518ce739ae655 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:55:08 +0300 Subject: dbusiface-device: Save one level of identation by returning early. --- src/modules/dbus/iface-device.c | 195 ++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 2e5940c3..8dc0b2c4 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -1075,126 +1075,129 @@ static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void * static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_device *d = userdata; + DBusMessage *signal = NULL; + const pa_cvolume *new_volume = NULL; + pa_bool_t new_muted = FALSE; + pa_sink_state_t new_sink_state = 0; + pa_source_state_t new_source_state = 0; + pa_device_port *new_active_port = NULL; + pa_proplist *new_proplist = NULL; + unsigned i = 0; pa_assert(c); pa_assert(d); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - const pa_cvolume *new_volume = NULL; - pa_bool_t new_muted = FALSE; - pa_sink_state_t new_sink_state = 0; - pa_source_state_t new_source_state = 0; - pa_device_port *new_active_port = NULL; - pa_proplist *new_proplist = NULL; - unsigned i = 0; - - pa_assert(((d->type == DEVICE_TYPE_SINK) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) - || ((d->type == DEVICE_TYPE_SOURCE) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); - - new_volume = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_volume(d->sink, FALSE, FALSE) - : pa_source_get_volume(d->source, FALSE); - - if (!pa_cvolume_equal(&d->volume, new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; - - d->volume = *new_volume; - - for (i = 0; i < d->volume.channels; ++i) - volume[i] = d->volume.values[i]; - - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, - DBUS_TYPE_INVALID)); - - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if ((d->type == DEVICE_TYPE_SINK && idx != d->sink->index) || (d->type == DEVICE_TYPE_SOURCE && idx != d->source->index)) + return; - new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - if (d->is_muted != new_muted) { - d->is_muted = new_muted; + pa_assert(((d->type == DEVICE_TYPE_SINK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)) + || ((d->type == DEVICE_TYPE_SOURCE) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + new_volume = (d->type == DEVICE_TYPE_SINK) + ? pa_sink_get_volume(d->sink, FALSE, FALSE) + : pa_source_get_volume(d->source, FALSE); - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (!pa_cvolume_equal(&d->volume, new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; - if (d->type == DEVICE_TYPE_SINK) - new_sink_state = pa_sink_get_state(d->sink); - else - new_source_state = pa_source_get_state(d->source); + d->volume = *new_volume; - if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) - || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { - dbus_uint32_t state = 0; + for (i = 0; i < d->volume.channels; ++i) + volume[i] = d->volume.values[i]; - if (d->type == DEVICE_TYPE_SINK) - d->sink_state = new_sink_state; - else - d->source_state = new_source_state; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels, + DBUS_TYPE_INVALID)); - state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_STATE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); + new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->is_muted != new_muted) { + d->is_muted = new_muted; - new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); - if (d->active_port != new_active_port) { - const char *object_path = NULL; + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - d->active_port = new_active_port; - object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + if (d->type == DEVICE_TYPE_SINK) + new_sink_state = pa_sink_get_state(d->sink); + else + new_source_state = pa_source_get_state(d->source); - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state) + || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) { + dbus_uint32_t state = 0; - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->type == DEVICE_TYPE_SINK) + d->sink_state = new_sink_state; + else + d->source_state = new_source_state; - new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state; - if (!pa_proplist_equal(d->proplist, new_proplist)) { - DBusMessageIter msg_iter; + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_STATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)); - pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_assert_se(signal = dbus_message_new_signal(d->path, - PA_DBUSIFACE_DEVICE_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, d->proplist); + new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port; - pa_dbus_protocol_send_signal(d->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + if (d->active_port != new_active_port) { + const char *object_path = NULL; + + d->active_port = new_active_port; + object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name)); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_ACTIVE_PORT_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist; + + if (!pa_proplist_equal(d->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(d->path, + PA_DBUSIFACE_DEVICE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, d->proplist); + + pa_dbus_protocol_send_signal(d->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } -- cgit From 36dc61a2bff7ee93ca289c33c24b76cb82068cac Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 17 Aug 2009 16:56:12 +0300 Subject: dbusiface-stream: Finish the Stream D-Bus interface. --- src/modules/dbus/iface-stream.c | 578 ++++++++++++++++++++++++++++++++++------ 1 file changed, 498 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 4333c16f..354ca6eb 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -39,78 +39,86 @@ enum stream_type { }; struct pa_dbusiface_stream { + pa_dbusiface_core *core; + union { pa_sink_input *sink_input; pa_source_output *source_output; }; enum stream_type type; char *path; + union { + pa_sink *sink; + pa_source *source; + }; + uint32_t sample_rate; pa_cvolume volume; pa_bool_t is_muted; pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; pa_subscription *subscription; + pa_hook_slot *send_event_slot; }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); enum property_handler_index { PROPERTY_HANDLER_INDEX, -/* PROPERTY_HANDLER_DRIVER, + PROPERTY_HANDLER_DRIVER, PROPERTY_HANDLER_OWNER_MODULE, PROPERTY_HANDLER_CLIENT, PROPERTY_HANDLER_DEVICE, PROPERTY_HANDLER_SAMPLE_FORMAT, PROPERTY_HANDLER_SAMPLE_RATE, - PROPERTY_HANDLER_CHANNELS,*/ + PROPERTY_HANDLER_CHANNELS, PROPERTY_HANDLER_VOLUME, PROPERTY_HANDLER_IS_MUTED, -/* PROPERTY_HANDLER_BUFFER_LATENCY, + PROPERTY_HANDLER_BUFFER_LATENCY, PROPERTY_HANDLER_DEVICE_LATENCY, - PROPERTY_HANDLER_RESAMPLE_METHOD,*/ + PROPERTY_HANDLER_RESAMPLE_METHOD, PROPERTY_HANDLER_PROPERTY_LIST, PROPERTY_HANDLER_MAX }; static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, -/* [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, + [PROPERTY_HANDLER_DRIVER] = { .property_name = "Driver", .type = "s", .get_cb = handle_get_driver, .set_cb = NULL }, [PROPERTY_HANDLER_OWNER_MODULE] = { .property_name = "OwnerModule", .type = "o", .get_cb = handle_get_owner_module, .set_cb = NULL }, [PROPERTY_HANDLER_CLIENT] = { .property_name = "Client", .type = "o", .get_cb = handle_get_client, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "o", .get_cb = handle_get_device, .set_cb = NULL }, [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, - [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL },*/ + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, -/* [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, + [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, - [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL },*/ + [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL }, [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } }; -/*enum method_handler_index { +enum method_handler_index { METHOD_HANDLER_MOVE, METHOD_HANDLER_KILL, METHOD_HANDLER_MAX @@ -129,38 +137,38 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .arguments = NULL, .n_arguments = 0, .receive_cb = handle_kill } -};*/ +}; enum signal_index { -/* SIGNAL_DEVICE_UPDATED, - SIGNAL_SAMPLE_RATE_UPDATED,*/ + SIGNAL_DEVICE_UPDATED, + SIGNAL_SAMPLE_RATE_UPDATED, SIGNAL_VOLUME_UPDATED, SIGNAL_MUTE_UPDATED, SIGNAL_PROPERTY_LIST_UPDATED, -/* SIGNAL_STREAM_EVENT,*/ + SIGNAL_STREAM_EVENT, SIGNAL_MAX }; -/*static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; -static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } };*/ +static pa_dbus_arg_info device_updated_args[] = { { "device", "o", NULL } }; +static pa_dbus_arg_info sample_rate_updated_args[] = { { "sample_rate", "u", NULL } }; static pa_dbus_arg_info volume_updated_args[] = { { "volume", "au", NULL } }; static pa_dbus_arg_info mute_updated_args[] = { { "muted", "b", NULL } }; static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; -/*static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } };*/ +static pa_dbus_arg_info stream_event_args[] = { { "name", "s", NULL }, { "property_list", "a{say}", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { -/* [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, - [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 },*/ + [SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = device_updated_args, .n_arguments = 1 }, + [SIGNAL_SAMPLE_RATE_UPDATED] = { .name = "SampleRateUpdated", .arguments = sample_rate_updated_args, .n_arguments = 1 }, [SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = volume_updated_args, .n_arguments = 1 }, [SIGNAL_MUTE_UPDATED] = { .name = "MuteUpdated", .arguments = mute_updated_args, .n_arguments = 1 }, - [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*, - [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/ + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, + [SIGNAL_STREAM_EVENT] = { .name = "StreamEvent", .arguments = stream_event_args, .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) } }; static pa_dbus_interface_info stream_interface_info = { .name = PA_DBUSIFACE_STREAM_INTERFACE, - .method_handlers = /*method_handlers*/ NULL, - .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, .n_property_handlers = PROPERTY_HANDLER_MAX, .get_all_properties_cb = handle_get_all, @@ -181,6 +189,140 @@ static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userd pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } +static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *driver = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver; + + if (!driver) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u doesn't have a driver.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u doesn't have a driver.", s->source_output->index); + return; + } + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver); +} + +static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_module *owner_module = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module; + + if (!owner_module) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u doesn't have an owner module.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u doesn't have an owner module.", s->source_output->index); + return; + } + + object_path = pa_dbusiface_core_get_module_path(s->core, owner_module); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_client *client = NULL; + const char *object_path = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client; + + if (!client) { + if (s->type == STREAM_TYPE_PLAYBACK) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Playback stream %u isn't associated to any client.", s->sink_input->index); + else + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Record stream %u isn't associated to any client.", s->source_output->index); + return; + } + + object_path = pa_dbusiface_core_get_client_path(s->core, client); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); +} + +static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *device = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + device = pa_dbusiface_core_get_sink_path(s->core, s->sink); + else + device = pa_dbusiface_core_get_source_path(s->core, s->source); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + sample_format = (s->type == STREAM_TYPE_PLAYBACK) + ? s->sink_input->sample_spec.format + : s->source_output->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map; + + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels); +} + static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; dbus_uint32_t volume[PA_CHANNELS_MAX]; @@ -228,7 +370,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user return; if (n_volume_entries != stream_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Expected %u volume entries, got %u.", stream_channels, n_volume_entries); return; } @@ -281,6 +424,54 @@ static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_empty_reply(conn, msg); }; +static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint64_t buffer_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL); + else + buffer_latency = pa_source_output_get_latency(s->source_output, NULL); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency); +} + +static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + dbus_uint64_t device_latency = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_get_latency(s->sink_input, &device_latency); + else + pa_source_output_get_latency(s->source_output, &device_latency); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency); +} + +static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *resample_method = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); + else + resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method); +} + static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; @@ -296,19 +487,55 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat DBusMessage *reply = NULL; DBusMessageIter msg_iter; DBusMessageIter dict_iter; - dbus_uint32_t idx; + dbus_uint32_t idx = 0; + const char *driver = NULL; + pa_module *owner_module = NULL; + const char *owner_module_path = NULL; + pa_client *client = NULL; + const char *client_path = NULL; + const char *device = NULL; + dbus_uint32_t sample_format = 0; + pa_channel_map *channel_map = NULL; + dbus_uint32_t channels[PA_CHANNELS_MAX]; dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint64_t buffer_latency = 0; + dbus_uint64_t device_latency = 0; + const char *resample_method = NULL; unsigned i = 0; pa_assert(conn); pa_assert(msg); pa_assert(s); - idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index; if (s->type == STREAM_TYPE_PLAYBACK) { + idx = s->sink_input->index; + driver = s->sink_input->driver; + owner_module = s->sink_input->module; + client = s->sink_input->client; + device = pa_dbusiface_core_get_sink_path(s->core, s->sink); + sample_format = s->sink_input->sample_spec.format; + channel_map = &s->sink_input->channel_map; for (i = 0; i < s->volume.channels; ++i) volume[i] = s->volume.values[i]; + buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency); + resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); + } else { + idx = s->source_output->index; + driver = s->source_output->driver; + owner_module = s->source_output->module; + client = s->source_output->client; + device = pa_dbusiface_core_get_source_path(s->core, s->source); + sample_format = s->source_output->sample_spec.format; + channel_map = &s->source_output->channel_map; + buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency); + resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method); } + if (owner_module) + owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module); + if (client) + client_path = pa_dbusiface_core_get_client_path(s->core, client); + for (i = 0; i < channel_map->channels; ++i) + channels[i] = channel_map->map[i]; pa_assert_se((reply = dbus_message_new_method_return(msg))); @@ -317,11 +544,27 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + if (driver) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver); + + if (owner_module) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path); + + if (client) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); + if (s->type == STREAM_TYPE_PLAYBACK) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); } + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method); pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); @@ -329,83 +572,240 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat dbus_message_unref(reply); } +static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + const char *device = NULL; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + dbus_error_init(&error); + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device); + + if (!sink) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device); + return; + } + + if (pa_sink_input_move_to(s->sink_input, sink, TRUE) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name); + return; + } + } else { + pa_source *source = pa_dbusiface_core_get_source(s->core, device); + + if (!source) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device); + return; + } + + if (pa_source_output_move_to(s->source_output, source, TRUE) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Moving record stream %u to source %s failed.", s->source_output->index, source->name); + return; + } + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_stream *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) + pa_sink_input_kill(s->sink_input); + else + pa_source_output_kill(s->source_output); + + pa_dbus_send_empty_reply(conn, msg); +} + static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_stream *s = userdata; + DBusMessage *signal = NULL; + const char *new_device_path = NULL; + uint32_t new_sample_rate = 0; + pa_proplist *new_proplist = NULL; + unsigned i = 0; pa_assert(c); pa_assert(s); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - pa_proplist *new_proplist = NULL; - unsigned i = 0; - - pa_assert(((s->type == STREAM_TYPE_PLAYBACK) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) - || ((s->type == STREAM_TYPE_RECORD) - && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); + if ((s->type == STREAM_TYPE_PLAYBACK && idx != s->sink_input->index) + || (s->type == STREAM_TYPE_RECORD && idx != s->source_output->index)) + return; - if (s->type == STREAM_TYPE_PLAYBACK) { - pa_cvolume new_volume; - pa_bool_t new_muted = FALSE; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + pa_assert(((s->type == STREAM_TYPE_PLAYBACK) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT)) + || ((s->type == STREAM_TYPE_RECORD) + && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT))); - if (!pa_cvolume_equal(&s->volume, &new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink *new_sink = s->sink_input->sink; - s->volume = new_volume; + if (s->sink != new_sink) { + pa_sink_unref(s->sink); + s->sink = pa_sink_ref(new_sink); - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; + new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink); - pa_assert_se(signal = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, - DBUS_TYPE_INVALID)); + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); - pa_dbus_protocol_send_signal(s->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } else { + pa_source *new_source = s->source_output->source; - new_muted = pa_sink_input_get_mute(s->sink_input); + if (s->source != new_source) { + pa_source_unref(s->source); + s->source = pa_source_ref(new_source); - if (s->is_muted != new_muted) { - s->is_muted = new_muted; + new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source); - pa_assert_se(signal = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_DEVICE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID)); - pa_dbus_protocol_send_signal(s->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } + } + + new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->sample_spec.rate : s->source_output->sample_spec.rate; + + if (s->sample_rate != new_sample_rate) { + s->sample_rate = new_sample_rate; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_SAMPLE_RATE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_cvolume new_volume; + pa_bool_t new_muted = FALSE; + + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; + + s->volume = new_volume; + + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); - new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - if (!pa_proplist_equal(s->proplist, new_proplist)) { - DBusMessageIter msg_iter; + new_muted = pa_sink_input_get_mute(s->sink_input); - pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + if (s->is_muted != new_muted) { + s->is_muted = new_muted; pa_assert_se(signal = dbus_message_new_signal(s->path, PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, s->proplist); + signals[SIGNAL_MUTE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(s->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; } } + + new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist; + + if (!pa_proplist_equal(s->proplist, new_proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + +static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) { + pa_dbusiface_stream *s = slot_data; + DBusMessage *signal = NULL; + DBusMessageIter msg_iter; + const char *name = NULL; + pa_proplist *property_list = NULL; + + pa_assert(call_data); + pa_assert(s); + + if (s->type == STREAM_TYPE_PLAYBACK) { + pa_sink_input_send_event_hook_data *data = call_data; + + name = data->event; + property_list = data->data; + } else { + pa_source_output_send_event_hook_data *data = call_data; + + name = data->event; + property_list = data->data; + } + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_STREAM_EVENT].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_proplist(&msg_iter, property_list); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + + return PA_HOOK_OK; } pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) { @@ -415,14 +815,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p pa_assert(sink_input); s = pa_xnew(pa_dbusiface_stream, 1); + s->core = core; s->sink_input = pa_sink_input_ref(sink_input); s->type = STREAM_TYPE_PLAYBACK; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); + s->sink = pa_sink_ref(sink_input->sink); + s->sample_rate = sink_input->sample_spec.rate; pa_sink_input_get_volume(sink_input, &s->volume, TRUE); s->is_muted = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); + s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], + PA_HOOK_NORMAL, + send_event_cb, + s); pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); @@ -436,14 +843,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ pa_assert(source_output); s = pa_xnew(pa_dbusiface_stream, 1); + s->core = core; s->source_output = pa_source_output_ref(source_output); s->type = STREAM_TYPE_RECORD; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index); + s->source = pa_source_ref(source_output->source); + s->sample_rate = source_output->sample_spec.rate; pa_cvolume_init(&s->volume); s->is_muted = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); + s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], + PA_HOOK_NORMAL, + send_event_cb, + s); pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0); @@ -455,14 +869,18 @@ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) { pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0); - if (s->type == STREAM_TYPE_PLAYBACK) + if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink_input_unref(s->sink_input); - else + pa_sink_unref(s->sink); + } else { pa_source_output_unref(s->source_output); + pa_source_unref(s->source); + } pa_proplist_free(s->proplist); pa_dbus_protocol_unref(s->dbus_protocol); pa_subscription_free(s->subscription); + pa_hook_slot_free(s->send_event_slot); pa_xfree(s->path); pa_xfree(s); -- cgit From 8e6664f499ff3431ea51c99baf366ef11c5301a5 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:09:40 +0300 Subject: dbusiface-core: Split some overly long lines. --- src/modules/dbus/iface-core.c | 160 +++++++++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 86a8fc7b..946fdcc2 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -450,7 +450,8 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, } if (n_channels > PA_CHANNELS_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -627,7 +628,8 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_assert(c); if (!c->fallback_sink) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sinks, and therefore no fallback sink either."); return; } @@ -647,7 +649,8 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_assert(c); if (!c->fallback_sink) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sinks, and therefore no fallback sink either."); return; } @@ -713,7 +716,8 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_assert(c); if (!c->fallback_source) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sources, and therefore no fallback source either."); return; } @@ -733,7 +737,8 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_assert(c); if (!c->fallback_source) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either."); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "There are no sources, and therefore no fallback source either."); return; } @@ -1037,9 +1042,14 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat default_sample_rate = c->core->default_sample_spec.rate; cards = get_cards(c, &n_cards); sinks = get_sinks(c, &n_sinks); - fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL; + fallback_sink = c->fallback_sink + ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) + : NULL; sources = get_sources(c, &n_sources); - fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL; + fallback_source = c->fallback_source + ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, + PA_UINT32_TO_PTR(c->fallback_source->index))) + : NULL; playback_streams = get_playback_streams(c, &n_playback_streams); record_streams = get_record_streams(c, &n_record_streams); samples = get_samples(c, &n_samples); @@ -1299,7 +1309,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (n_channels > PA_CHANNELS_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX); goto finish; } @@ -1311,7 +1322,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (n_volume_entries != 0 && n_volume_entries != n_channels) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "The channels and default_volume arguments have different number of elements."); goto finish; } @@ -1337,7 +1349,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u ss.channels = n_channels; if (!pa_frame_aligned(data_length, &ss)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "The sample length in bytes doesn't align with the sample format and channels."); goto finish; } @@ -1431,15 +1444,19 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use arg_type = dbus_message_iter_get_arg_type(&msg_iter); if (arg_type != DBUS_TYPE_ARRAY) { if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too few arguments. A dictionary from strings to strings was expected."); else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", + (char) arg_type); return; } arg_type = dbus_message_iter_get_element_type(&msg_iter); if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); return; } @@ -1455,7 +1472,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); goto finish; } @@ -1472,7 +1490,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use if (arg_type == DBUS_TYPE_INVALID) pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); goto finish; } @@ -1532,7 +1551,10 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi dbus_error_init(&error); - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) { + if (!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, + DBUS_TYPE_INVALID)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); goto finish; @@ -1591,10 +1613,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_sink != new_fallback_sink) { c->fallback_sink = new_fallback_sink; - if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { + if (new_fallback_sink + && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1605,10 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if (c->fallback_source != new_fallback_source) { c->fallback_source = new_fallback_source; - if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { + if (new_fallback_source + && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); @@ -1626,7 +1654,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_CARD].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1634,7 +1664,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_card_get_path(card); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_card_free(card); @@ -1653,7 +1685,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SINK].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1665,7 +1699,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * the D-Bus sink object wasn't created yet. Now that the * object is created, let's send the fallback sink change * signal. */ - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1678,7 +1714,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1697,7 +1735,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SOURCE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1709,7 +1749,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 * point the D-Bus source object wasn't created yet. Now * that the object is created, let's send the fallback * source change signal. */ - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1722,7 +1764,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_device_get_path(device); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_device_free(device); @@ -1738,7 +1782,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_PLAYBACK_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1746,7 +1792,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1762,7 +1810,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_RECORD_STREAM].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1770,7 +1820,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_stream_get_path(stream); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_stream_free(stream); @@ -1786,7 +1838,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SAMPLE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1794,7 +1848,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_sample_get_path(sample); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_sample_free(sample); @@ -1810,7 +1866,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_MODULE].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1818,7 +1876,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_module_get_path(module); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_module_free(module); @@ -1834,7 +1894,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_CLIENT].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { @@ -1842,7 +1904,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 object_path = pa_dbusiface_client_get_path(client); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); pa_dbusiface_client_free(client); @@ -1864,7 +1928,9 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_EXTENSION].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1881,7 +1947,9 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da pa_assert(c); pa_assert(ext_name); - pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name))); + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_EXTENSION_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(c->dbus_protocol, signal); @@ -1921,8 +1989,16 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); c->fallback_sink = pa_namereg_get_default_sink(core); c->fallback_source = pa_namereg_get_default_source(core); - c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c); - c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c); + c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, + PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, + PA_HOOK_NORMAL, + extension_registered_cb, + c); + c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, + PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, + PA_HOOK_NORMAL, + extension_unregistered_cb, + c); c->memstats = pa_dbusiface_memstats_new(c, core); for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) -- cgit From 636dbc31f9f7acd76402ea01121f327d21315177 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:10:38 +0300 Subject: dbusiface-core: Use the PA_IDXSET_FOREACH macro. --- src/modules/dbus/iface-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 946fdcc2..e0aedbec 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2001,34 +2001,34 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c); c->memstats = pa_dbusiface_memstats_new(c, core); - for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx)) + PA_IDXSET_FOREACH(card, core->cards, idx) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); - for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) { + PA_IDXSET_FOREACH(sink, core->sinks, idx) { device = pa_dbusiface_device_new_sink(c, sink); pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); } - for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) { + PA_IDXSET_FOREACH(source, core->sources, idx) { device = pa_dbusiface_device_new_source(c, source); pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); } - for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx)) + PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx) pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input)); - for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx)) + PA_IDXSET_FOREACH(source_output, core->source_outputs, idx) pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output)); - for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx)) + PA_IDXSET_FOREACH(sample, core->scache, idx) pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); - for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx)) + PA_IDXSET_FOREACH(module, core->modules, idx) pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); - for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx)) + PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); -- cgit From 3de210b67120debc680d74e93118a80d360fe1e1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:13:59 +0300 Subject: dbusiface-core: Assert that _add/remove_interface calls succeed. --- src/modules/dbus/iface-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index e0aedbec..a0694d6f 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -2031,7 +2031,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); - pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c); + pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0); return c; } @@ -2087,7 +2087,7 @@ static void free_client_cb(void *p, void *userdata) { void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_assert(c); - pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name); + pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0); pa_subscription_free(c->subscription); pa_hashmap_free(c->cards, free_card_cb, NULL); -- cgit From b4e0d5d1e17409557d21dae9a770d1429e17cb15 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:18:50 +0300 Subject: dbusiface-sample: Implement the Sample D-Bus interface. --- src/modules/dbus/iface-sample.c | 467 +++++++++++++++++++++++++++++++++++++++- src/modules/dbus/iface-sample.h | 3 + 2 files changed, 468 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 44cfb031..4cffd59c 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -24,6 +24,8 @@ #endif #include +#include +#include #include #include "iface-sample.h" @@ -31,19 +33,474 @@ #define OBJECT_NAME "sample" struct pa_dbusiface_sample { + pa_dbusiface_core *core; + pa_scache_entry *sample; char *path; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_SAMPLE_FORMAT, + PROPERTY_HANDLER_SAMPLE_RATE, + PROPERTY_HANDLER_CHANNELS, + PROPERTY_HANDLER_DEFAULT_VOLUME, + PROPERTY_HANDLER_DURATION, + PROPERTY_HANDLER_BYTES, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_FORMAT] = { .property_name = "SampleFormat", .type = "u", .get_cb = handle_get_sample_format, .set_cb = NULL }, + [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, + [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, + [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au", .get_cb = handle_get_default_volume, .set_cb = NULL }, + [PROPERTY_HANDLER_DURATION] = { .property_name = "Duration", .type = "t", .get_cb = handle_get_duration, .set_cb = NULL }, + [PROPERTY_HANDLER_BYTES] = { .property_name = "Bytes", .type = "u", .get_cb = handle_get_bytes, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_PLAY, + METHOD_HANDLER_PLAY_TO_SINK, + METHOD_HANDLER_REMOVE, + METHOD_HANDLER_MAX +}; + +static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } }; +static pa_dbus_arg_info play_to_sink_args[] = { { "sink", "o", "in" }, + { "volume", "u", "in" }, + { "property_list", "a{say}", "in" } }; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_PLAY] = { + .method_name = "Play", + .arguments = play_args, + .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_play }, + [METHOD_HANDLER_PLAY_TO_SINK] = { + .method_name = "PlayToSink", + .arguments = play_to_sink_args, + .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info), + .receive_cb = handle_play_to_sink }, + [METHOD_HANDLER_REMOVE] = { + .method_name = "Remove", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_remove } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } }; +static pa_dbus_interface_info sample_interface_info = { + .name = PA_DBUSIFACE_SAMPLE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = s->sample->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name); +} + +static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t sample_format = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name); + return; + } + + sample_format = s->sample->sample_spec.format; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format); +} + +static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t sample_rate = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name); + return; + } + + sample_rate = s->sample->sample_spec.rate; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate); +} + +static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name); + return; + } + + for (i = 0; i < s->sample->channel_map.channels; ++i) + channels[i] = s->sample->channel_map.map[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels); +} + +static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t default_volume[PA_CHANNELS_MAX]; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->volume_is_set) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s doesn't have default volume stored.", s->sample->name); + return; + } + + for (i = 0; i < s->sample->volume.channels; ++i) + default_volume[i] = s->sample->volume.values[i]; + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels); +} + +static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint64_t duration = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name); + return; + } + + duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration); +} + +static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + dbus_uint32_t bytes = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!s->sample->memchunk.memblock) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name); + return; + } + + bytes = s->sample->memchunk.length; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + dbus_uint32_t idx = 0; + dbus_uint32_t sample_format = 0; + dbus_uint32_t sample_rate = 0; + dbus_uint32_t channels[PA_CHANNELS_MAX]; + dbus_uint32_t default_volume[PA_CHANNELS_MAX]; + dbus_uint64_t duration = 0; + dbus_uint32_t bytes = 0; + unsigned i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + idx = s->sample->index; + if (s->sample->memchunk.memblock) { + sample_format = s->sample->sample_spec.format; + sample_rate = s->sample->sample_spec.rate; + for (i = 0; i < s->sample->channel_map.channels; ++i) + channels[i] = s->sample->channel_map.map[i]; + duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec); + bytes = s->sample->memchunk.length; + } + if (s->sample->volume_is_set) { + for (i = 0; i < s->sample->volume.channels; ++i) + default_volume[i] = s->sample->volume.values[i]; + } + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name); + + if (s->sample->memchunk.memblock) { + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels); + } + + if (s->sample->volume_is_set) + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_VOLUME].property_name, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels); + + if (s->sample->memchunk.memblock) { + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes); + } + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessageIter msg_iter; + dbus_uint32_t volume = 0; + pa_proplist *property_list = NULL; + pa_sink *sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (volume > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + + if (!(sink = pa_namereg_get_default_sink(s->sample->core))) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, + "Can't play sample %s, because there are no sinks available.", s->sample->name); + goto finish; + } + + if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); + goto finish; + } + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessageIter msg_iter; + const char *sink_path = NULL; + dbus_uint32_t volume = 0; + pa_proplist *property_list = NULL; + pa_sink *sink = NULL; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) + return; + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path); + goto finish; + } + + if (volume > PA_VOLUME_MAX) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + goto finish; + } + + if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); + goto finish; + } + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_sample *s = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(s); + + if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name); + return; + } + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_sample *s = userdata; + DBusMessage *signal = NULL; + + pa_assert(c); + pa_assert(s); + + if (idx != s->sample->index) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; + + if (!pa_proplist_equal(s->proplist, s->sample->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(s->proplist, PA_UPDATE_SET, s->sample->proplist); + + pa_assert_se(signal = dbus_message_new_signal(s->path, + PA_DBUSIFACE_SAMPLE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, s->proplist); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) { - pa_dbusiface_sample *s; + pa_dbusiface_sample *s = NULL; pa_assert(core); pa_assert(sample); - s = pa_xnew(pa_dbusiface_sample, 1); + s = pa_xnew0(pa_dbusiface_sample, 1); + s->core = core; s->sample = sample; s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index); + s->proplist = pa_proplist_copy(sample->proplist); + s->dbus_protocol = pa_dbus_protocol_get(sample->core); + s->subscription = pa_subscription_new(sample->core, PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, subscription_cb, s); + + pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0); return s; } @@ -51,6 +508,12 @@ pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_ void pa_dbusiface_sample_free(pa_dbusiface_sample *s) { pa_assert(s); + pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0); + + pa_proplist_free(s->proplist); + pa_dbus_protocol_unref(s->dbus_protocol); + pa_subscription_free(s->subscription); + pa_xfree(s->path); pa_xfree(s); } diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h index 1b82648d..f1947ce8 100644 --- a/src/modules/dbus/iface-sample.h +++ b/src/modules/dbus/iface-sample.h @@ -29,9 +29,12 @@ */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_SAMPLE_INTERFACE PA_DBUS_CORE_INTERFACE ".Sample" + typedef struct pa_dbusiface_sample pa_dbusiface_sample; pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample); -- cgit From 179f849c0869982c6fc97d6bbf9083203c586b17 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 19 Aug 2009 09:54:09 +0300 Subject: dbusifaca-device: Adapt to the changed pa_sink_get/set_volume() interface. --- src/modules/dbus/iface-device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 8dc0b2c4..8f719bce 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -444,7 +444,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user } if (d->type == DEVICE_TYPE_SINK) - pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE); + pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE); else pa_source_set_volume(d->source, &new_vol, TRUE); @@ -1099,7 +1099,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE))); new_volume = (d->type == DEVICE_TYPE_SINK) - ? pa_sink_get_volume(d->sink, FALSE, FALSE) + ? pa_sink_get_volume(d->sink, FALSE) : pa_source_get_volume(d->source, FALSE); if (!pa_cvolume_equal(&d->volume, new_volume)) { @@ -1212,7 +1212,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si d->sink = pa_sink_ref(sink); d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); - d->volume = *pa_sink_get_volume(sink, FALSE, FALSE); + d->volume = *pa_sink_get_volume(sink, FALSE); d->is_muted = pa_sink_get_mute(sink, FALSE); d->sink_state = pa_sink_get_state(sink); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- cgit From 1e4e26c87fc4f74c9805cc084c88783558acb418 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:22:32 +0300 Subject: proplist: Return early from pa_proplist_equal() if the pointers are equal. --- src/pulse/proplist.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 4f0d6a6d..8b5b6538 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -690,6 +690,9 @@ int pa_proplist_equal(pa_proplist *a, pa_proplist *b) { pa_assert(a); pa_assert(b); + if (a == b) + return 1; + if (pa_proplist_size(a) != pa_proplist_size(b)) return 0; -- cgit From 187c4f32cffb75787213e8692b27d0a3c736b95e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:23:49 +0300 Subject: proplist: A couple of documentation fixes. --- src/pulse/proplist.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index a585944a..9effc861 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -337,7 +337,7 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep); * readable string. \since 0.9.15 */ pa_proplist *pa_proplist_from_string(const char *str); - /** Returns 1 if an entry for the specified key is existant in the +/** Returns 1 if an entry for the specified key is existant in the * property list. \since 0.9.11 */ int pa_proplist_contains(pa_proplist *p, const char *key); @@ -354,7 +354,8 @@ unsigned pa_proplist_size(pa_proplist *t); /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */ int pa_proplist_isempty(pa_proplist *t); -/** Return non-zero when a and b have the same keys and values. */ +/** Return non-zero when a and b have the same keys and values. + * \since 0.9.16 */ int pa_proplist_equal(pa_proplist *a, pa_proplist *b); PA_C_DECL_END -- cgit From 7049b3c5bc351b9ea7ed932baa5bf7ccc1b67347 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:24:59 +0300 Subject: modargs: New function: pa_modargs_iterate(). --- src/pulsecore/modargs.c | 10 ++++++++++ src/pulsecore/modargs.h | 9 +++++++++ 2 files changed, 19 insertions(+) (limited to 'src') diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index c7d734d9..e78cdb9a 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa return 0; } + +const char *pa_modargs_iterate(pa_modargs *ma, void **state) { + pa_hashmap *map = (pa_hashmap*) ma; + struct entry *e; + + if (!(e = pa_hashmap_iterate(map, state, NULL))) + return NULL; + + return e->key; +} diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h index b3125b10..1ed66e9a 100644 --- a/src/pulsecore/modargs.h +++ b/src/pulsecore/modargs.h @@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m); +/* Iterate through the module argument list. The user should allocate a + * state variable of type void* and initialize it with NULL. A pointer + * to this variable should then be passed to pa_modargs_iterate() + * which should be called in a loop until it returns NULL which + * signifies EOL. On each invication this function will return the + * key string for the next entry. The keys in the argument list do not + * have any particular order. */ +const char *pa_modargs_iterate(pa_modargs *ma, void **state); + #endif -- cgit From 57886ff34ac39dae709cb50cbc7fbbf817df534b Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:26:13 +0300 Subject: dbus-protocol: Print a debug line whenever interfaces are unregistered. --- src/pulsecore/protocol-dbus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index cf561c83..d1e19ff3 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -694,6 +694,8 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con update_introspection(obj_entry); + pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path); + pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); -- cgit From 3025645b0b58f476f6404f2aed3b61a633794d10 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 24 Aug 2009 14:27:14 +0300 Subject: dbusiface-module: Implement the Module D-Bus interface. --- src/modules/dbus/iface-core.c | 6 +- src/modules/dbus/iface-module.c | 275 +++++++++++++++++++++++++++++++++++++++- src/modules/dbus/iface-module.h | 5 +- 3 files changed, 279 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index a0694d6f..9e8f775e 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1509,7 +1509,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use goto finish; } - dbus_module = pa_dbusiface_module_new(c, module); + dbus_module = pa_dbusiface_module_new(module); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module); object_path = pa_dbusiface_module_get_path(dbus_module); @@ -1860,7 +1860,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx)); + module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx)); pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); } @@ -2026,7 +2026,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample)); PA_IDXSET_FOREACH(module, core->modules, idx) - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module)); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module)); PA_IDXSET_FOREACH(client, core->clients, idx) pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client)); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 788d104b..63786dae 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -24,6 +24,8 @@ #endif #include +#include +#include #include #include "iface-module.h" @@ -33,17 +35,278 @@ struct pa_dbusiface_module { pa_module *module; char *path; + pa_proplist *proplist; + + pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; -pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) { - pa_dbusiface_module *m; +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); + +static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata); + +enum property_handler_index { + PROPERTY_HANDLER_INDEX, + PROPERTY_HANDLER_NAME, + PROPERTY_HANDLER_ARGUMENTS, + PROPERTY_HANDLER_USAGE_COUNTER, + PROPERTY_HANDLER_PROPERTY_LIST, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { + [PROPERTY_HANDLER_INDEX] = { .property_name = "Index", .type = "u", .get_cb = handle_get_index, .set_cb = NULL }, + [PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_get_name, .set_cb = NULL }, + [PROPERTY_HANDLER_ARGUMENTS] = { .property_name = "Arguments", .type = "a{ss}", .get_cb = handle_get_arguments, .set_cb = NULL }, + [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u", .get_cb = handle_get_usage_counter, .set_cb = NULL }, + [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } +}; + +enum method_handler_index { + METHOD_HANDLER_UNLOAD, + METHOD_HANDLER_MAX +}; + +static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { + [METHOD_HANDLER_UNLOAD] = { + .method_name = "Unload", + .arguments = NULL, + .n_arguments = 0, + .receive_cb = handle_unload } +}; + +enum signal_index { + SIGNAL_PROPERTY_LIST_UPDATED, + SIGNAL_MAX +}; + +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; + +static pa_dbus_signal_info signals[SIGNAL_MAX] = { + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } +}; + +static pa_dbus_interface_info module_interface_info = { + .name = PA_DBUSIFACE_MODULE_INTERFACE, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, + .property_handlers = property_handlers, + .n_property_handlers = PROPERTY_HANDLER_MAX, + .get_all_properties_cb = handle_get_all, + .signals = signals, + .n_signals = SIGNAL_MAX +}; + +static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + dbus_uint32_t idx = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + idx = m->module->index; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); +} + +static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name); +} + +static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) { + pa_modargs *ma = NULL; + DBusMessageIter variant_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + void *state = NULL; + const char *key = NULL; + const char *value = NULL; + + pa_assert(iter); + pa_assert(m); + + pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL)); + + pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter)); + pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter)); + + for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) { + pa_assert_se(value = pa_modargs_get_value(ma, key, NULL)); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value)); + + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + } + + pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter)); + pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter)); + + pa_modargs_free(ma); +} + +static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_assert_se(reply = dbus_message_new_method_return(msg)); + dbus_message_iter_init_append(reply, &msg_iter); + append_modargs_variant(&msg_iter, m); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + int real_counter_value = -1; + dbus_uint32_t usage_counter = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) { + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, + "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name); + return; + } + + usage_counter = real_counter_value; + + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter); +} + +static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist); +} + +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + DBusMessageIter dict_entry_iter; + dbus_uint32_t idx = 0; + int real_counter_value = -1; + dbus_uint32_t usage_counter = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + idx = m->module->index; + if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0) + usage_counter = real_counter_value; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name); + + pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)); + pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name)); + append_modargs_variant(&dict_entry_iter, m); + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); + + if (real_counter_value >= 0) + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter); + + pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + + dbus_message_unref(reply); +} + +static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_module *m = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(m); + + if (m->module->core->disallow_module_loading) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading."); + return; + } + + pa_module_unload_request(m->module, FALSE); + + pa_dbus_send_empty_reply(conn, msg); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_module *m = userdata; pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); + pa_assert(m); + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { + DBusMessage *signal = NULL; + + if (!pa_proplist_equal(m->proplist, m->module->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); + + pa_assert_se(signal = dbus_message_new_signal(m->path, + PA_DBUSIFACE_MODULE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, m->proplist); + + pa_dbus_protocol_send_signal(m->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } + } +} + +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) { + pa_dbusiface_module *m; + pa_assert(module); - m = pa_xnew(pa_dbusiface_module, 1); + m = pa_xnew0(pa_dbusiface_module, 1); m->module = module; m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index); + m->proplist = pa_proplist_copy(module->proplist); + m->dbus_protocol = pa_dbus_protocol_get(module->core); + m->subscription = pa_subscription_new(module->core, PA_SUBSCRIPTION_MASK_MODULE, subscription_cb, m); + + pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0); return m; } @@ -51,6 +314,12 @@ pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module void pa_dbusiface_module_free(pa_dbusiface_module *m) { pa_assert(m); + pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0); + + pa_proplist_free(m->proplist); + pa_dbus_protocol_unref(m->dbus_protocol); + pa_subscription_free(m->subscription); + pa_xfree(m->path); pa_xfree(m); } diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h index c4f29210..68ca1de5 100644 --- a/src/modules/dbus/iface-module.h +++ b/src/modules/dbus/iface-module.h @@ -29,12 +29,15 @@ */ #include +#include #include "iface-core.h" +#define PA_DBUSIFACE_MODULE_INTERFACE PA_DBUS_CORE_INTERFACE ".Module" + typedef struct pa_dbusiface_module pa_dbusiface_module; -pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module); +pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module); void pa_dbusiface_module_free(pa_dbusiface_module *m); const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m); -- cgit From 3e0e685a8c6372fb67efad5ffa54583dc757203e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:12:42 +0300 Subject: dbus: Save one level of identation by returning early. --- src/modules/dbus/iface-card.c | 53 +++++++++++++++++++++-------------------- src/modules/dbus/iface-module.c | 28 +++++++++++----------- 2 files changed, 41 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 5a797948..fce44ff7 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -459,45 +459,46 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_card *c = userdata; + DBusMessage *signal = NULL; pa_assert(core); pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); pa_assert(c); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; - if (c->active_profile != c->card->active_profile) { - const char *object_path; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - c->active_profile = c->card->active_profile; - object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); + if (c->active_profile != c->card->active_profile) { + const char *object_path; - pa_assert_se(signal = dbus_message_new_signal(c->path, - PA_DBUSIFACE_CARD_INTERFACE, - signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + c->active_profile = c->card->active_profile; + object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name)); - pa_dbus_protocol_send_signal(c->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - if (!pa_proplist_equal(c->proplist, c->card->proplist)) { - DBusMessageIter msg_iter; + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } - pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); + if (!pa_proplist_equal(c->proplist, c->card->proplist)) { + DBusMessageIter msg_iter; - pa_assert_se(signal = dbus_message_new_signal(c->path, - PA_DBUSIFACE_CARD_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, c->proplist); + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist); - pa_dbus_protocol_send_signal(c->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CARD_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index 63786dae..e3882fc9 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -268,29 +268,29 @@ static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_module *m = userdata; + DBusMessage *signal = NULL; pa_assert(core); pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); pa_assert(m); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - DBusMessage *signal = NULL; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; - if (!pa_proplist_equal(m->proplist, m->module->proplist)) { - DBusMessageIter msg_iter; + if (!pa_proplist_equal(m->proplist, m->module->proplist)) { + DBusMessageIter msg_iter; - pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); + pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist); - pa_assert_se(signal = dbus_message_new_signal(m->path, - PA_DBUSIFACE_MODULE_INTERFACE, - signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); - dbus_message_iter_init_append(signal, &msg_iter); - pa_dbus_append_proplist(&msg_iter, m->proplist); + pa_assert_se(signal = dbus_message_new_signal(m->path, + PA_DBUSIFACE_MODULE_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, m->proplist); - pa_dbus_protocol_send_signal(m->dbus_protocol, signal); - dbus_message_unref(signal); - signal = NULL; - } + pa_dbus_protocol_send_signal(m->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } -- cgit From edf80104e32830a3d4ec58f88f9a092c32203d8a Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:17:35 +0300 Subject: dbus: Make sure that subscription callbacks don't try to access removed objects. --- src/modules/dbus/iface-card.c | 4 + src/modules/dbus/iface-core.c | 202 ++++++++++++++++++++++++++-------------- src/modules/dbus/iface-module.c | 5 + src/modules/dbus/iface-sample.c | 4 +- 4 files changed, 143 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index fce44ff7..7e37f8bb 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -465,6 +465,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD); pa_assert(c); + /* We can't use idx != c->card->index, because the c->card pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->cards, idx) != c->card) + return; if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) return; diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 9e8f775e..c54a3b7a 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -1592,12 +1592,12 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage * static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_dbusiface_core *c = userdata; - pa_dbusiface_card *card = NULL; - pa_dbusiface_device *device = NULL; - pa_dbusiface_stream *stream = NULL; - pa_dbusiface_sample *sample = NULL; - pa_dbusiface_module *module = NULL; - pa_dbusiface_client *client = NULL; + pa_dbusiface_card *card_iface = NULL; + pa_dbusiface_device *device_iface = NULL; + pa_dbusiface_stream *stream_iface = NULL; + pa_dbusiface_sample *sample_iface = NULL; + pa_dbusiface_module *module_iface = NULL; + pa_dbusiface_client *client_iface = NULL; DBusMessage *signal = NULL; const char *object_path = NULL; pa_sink *new_fallback_sink = NULL; @@ -1611,11 +1611,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 new_fallback_source = pa_namereg_get_default_source(core); if (c->fallback_sink != new_fallback_sink) { - c->fallback_sink = new_fallback_sink; + if (c->fallback_sink) + pa_sink_unref(c->fallback_sink); + c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL; if (new_fallback_sink - && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { - object_path = pa_dbusiface_device_get_path(device); + && (device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) { + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1628,11 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } if (c->fallback_source != new_fallback_source) { - c->fallback_source = new_fallback_source; + if (c->fallback_source) + pa_source_unref(c->fallback_source); + c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL; if (new_fallback_source - && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { - object_path = pa_dbusiface_device_get_path(device); + && (device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) { + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1647,12 +1651,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { - card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx)); - pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card); + if (!(card_iface = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) { + pa_card *card = NULL; + + if (!(card = pa_idxset_get_by_index(core->cards, idx))) + return; /* The card was removed immediately after creation. */ + + card_iface = pa_dbusiface_card_new(c, card); + pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card_iface); } - object_path = pa_dbusiface_card_get_path(card); + object_path = pa_dbusiface_card_get_path(card_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1660,30 +1669,34 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))); + if (!(card_iface = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_card_get_path(card); + object_path = pa_dbusiface_card_get_path(card_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_card_free(card); + pa_dbusiface_card_free(card_iface); } break; case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx); + pa_sink *sink = NULL; - if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_sink(c, sink); - pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device); + if (!(sink = pa_idxset_get_by_index(core->sinks, idx))) + return; /* The sink was removed immediately after creation. */ + + if (!(device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) { + device_iface = pa_dbusiface_device_new_sink(c, sink); + pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device_iface); + pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device_iface), device_iface); } - object_path = pa_dbusiface_device_get_path(device); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1710,8 +1723,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))); - object_path = pa_dbusiface_device_get_path(device); + if (!(device_iface = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) + return; + + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path)); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1719,7 +1734,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 signals[SIGNAL_SINK_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_device_free(device); + pa_dbusiface_device_free(device_iface); } break; @@ -1727,13 +1742,16 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { pa_source *source = pa_idxset_get_by_index(core->sources, idx); - if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { - device = pa_dbusiface_device_new_source(c, source); - pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device); - pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device); + if (!(source = pa_idxset_get_by_index(core->sources, idx))) + return; /* The source was removed immediately after creation. */ + + if (!(device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) { + device_iface = pa_dbusiface_device_new_source(c, source); + pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device_iface); + pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device_iface), device_iface); } - object_path = pa_dbusiface_device_get_path(device); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1760,8 +1778,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 } } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))); - object_path = pa_dbusiface_device_get_path(device); + if (!(device_iface = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) + return; + + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path)); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1769,18 +1789,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 signals[SIGNAL_SOURCE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_device_free(device); + pa_dbusiface_device_free(device_iface); } break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx)); - pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream); + pa_sink_input *sink_input = NULL; + + if (!(sink_input = pa_idxset_get_by_index(core->sink_inputs, idx))) + return; /* The sink input was removed immediately after creation. */ + + if (!(stream_iface = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) { + stream_iface = pa_dbusiface_stream_new_playback(c, sink_input); + pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream_iface); } - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1788,27 +1813,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))); + if (!(stream_iface = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_stream_free(stream); + pa_dbusiface_stream_free(stream_iface); } break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { - stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx)); - pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream); + pa_source_output *source_output = NULL; + + if (!(source_output = pa_idxset_get_by_index(core->source_outputs, idx))) + return; /* The source output was removed immediately after creation. */ + + if (!(stream_iface = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) { + stream_iface = pa_dbusiface_stream_new_record(c, source_output); + pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream_iface); } - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1816,27 +1847,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))); + if (!(stream_iface = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_stream_get_path(stream); + object_path = pa_dbusiface_stream_get_path(stream_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_stream_free(stream); + pa_dbusiface_stream_free(stream_iface); } break; case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { - sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx)); - pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample); + pa_scache_entry *sample = NULL; + + if (!(sample = pa_idxset_get_by_index(core->scache, idx))) + return; /* The sample was removed immediately after creation. */ + + if (!(sample_iface = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) { + sample_iface = pa_dbusiface_sample_new(c, sample); + pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample_iface); } - object_path = pa_dbusiface_sample_get_path(sample); + object_path = pa_dbusiface_sample_get_path(sample_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1844,27 +1881,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))); + if (!(sample_iface = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_sample_get_path(sample); + object_path = pa_dbusiface_sample_get_path(sample_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_sample_free(sample); + pa_dbusiface_sample_free(sample_iface); } break; case PA_SUBSCRIPTION_EVENT_MODULE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { - module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx)); - pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module); + pa_module *module = NULL; + + if (!(module = pa_idxset_get_by_index(core->modules, idx))) + return; /* The module was removed immediately after creation. */ + + if (!(module_iface = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) { + module_iface = pa_dbusiface_module_new(module); + pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module_iface); } - object_path = pa_dbusiface_module_get_path(module); + object_path = pa_dbusiface_module_get_path(module_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1872,27 +1915,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))); + if (!(module_iface = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_module_get_path(module); + object_path = pa_dbusiface_module_get_path(module_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_module_free(module); + pa_dbusiface_module_free(module_iface); } break; case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { - client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx)); - pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client); + pa_client *client = NULL; + + if (!(client = pa_idxset_get_by_index(core->clients, idx))) + return; /* The client was removed immediately after creation. */ + + if (!(client_iface = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) { + client_iface = pa_dbusiface_client_new(c, client); + pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client_iface); } - object_path = pa_dbusiface_client_get_path(client); + object_path = pa_dbusiface_client_get_path(client_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, @@ -1900,16 +1949,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))); + if (!(client_iface = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx)))) + return; - object_path = pa_dbusiface_client_get_path(client); + object_path = pa_dbusiface_client_get_path(client_iface); pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name))); pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - pa_dbusiface_client_free(client); + pa_dbusiface_client_free(client_iface); } break; } @@ -2001,6 +2051,11 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c); c->memstats = pa_dbusiface_memstats_new(c, core); + if (c->fallback_sink) + pa_sink_ref(c->fallback_sink); + if (c->fallback_source) + pa_source_ref(c->fallback_source); + PA_IDXSET_FOREACH(card, core->cards, idx) pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card)); @@ -2104,6 +2159,11 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) { pa_hook_slot_free(c->extension_unregistered_slot); pa_dbusiface_memstats_free(c->memstats); + if (c->fallback_sink) + pa_sink_unref(c->fallback_sink); + if (c->fallback_source) + pa_source_unref(c->fallback_source); + pa_dbus_protocol_unref(c->dbus_protocol); pa_core_unref(c->core); diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c index e3882fc9..e8aea50f 100644 --- a/src/modules/dbus/iface-module.c +++ b/src/modules/dbus/iface-module.c @@ -274,6 +274,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE); pa_assert(m); + /* We can't use idx != m->module->index, because the m->module pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->modules, idx) != m->module) + return; + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) return; diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 4cffd59c..7147be14 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -463,7 +463,9 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t pa_assert(c); pa_assert(s); - if (idx != s->sample->index) + /* We can't use idx != s->sample->index, because the s->sample pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(c->scache, idx) != s->sample) return; if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) -- cgit From 11fcc8c85f15dba8e78dffb88b3d0d04ebc329e1 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:19:11 +0300 Subject: dbusiface-stream: Only send stream event signals from the right D-Bus objects. --- src/modules/dbus/iface-stream.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 354ca6eb..183625ba 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -786,11 +786,17 @@ static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *sl if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink_input_send_event_hook_data *data = call_data; + if (data->sink_input != s->sink_input) + return PA_HOOK_OK; + name = data->event; property_list = data->data; } else { pa_source_output_send_event_hook_data *data = call_data; + if (data->source_output != s->source_output) + return PA_HOOK_OK; + name = data->event; property_list = data->data; } -- cgit From 219f7508f6420f94ad8c426c6aa3dc79df246f36 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Wed, 26 Aug 2009 14:20:26 +0300 Subject: dbus: Finish the Client D-Bus interface. --- src/modules/dbus/iface-client.c | 163 ++++++++++++++++++++++++++++---- src/modules/dbus/module-dbus-protocol.c | 31 +++++- 2 files changed, 176 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 587d5c42..a68259a9 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -39,8 +39,10 @@ struct pa_dbusiface_client { pa_client *client; char *path; + pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; + pa_subscription *subscription; }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -52,9 +54,9 @@ static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); -/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/ +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata); enum property_handler_index { PROPERTY_HANDLER_INDEX, @@ -75,7 +77,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL } }; -/*enum method_handler_index { +enum method_handler_index { METHOD_HANDLER_KILL, METHOD_HANDLER_UPDATE_PROPERTIES, METHOD_HANDLER_REMOVE_PROPERTIES, @@ -83,7 +85,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { }; static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } }; -static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } }; +static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { [METHOD_HANDLER_KILL] = { @@ -93,14 +95,14 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { .receive_cb = handle_kill }, [METHOD_HANDLER_UPDATE_PROPERTIES] = { .method_name = "UpdateProperties", - .arguments = update_propertes_args, + .arguments = update_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_update_properties }, [METHOD_HANDLER_REMOVE_PROPERTIES] = { .method_name = "RemoveProperties", - .arguments = remove_propertes_args, + .arguments = remove_properties_args, .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), - .receive_cb = handle_update_properties } + .receive_cb = handle_remove_properties } }; enum signal_index { @@ -109,23 +111,25 @@ enum signal_index { SIGNAL_MAX }; -static pa_dbus_arg_info active_profile_updated_args[] = { { "profile", "o", NULL } }; -static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } }; +static pa_dbus_arg_info client_event_args[] = { { "name", "s", NULL }, + { "property_list", "a{say}", NULL } }; static pa_dbus_signal_info signals[SIGNAL_MAX] = { - [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 }, - [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 } -};*/ + [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }, + /* ClientEvent is sent from module-dbus-protocol.c. */ + [SIGNAL_CLIENT_EVENT] = { .name = "ClientEvent", .arguments = client_event_args, .n_arguments = 1 } +}; static pa_dbus_interface_info client_interface_info = { .name = PA_DBUSIFACE_CLIENT_INTERFACE, - .method_handlers = /*method_handlers*/ NULL, - .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0, + .method_handlers = method_handlers, + .n_method_handlers = METHOD_HANDLER_MAX, .property_handlers = property_handlers, .n_property_handlers = PROPERTY_HANDLER_MAX, .get_all_properties_cb = handle_get_all, - .signals = /*signals*/ NULL, - .n_signals = /*SIGNAL_MAX*/ 0 + .signals = signals, + .n_signals = SIGNAL_MAX }; static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) { @@ -304,6 +308,131 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_xfree(record_streams); } +static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_connection_ref(conn); + + pa_client_kill(c->client); + + pa_dbus_send_empty_reply(conn, msg); + + dbus_connection_unref(conn); +} + +static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessageIter msg_iter; + pa_proplist *property_list = NULL; + dbus_uint32_t update_mode = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_iter_init(msg, &msg_iter)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); + return; + } + + if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) + return; + + if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0) + goto finish; + + if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); + goto finish; + } + + pa_client_update_proplist(c->client, update_mode, property_list); + + pa_dbus_send_empty_reply(conn, msg); + +finish: + if (property_list) + pa_proplist_free(property_list); +} + +static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) { + pa_dbusiface_client *c = userdata; + char **keys = NULL; + int n_keys = 0; + DBusError error; + pa_bool_t changed = FALSE; + int i = 0; + + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + + dbus_error_init(&error); + + if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); + return; + } + + if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) { + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + + for (i = 0; i < n_keys; ++i) + changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; + + pa_dbus_send_empty_reply(conn, msg); + + if (changed) + pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + + dbus_free_string_array(keys); +} + +static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + pa_dbusiface_client *c = userdata; + DBusMessage *signal = NULL; + + pa_assert(core); + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CLIENT); + pa_assert(c); + + /* We can't use idx != c->client->index, because the c->client pointer may + * be stale at this point. */ + if (pa_idxset_get_by_index(core->clients, idx) != c->client) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) + return; + + if (!pa_proplist_equal(c->proplist, c->client->proplist)) { + DBusMessageIter msg_iter; + + pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist); + + pa_assert_se(signal = dbus_message_new_signal(c->path, + PA_DBUSIFACE_CLIENT_INTERFACE, + signals[SIGNAL_PROPERTY_LIST_UPDATED].name)); + dbus_message_iter_init_append(signal, &msg_iter); + pa_dbus_append_proplist(&msg_iter, c->proplist); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; + } +} + pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) { pa_dbusiface_client *c = NULL; @@ -314,7 +443,9 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client c->core = core; c->client = client; c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index); + c->proplist = pa_proplist_copy(client->proplist); c->dbus_protocol = pa_dbus_protocol_get(client->core); + c->subscription = pa_subscription_new(client->core, PA_SUBSCRIPTION_MASK_CLIENT, subscription_cb, c); pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0); diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c index 807d32da..11064c33 100644 --- a/src/modules/dbus/module-dbus-protocol.c +++ b/src/modules/dbus/module-dbus-protocol.c @@ -40,6 +40,7 @@ #include #include +#include "iface-client.h" #include "iface-core.h" #include "module-dbus-protocol-symdef.h" @@ -117,10 +118,36 @@ static void client_kill_cb(pa_client *c) { conn = c->userdata; connection_free(conn); + c->userdata = NULL; pa_log_info("Connection killed."); } +/* Called from pa_client_send_event(). */ +static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) { + struct connection *conn = NULL; + DBusMessage *signal = NULL; + DBusMessageIter msg_iter; + + pa_assert(c); + pa_assert(name); + pa_assert(data); + pa_assert(c->userdata); + + conn = c->userdata; + + pa_assert_se(signal = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c), + PA_DBUSIFACE_CLIENT_INTERFACE, + "ClientEvent")); + dbus_message_iter_init_append(signal, &msg_iter); + pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name)); + pa_dbus_append_proplist(&msg_iter, data); + + pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal, NULL)); + dbus_message_unref(signal); +} + +/* Called by D-Bus at the authentication phase. */ static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) { pa_log_debug("Allowing connection by user %lu.", uid); @@ -140,7 +167,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne pa_client_new_data_init(&new_data); new_data.module = s->userdata->module; new_data.driver = __FILE__; - pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */ + pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); client = pa_client_new(s->userdata->module->core, &new_data); pa_client_new_data_done(&new_data); @@ -162,7 +189,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne c->client = client; c->client->kill = client_kill_cb; - c->client->send_event = NULL; /* TODO: Implement this. */ + c->client->send_event = client_send_event_cb; c->client->userdata = c; pa_idxset_put(s->userdata->connections, c, NULL); -- cgit From 0e096632c53b746b9f4b4c0249d9e5a18c1c543d Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Sun, 30 Aug 2009 19:52:22 +0300 Subject: dbus: Do message argument type checking early, centrally. --- src/modules/dbus/iface-card.c | 19 +- src/modules/dbus/iface-client.c | 17 +- src/modules/dbus/iface-core.c | 247 ++++++++++---------------- src/modules/dbus/iface-device.c | 56 +++--- src/modules/dbus/iface-sample.c | 24 +-- src/modules/dbus/iface-stream.c | 33 ++-- src/modules/module-stream-restore.c | 120 ++++--------- src/pulsecore/dbus-util.c | 193 +++------------------ src/pulsecore/dbus-util.h | 42 +++-- src/pulsecore/protocol-dbus.c | 336 +++++++++++++++++++++++------------- src/pulsecore/protocol-dbus.h | 26 ++- 11 files changed, 471 insertions(+), 642 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c index 7e37f8bb..1714df36 100644 --- a/src/modules/dbus/iface-card.c +++ b/src/modules/dbus/iface-card.c @@ -43,7 +43,7 @@ static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userd static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -322,7 +322,7 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile); } -static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_card *c = userdata; const char *new_active_path; pa_dbusiface_card_profile *new_active; @@ -330,11 +330,9 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) - return; - if (!c->active_profile) { pa_assert(pa_hashmap_isempty(c->profiles)); @@ -344,6 +342,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo return; } + dbus_message_iter_get_basic(iter, &new_active_path); + if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path); return; @@ -430,7 +430,6 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_card *c = userdata; - DBusError error; const char *profile_name = NULL; pa_dbusiface_card_profile *profile = NULL; const char *profile_path = NULL; @@ -439,13 +438,7 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)); if (!(profile = pa_hashmap_get(c->profiles, profile_name))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name); diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index a68259a9..54550d2c 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -339,16 +339,12 @@ static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, voi return; } - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0) - goto finish; + dbus_message_iter_get_basic(&msg_iter, &update_mode); if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode); @@ -368,7 +364,6 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi pa_dbusiface_client *c = userdata; char **keys = NULL; int n_keys = 0; - DBusError error; pa_bool_t changed = FALSE; int i = 0; @@ -376,18 +371,12 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) { pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client."); return; } - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)); for (i = 0; i < n_keys; ++i) changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0; diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index c54a3b7a..0507ac9c 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -57,18 +57,18 @@ static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *us static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -428,30 +428,32 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, pa_xfree(default_channels); } -static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; + DBusMessageIter array_iter; pa_channel_map new_channel_map; - dbus_uint32_t *default_channels; - unsigned n_channels; + const dbus_uint32_t *default_channels; + int n_channels; unsigned i; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); pa_channel_map_init(&new_channel_map); - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels); if (n_channels <= 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array."); return; } - if (n_channels > PA_CHANNELS_MAX) { + if (n_channels > (int) PA_CHANNELS_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); + "Too many channels: %i. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX); return; } @@ -485,16 +487,16 @@ static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage * pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format); } -static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; dbus_uint32_t default_sample_format; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0) - return; + dbus_message_iter_get_basic(iter, &default_sample_format); if (default_sample_format >= PA_SAMPLE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); @@ -519,16 +521,16 @@ static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *ms pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate); } -static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; dbus_uint32_t default_sample_rate; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0) - return; + dbus_message_iter_get_basic(iter, &default_sample_rate); if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate."); @@ -639,13 +641,14 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); } -static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; pa_dbusiface_device *fallback_sink; const char *object_path; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); if (!c->fallback_sink) { @@ -654,8 +657,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi return; } - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) - return; + dbus_message_iter_get_basic(iter, &object_path); if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path); @@ -727,13 +729,14 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path); } -static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_core *c = userdata; pa_dbusiface_device *fallback_source; const char *object_path; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(c); if (!c->fallback_source) { @@ -742,8 +745,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v return; } - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0) - return; + dbus_message_iter_get_basic(iter, &object_path); if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path); @@ -1117,19 +1119,12 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void pa_card *card; pa_dbusiface_card *dbus_card; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)); if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card."); @@ -1149,19 +1144,12 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void pa_sink *sink; pa_dbusiface_device *dbus_sink; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)); if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name); @@ -1181,19 +1169,12 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo pa_source *source; pa_dbusiface_device *dbus_source; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)); if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name); @@ -1213,19 +1194,12 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo pa_scache_entry *sample; pa_dbusiface_sample *dbus_sample; const char *object_path; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)); if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample."); @@ -1242,17 +1216,18 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; DBusMessageIter msg_iter; + DBusMessageIter array_iter; const char *name; dbus_uint32_t sample_format; dbus_uint32_t sample_rate; const dbus_uint32_t *channels; - unsigned n_channels; + int n_channels; const dbus_uint32_t *default_volume; - unsigned n_volume_entries; + int n_volume_entries; pa_proplist *property_list; const uint8_t *data; - unsigned data_length; - unsigned i; + int data_length; + int i; pa_sample_spec ss; pa_channel_map map; pa_memchunk chunk; @@ -1267,31 +1242,29 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u chunk.memblock = NULL; - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sample_format); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sample_rate); - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &channels, &n_channels); - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &default_volume, &n_volume_entries); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; - if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0) - goto finish; + dbus_message_iter_recurse(&msg_iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &data, &data_length); if (sample_format >= PA_SAMPLE_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format."); @@ -1303,14 +1276,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u goto finish; } - if (n_channels == 0) { + if (n_channels <= 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map."); goto finish; } - if (n_channels > PA_CHANNELS_MAX) { + if (n_channels > (int) PA_CHANNELS_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX); + "Too many channels: %i. The maximum is %u.", n_channels, PA_CHANNELS_MAX); goto finish; } @@ -1323,13 +1296,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u if (n_volume_entries != 0 && n_volume_entries != n_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "The channels and default_volume arguments have different number of elements."); + "The channels and default_volume arguments have different number of elements (%i and %i, resp).", + n_channels, n_volume_entries); goto finish; } for (i = 0; i < n_volume_entries; ++i) { if (default_volume[i] > PA_VOLUME_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u.", default_volume[i]); goto finish; } } @@ -1340,7 +1314,9 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u } if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample."); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, + "Too big sample: %i bytes. The maximum sample length is %u bytes.", + data_length, PA_SCACHE_ENTRY_SIZE_MAX); goto finish; } @@ -1348,9 +1324,13 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u ss.rate = sample_rate; ss.channels = n_channels; + pa_assert(pa_sample_spec_valid(&ss)); + if (!pa_frame_aligned(data_length, &ss)) { + char buf[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "The sample length in bytes doesn't align with the sample format and channels."); + "The sample length (%i bytes) doesn't align with the sample format and channels (%s).", + data_length, pa_sample_spec_snprint(buf, sizeof(buf), &ss)); goto finish; } @@ -1414,15 +1394,15 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use DBusMessageIter msg_iter; DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; - char *name; - const char *key; - const char *value; - char *escaped_value; + char *name = NULL; + const char *key = NULL; + const char *value = NULL; + char *escaped_value = NULL; pa_strbuf *arg_buffer = NULL; - pa_module *module; - pa_dbusiface_module *dbus_module; - const char *object_path; - int arg_type; + char *arg_string = NULL; + pa_module *module = NULL; + pa_dbusiface_module *dbus_module = NULL; + const char *object_path = NULL; pa_assert(conn); pa_assert(msg); @@ -1433,35 +1413,12 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use return; } - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; - - arg_type = dbus_message_iter_get_arg_type(&msg_iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Too few arguments. A dictionary from strings to strings was expected."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", - (char) arg_type); - return; - } - - arg_type = dbus_message_iter_get_element_type(&msg_iter); - if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); arg_buffer = pa_strbuf_new(); + pa_assert_se(dbus_message_iter_next(&msg_iter)); dbus_message_iter_recurse(&msg_iter, &dict_iter); while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { @@ -1470,41 +1427,26 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); - goto finish; - } - dbus_message_iter_get_basic(&dict_entry_iter, &key); - dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key); goto finish; } - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Wrong dict value type: '%c'. A string was expected.", (char) arg_type); - goto finish; - } - + pa_assert_se(dbus_message_iter_next(&dict_entry_iter)); dbus_message_iter_get_basic(&dict_entry_iter, &value); - dbus_message_iter_next(&dict_iter); - escaped_value = pa_escape(value, "\""); pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value); pa_xfree(escaped_value); + + dbus_message_iter_next(&dict_iter); } - if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) { + arg_string = pa_strbuf_tostring(arg_buffer); + + if (!(module = pa_module_load(c->core, name, arg_string))) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module."); goto finish; } @@ -1519,6 +1461,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use finish: if (arg_buffer) pa_strbuf_free(arg_buffer); + + pa_xfree(arg_string); } static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) { @@ -1543,47 +1487,32 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi const char *signal; char **objects = NULL; int n_objects; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, - DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - goto finish; - } + pa_assert_se(dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, + DBUS_TYPE_INVALID)); pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects); pa_dbus_send_empty_reply(conn, msg); -finish: dbus_free_string_array(objects); } static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_core *c = userdata; const char *signal; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(c); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)); pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL); diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 8f719bce..27525113 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -43,13 +43,13 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -60,7 +60,7 @@ static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -408,16 +408,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels); } -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; - unsigned device_channels = 0; + DBusMessageIter array_iter; + int device_channels = 0; dbus_uint32_t *volume = NULL; - unsigned n_volume_entries = 0; + int n_volume_entries = 0; pa_cvolume new_vol; - unsigned i = 0; + int i = 0; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); pa_cvolume_init(&new_vol); @@ -426,12 +428,12 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user new_vol.channels = device_channels; - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); if (n_volume_entries != device_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, - "Expected %u volume entries, got %u.", device_channels, n_volume_entries); + "Expected %u volume entries, got %i.", device_channels, n_volume_entries); return; } @@ -515,16 +517,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t is_muted = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) - return; + dbus_message_iter_get_basic(iter, &is_muted); if (d->type == DEVICE_TYPE_SINK) pa_sink_set_mute(d->sink, is_muted, TRUE); @@ -722,7 +724,7 @@ static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port); } -static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; const char *new_active_path; pa_dbusiface_device_port *new_active; @@ -730,11 +732,9 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(d); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0) - return; - if (!d->active_port) { pa_assert(pa_hashmap_isempty(d->ports)); @@ -747,8 +747,10 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void return; } + dbus_message_iter_get_basic(iter, &new_active_path); + if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path); + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such port: %s", new_active_path); return; } @@ -923,19 +925,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; dbus_bool_t suspend = FALSE; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(d); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)); if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed."); @@ -950,7 +945,6 @@ static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; - DBusError error; const char *port_name = NULL; pa_dbusiface_device_port *port = NULL; const char *port_path = NULL; @@ -959,13 +953,7 @@ static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void pa_assert(msg); pa_assert(d); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)); if (!(port = pa_hashmap_get(d->ports, port_name))) { if (d->type == DEVICE_TYPE_SINK) diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 7147be14..b0542a60 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -359,14 +359,10 @@ static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) pa_assert(msg); pa_assert(s); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &volume); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; @@ -405,17 +401,13 @@ static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *us pa_assert(msg); pa_assert(s); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0) - return; + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &sink_path); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &volume); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter))) return; diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 183625ba..a5f9bb51 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -70,9 +70,9 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -343,16 +343,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels); } -static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; - unsigned stream_channels = 0; + DBusMessageIter array_iter; + int stream_channels = 0; dbus_uint32_t *volume = NULL; - unsigned n_volume_entries = 0; + int n_volume_entries = 0; pa_cvolume new_vol; - unsigned i = 0; + int i = 0; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(s); if (s->type == STREAM_TYPE_RECORD) { @@ -366,8 +368,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user new_vol.channels = stream_channels; - if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0) - return; + dbus_message_iter_recurse(iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries); if (n_volume_entries != stream_channels) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, @@ -403,16 +405,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; dbus_bool_t is_muted = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(s); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0) - return; + dbus_message_iter_get_basic(iter, &is_muted); if (s->type == STREAM_TYPE_RECORD) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); @@ -575,19 +577,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; const char *device = NULL; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(s); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)); if (s->type == STREAM_TYPE_PLAYBACK) { pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 076b3918..d45cf792 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -167,11 +167,11 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -326,18 +326,20 @@ static void dbus_entry_free(struct dbus_entry *de) { /* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are * are a channel position and a volume value, respectively. The result is - * stored in the map and vol arguments. If the volume can't be read from the - * iterator, an error reply is sent and a negative number is returned. In case - * of a failure we make no guarantees about the state of map and vol. In case - * of an empty array the channels field of both map and vol are set to 0. */ + * stored in the map and vol arguments. The iterator must point to a "a(uu)" + * element. If the data is invalid, an error reply is sent and a negative + * number is returned. In case of a failure we make no guarantees about the + * state of map and vol. In case of an empty array the channels field of both + * map and vol are set to 0. This function calls dbus_message_iter_next(iter) + * before returning. */ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) { DBusMessageIter array_iter; DBusMessageIter struct_iter; - int arg_type; pa_assert(conn); pa_assert(msg); pa_assert(iter); + pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a(uu)")); pa_assert(map); pa_assert(vol); @@ -347,21 +349,6 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte map->channels = 0; vol->channels = 0; - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); - return -1; - } - - arg_type = dbus_message_iter_get_element_type(iter); - if (arg_type != DBUS_TYPE_STRUCT) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type); - return -1; - } - dbus_message_iter_recurse(iter, &array_iter); while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) { @@ -370,29 +357,14 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte dbus_message_iter_recurse(&array_iter, &struct_iter); - arg_type = dbus_message_iter_get_arg_type(&struct_iter); - if (arg_type != DBUS_TYPE_UINT32) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); - return -1; - } - dbus_message_iter_get_basic(&struct_iter, &chan_pos); - dbus_message_iter_next(&struct_iter); if (chan_pos >= PA_CHANNEL_POSITION_MAX) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos); return -1; } - arg_type = dbus_message_iter_get_arg_type(&struct_iter); - if (arg_type != DBUS_TYPE_UINT32) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing."); - else - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type); - return -1; - } - + pa_assert_se(dbus_message_iter_next(&struct_iter)); dbus_message_iter_get_basic(&struct_iter, &chan_vol); if (chan_vol > PA_VOLUME_MAX) { @@ -612,40 +584,35 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) { struct userdata *u = userdata; DBusMessageIter msg_iter; - const char *name; - const char *device; + const char *name = NULL; + const char *device = NULL; pa_channel_map map; pa_cvolume vol; - dbus_bool_t muted; - dbus_bool_t apply_immediately; + dbus_bool_t muted = FALSE; + dbus_bool_t apply_immediately = FALSE; pa_datum key; pa_datum value; - struct dbus_entry *dbus_entry; - struct entry *e; + struct dbus_entry *dbus_entry = NULL; + struct entry *e = NULL; pa_assert(conn); pa_assert(msg); pa_assert(u); - if (!dbus_message_iter_init(msg, &msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } + pa_assert_se(dbus_message_iter_init(msg, &msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &name); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0) - return; - - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &device); + pa_assert_se(dbus_message_iter_next(&msg_iter)); if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) return; - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0) - return; + dbus_message_iter_get_basic(&msg_iter, &muted); - if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0) - return; + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &apply_immediately); if (!*name) { pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name."); @@ -714,19 +681,12 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi struct userdata *u = userdata; const char *name; struct dbus_entry *de; - DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); - dbus_error_init(&error); - - if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); - dbus_error_free(&error); - return; - } + pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)); if (!(de = pa_hashmap_get(u->dbus_entries, name))) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry."); @@ -774,7 +734,7 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void pa_xfree(e); } -static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; const char *device; struct entry *e; @@ -782,10 +742,10 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0) - return; + dbus_message_iter_get_basic(iter, &device); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); @@ -835,25 +795,19 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void pa_xfree(e); } -static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; - DBusMessageIter msg_iter; pa_channel_map map; pa_cvolume vol; - struct entry *e; - pa_bool_t updated; + struct entry *e = NULL; + pa_bool_t updated = FALSE; pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return; - } - - if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0) + if (get_volume_arg(conn, msg, iter, &map, &vol) < 0) return; pa_assert_se(e = read_entry(de->userdata, de->entry_name)); @@ -901,7 +855,7 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_xfree(e); } -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; pa_bool_t muted; struct entry *e; @@ -909,10 +863,10 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_assert(conn); pa_assert(msg); + pa_assert(iter); pa_assert(de); - if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0) - return; + dbus_message_iter_get_basic(iter, &muted); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c index b45e6a6c..e3700ea5 100644 --- a/src/pulsecore/dbus-util.c +++ b/src/pulsecore/dbus-util.c @@ -291,7 +291,10 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool return pconn; } -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) { +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *m, + pa_bool_t use_rtclock, + DBusConnection *conn) { pa_dbus_wrap_connection *pconn; pa_assert(m); @@ -522,7 +525,10 @@ void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_t pa_assert_se((reply = dbus_message_new_method_return(in_reply_to))); dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter)); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, + DBUS_TYPE_VARIANT, + signature_from_basic_type(type), + &variant_iter)); pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data)); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter)); pa_assert_se(dbus_connection_send(c, reply, NULL)); @@ -548,7 +554,12 @@ static unsigned basic_type_size(int type) { } } -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) { +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n) { DBusMessage *reply = NULL; DBusMessageIter msg_iter; @@ -641,7 +652,12 @@ void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const c pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) { +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n) { DBusMessageIter dict_entry_iter; pa_assert(dict_iter); @@ -711,156 +727,18 @@ void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, cons pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter)); } -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) { - DBusMessageIter msg_iter; - DBusMessageIter variant_iter; - - pa_assert(c); - pa_assert(msg); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(data); - pa_assert(n); - - /* Skip the interface and property name arguments. */ - if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments."); - return -1; - } - - if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant."); - return -1; - } - - dbus_message_iter_recurse(&msg_iter, &variant_iter); - - if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0) - return -1; - - return 0; -} - -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) { - int arg_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_basic(type)); - pa_assert(data); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != type) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type); - return -1; - } - - dbus_message_iter_get_basic(iter, data); - - dbus_message_iter_next(iter); - - return 0; -} - -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) { - DBusMessageIter array_iter; - int signed_n; - int arg_type; - int element_type; - - pa_assert(c); - pa_assert(msg); - pa_assert(iter); - pa_assert(dbus_type_is_fixed(item_type)); - pa_assert(array); - pa_assert(n); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type); - return -1; - } - - element_type = dbus_message_iter_get_element_type(iter); - if (element_type != item_type) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type); - return -1; - } - - dbus_message_iter_recurse(iter, &array_iter); - - dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n); - - dbus_message_iter_next(iter); - - pa_assert(signed_n >= 0); - - *n = signed_n; - - return 0; -} - pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) { DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; - int arg_type; pa_proplist *proplist = NULL; - const char *key; - const uint8_t *value; - int value_length; + const char *key = NULL; + const uint8_t *value = NULL; + int value_length = 0; pa_assert(c); pa_assert(msg); pa_assert(iter); - - arg_type = dbus_message_iter_get_arg_type(iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type); - return NULL; - } - - arg_type = dbus_message_iter_get_element_type(iter); - if (arg_type != DBUS_TYPE_DICT_ENTRY) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type); - return NULL; - } + pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}")); proplist = pa_proplist_new(); @@ -869,32 +747,11 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) { dbus_message_iter_recurse(&dict_iter, &dict_entry_iter); - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_STRING) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type); - goto fail; - } - dbus_message_iter_get_basic(&dict_entry_iter, &key); dbus_message_iter_next(&dict_entry_iter); if (strlen(key) <= 0 || !pa_ascii_valid(key)) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key."); - goto fail; - } - - arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_ARRAY) { - if (arg_type == DBUS_TYPE_INVALID) - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing."); - else - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type); - goto fail; - } - - arg_type = dbus_message_iter_get_element_type(&dict_entry_iter); - if (arg_type != DBUS_TYPE_BYTE) { - pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type); + pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key); goto fail; } diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h index 9cee293c..f35e66cb 100644 --- a/src/pulsecore/dbus-util.h +++ b/src/pulsecore/dbus-util.h @@ -32,7 +32,10 @@ typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection; pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error); -pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn); +pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing( + pa_mainloop_api *mainloop, + pa_bool_t use_rtclock, + DBusConnection *conn); void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn); DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn); @@ -63,36 +66,41 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p); void pa_dbus_free_pending_list(pa_dbus_pending **p); /* Sends an error message as the reply to the given message. */ -void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); +void pa_dbus_send_error( + DBusConnection *c, + DBusMessage *in_reply_to, + const char *name, + const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5); void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to); void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data); -void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n); +void pa_dbus_send_basic_array_variant_reply( + DBusConnection *c, + DBusMessage *in_reply_to, + int item_type, + void *array, + unsigned n); void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist); void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n); void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data); void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data); -void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n); +void pa_dbus_append_basic_array_variant_dict_entry( + DBusMessageIter *dict_iter, + const char *key, + int item_type, + const void *array, + unsigned n); void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist); void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist); -/* Helper functions for extracting the value argument of a Set call. If the - * message is invalid, an error reply is sent and a negative number is - * returned. */ -int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data); -int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n); - -/* If the arguments can't be read from the iterator, an error reply is sent and - * a negative number is returned. */ -int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data); -int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n); - -/* Returns a new proplist that the caller has to free. If the proplist can't be - * read from the iterator, an error reply is sent and NULL is returned. */ +/* Returns a new proplist that the caller has to free. If the proplist contains + * invalid keys, an error reply is sent and NULL is returned. The iterator must + * point to "a{say}" element. This function calls dbus_message_iter_next(iter) + * before returning. */ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter); #endif diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index d1e19ff3..91022511 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -257,37 +257,90 @@ static void update_introspection(struct object_entry *oe) { oe->introspection = pa_strbuf_tostring_free(buf); } +/* Return value of find_handler() and its subfunctions. */ enum find_result_t { - FOUND_METHOD, + /* The received message is a valid .Get call. */ FOUND_GET_PROPERTY, + + /* The received message is a valid .Set call. */ FOUND_SET_PROPERTY, + + /* The received message is a valid .GetAll call. */ FOUND_GET_ALL, + + /* The received message is a valid method call. */ + FOUND_METHOD, + + /* The interface of the received message hasn't been registered for the + * destination object. */ + NO_SUCH_INTERFACE, + + /* No property handler was found for the received .Get or .Set call. */ + NO_SUCH_PROPERTY, + + /* The interface argument of a property call didn't match any registered + * interface. */ + NO_SUCH_PROPERTY_INTERFACE, + + /* The received message called .Get or .Set for a property whose access + * mode doesn't match the call. */ PROPERTY_ACCESS_DENIED, + + /* The new value signature of a .Set call didn't match the expexted + * signature. */ + INVALID_PROPERTY_SIG, + + /* No method handler was found for the received message. */ NO_SUCH_METHOD, - NO_SUCH_PROPERTY, - INVALID_MESSAGE_ARGUMENTS + + /* The signature of the received message didn't match the expected + * signature. Despite the name, this can also be returned for a property + * call if its message signature is invalid. */ + INVALID_METHOD_SIG }; -static enum find_result_t find_handler_by_property(struct object_entry *obj_entry, - DBusMessage *msg, - const char *property, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler) { +/* Data for resolving the correct reaction to a received message. */ +struct call_info { + DBusMessage *message; /* The received message. */ + struct object_entry *obj_entry; + const char *interface; /* Destination interface name (extracted from the message). */ + struct interface_entry *iface_entry; + + const char *property; /* Property name (extracted from the message). */ + const char *property_interface; /* The interface argument of a property call is stored here. */ + pa_dbus_property_handler *property_handler; + const char *expected_property_sig; /* Property signature from the introspection data. */ + const char *property_sig; /* The signature of the new value in the received .Set message. */ + DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */ + + const char *method; /* Method name (extracted from the message). */ + pa_dbus_method_handler *method_handler; + const char *expected_method_sig; /* Method signature from the introspection data. */ + const char *method_sig; /* The signature of the received message. */ +}; + +/* Called when call_info->property has been set and the property interface has + * not been given. In case of a Set call, call_info->property_sig is also set, + * which is checked against the expected value in this function. */ +static enum find_result_t find_handler_by_property(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(property); - pa_assert(iface_entry); - pa_assert(property_handler); - - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; - else + pa_assert(call_info); + + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + if (pa_streq(call_info->method, "Get")) + return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; + + else if (pa_streq(call_info->method, "Set")) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + else + return INVALID_PROPERTY_SIG; + + } else pa_assert_not_reached(); } } @@ -295,120 +348,138 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr return NO_SUCH_PROPERTY; } -static enum find_result_t find_handler_by_method(struct object_entry *obj_entry, - const char *method, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler) { +static enum find_result_t find_handler_by_method(struct call_info *call_info) { void *state = NULL; - pa_assert(obj_entry); - pa_assert(method); - pa_assert(iface_entry); - pa_assert(method_handler); + pa_assert(call_info); - PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) { - if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method))) + PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; } return NO_SUCH_METHOD; } -static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; +static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) { + pa_assert(call_info); - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); + if (pa_streq(call_info->method, "GetAll")) { + call_info->expected_method_sig = "s"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; - if (dbus_message_has_member(msg, "GetAll")) { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_INVALID)); - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface))) + if (*call_info->property_interface) { + if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) return FOUND_GET_ALL; - else { - return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */ - } + else + return NO_SUCH_PROPERTY_INTERFACE; + } else { - pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces))); + pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces)); return FOUND_GET_ALL; } - } else { - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID)) - return INVALID_MESSAGE_ARGUMENTS; - - if (*interface) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) { - if (dbus_message_has_member(msg, "Get")) - return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED; - else if (dbus_message_has_member(msg, "Set")) - return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED; + + } else if (pa_streq(call_info->method, "Get")) { + call_info->expected_method_sig = "ss"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_get_args(call_info->message, NULL, + DBUS_TYPE_STRING, &call_info->property_interface, + DBUS_TYPE_STRING, &call_info->property, + DBUS_TYPE_INVALID)); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) + return FOUND_GET_PROPERTY; + else + return NO_SUCH_PROPERTY; + + } else + return find_handler_by_property(call_info); + + } else if (pa_streq(call_info->method, "Set")) { + DBusMessageIter msg_iter; + + call_info->expected_method_sig = "ssv"; + if (!pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return INVALID_METHOD_SIG; + + pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter)); + + dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + dbus_message_iter_get_basic(&msg_iter, &call_info->property); + pa_assert_se(dbus_message_iter_next(&msg_iter)); + + dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter); + + call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter); + + if (*call_info->property_interface) { + if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface))) + return NO_SUCH_PROPERTY_INTERFACE; + + else if ((call_info->property_handler = + pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) { + call_info->expected_property_sig = call_info->property_handler->type; + + if (pa_streq(call_info->property_sig, call_info->expected_property_sig)) + return FOUND_SET_PROPERTY; else - pa_assert_not_reached(); + return INVALID_PROPERTY_SIG; + } else return NO_SUCH_PROPERTY; + } else - return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler); - } -} + return find_handler_by_property(call_info); -static enum find_result_t find_handler(struct object_entry *obj_entry, - DBusMessage *msg, - struct interface_entry **iface_entry, - pa_dbus_method_handler **method_handler, - pa_dbus_property_handler **property_handler, - const char **attempted_property) { - const char *interface; + } else + pa_assert_not_reached(); +} - pa_assert(obj_entry); - pa_assert(msg); - pa_assert(iface_entry); - pa_assert(method_handler); - pa_assert(property_handler); - pa_assert(attempted_property); +static enum find_result_t find_handler(struct call_info *call_info) { + pa_assert(call_info); - *iface_entry = NULL; - *method_handler = NULL; + if (call_info->interface) { + if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES)) + return find_handler_from_properties_call(call_info); - if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES)) - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface))) + return NO_SUCH_INTERFACE; - else if ((interface = dbus_message_get_interface(msg))) { - if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) && - (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg)))) + else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) return FOUND_METHOD; - else { - pa_log("Message has unknown interface or there's no method handler."); + + else return NO_SUCH_METHOD; - } } else { /* The method call doesn't contain an interface. */ - if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) { - if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD) - return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) { + if (find_handler_by_method(call_info) == FOUND_METHOD) + /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */ + return FOUND_METHOD; else /* Assume this is a .Properties call. */ - return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property); + return find_handler_from_properties_call(call_info); } else /* This is not a .Properties call. */ - return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler); + return find_handler_by_method(call_info); } } static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) { pa_dbus_protocol *p = user_data; - struct object_entry *obj_entry = NULL; - struct interface_entry *iface_entry = NULL; - pa_dbus_method_handler *method_handler = NULL; - pa_dbus_property_handler *property_handler = NULL; - const char *attempted_property = NULL; + struct call_info call_info; pa_assert(connection); pa_assert(message); @@ -423,49 +494,72 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa dbus_message_get_interface(message), dbus_message_get_member(message)); - pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)))); + call_info.message = message; + pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))); + call_info.interface = dbus_message_get_interface(message); + pa_assert_se(call_info.method = dbus_message_get_member(message)); + pa_assert_se(call_info.method_sig = dbus_message_get_signature(message)); if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") || (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) { - pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection); + pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection); goto finish; } - switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) { - case FOUND_METHOD: - method_handler->receive_cb(connection, message, iface_entry->userdata); - break; - + switch (find_handler(&call_info)) { case FOUND_GET_PROPERTY: - property_handler->get_cb(connection, message, iface_entry->userdata); + call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_SET_PROPERTY: - property_handler->set_cb(connection, message, iface_entry->userdata); + call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata); + break; + + case FOUND_METHOD: + call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata); break; case FOUND_GET_ALL: - if (iface_entry->get_all_properties_cb) - iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata); - /* TODO: Write an else branch where a dummy response is sent. */ + if (call_info.iface_entry->get_all_properties_cb) + call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata); + else { + DBusMessage *dummy_reply = NULL; + DBusMessageIter msg_iter; + DBusMessageIter dict_iter; + + pa_assert_se(dummy_reply = dbus_message_new_method_return(message)); + dbus_message_iter_init_append(dummy_reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL)); + dbus_message_unref(dummy_reply); + } break; case PROPERTY_ACCESS_DENIED: - pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property); + pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, + "%s access denied for property %s", call_info.method, call_info.property); break; case NO_SUCH_METHOD: - pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message)); + pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method); break; case NO_SUCH_PROPERTY: - pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property); + pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property); break; - case INVALID_MESSAGE_ARGUMENTS: - pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message)); + case INVALID_METHOD_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for method %s: '%s'. Expected '%s'.", + call_info.method, call_info.method_sig, call_info.expected_method_sig); break; + case INVALID_PROPERTY_SIG: + pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, + "Invalid signature for property %s: '%s'. Expected '%s'.", + call_info.property, call_info.property_sig, call_info.expected_property_sig); + default: pa_assert_not_reached(); } @@ -821,7 +915,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn return conn_entry->client; } -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) { +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects) { struct connection_entry *conn_entry; pa_idxset *object_set; char *object_path; @@ -981,7 +1080,12 @@ int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) return 0; } -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) { +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data) { pa_assert(p); pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX); pa_assert(cb); diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h index d771b4fc..6d100f7c 100644 --- a/src/pulsecore/protocol-dbus.h +++ b/src/pulsecore/protocol-dbus.h @@ -56,9 +56,19 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p); * message isn't a good idea; if you can't handle the message, reply with an * error. * + * The message signature is already checked against the introspection data, so + * you don't have to do that yourself. + * * All messages are method calls. */ typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata); +/* A specialized version of pa_dbus_receive_cb_t: the additional iterator + * argument points to the element inside the new value variant. + * + * The new value signature is checked against the introspection data, so you + * don't have to do that yourself. */ +typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); + typedef struct pa_dbus_arg_info { const char *name; const char *type; @@ -85,7 +95,7 @@ typedef struct pa_dbus_property_handler { /* The access mode for the property is determined by checking whether * get_cb or set_cb is NULL. */ pa_dbus_receive_cb_t get_cb; - pa_dbus_receive_cb_t set_cb; + pa_dbus_set_property_cb_t set_cb; } pa_dbus_property_handler; typedef struct pa_dbus_interface_info { @@ -140,7 +150,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn * only signals from the given objects are delivered. If this function is * called multiple time for the same connection and signal, the latest call * always replaces the previous object list. */ -void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects); +void pa_dbus_protocol_add_signal_listener( + pa_dbus_protocol *p, + DBusConnection *conn, + const char *signal, + char **objects, + unsigned n_objects); /* Disables the delivery of the signal for the given connection. The connection * must have been registered. If signal is NULL, all signals are disabled. If @@ -192,6 +207,11 @@ typedef enum pa_dbus_protocol_hook { PA_DBUS_PROTOCOL_HOOK_MAX } pa_dbus_protocol_hook_t; -pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data); +pa_hook_slot *pa_dbus_protocol_hook_connect( + pa_dbus_protocol *p, + pa_dbus_protocol_hook_t hook, + pa_hook_priority_t prio, + pa_hook_cb_t cb, + void *data); #endif -- cgit From 411feaed15edcef685e88582d76eddc5acfd965e Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 09:14:50 +0300 Subject: dbusiface-core: Add signals FallbackSinkUnset and FallbackSourceUnset. --- src/modules/dbus/iface-core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src') diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 0507ac9c..169e8e55 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -247,9 +247,11 @@ enum signal_index { SIGNAL_NEW_SINK, SIGNAL_SINK_REMOVED, SIGNAL_FALLBACK_SINK_UPDATED, + SIGNAL_FALLBACK_SINK_UNSET, SIGNAL_NEW_SOURCE, SIGNAL_SOURCE_REMOVED, SIGNAL_FALLBACK_SOURCE_UPDATED, + SIGNAL_FALLBACK_SOURCE_UNSET, SIGNAL_NEW_PLAYBACK_STREAM, SIGNAL_PLAYBACK_STREAM_REMOVED, SIGNAL_NEW_RECORD_STREAM, @@ -292,9 +294,11 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = { [SIGNAL_NEW_SINK] = { .name = "NewSink", .arguments = new_sink_args, .n_arguments = 1 }, [SIGNAL_SINK_REMOVED] = { .name = "SinkRemoved", .arguments = sink_removed_args, .n_arguments = 1 }, [SIGNAL_FALLBACK_SINK_UPDATED] = { .name = "FallbackSinkUpdated", .arguments = fallback_sink_updated_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SINK_UNSET] = { .name = "FallbackSinkUnset", .arguments = NULL, .n_arguments = 0 }, [SIGNAL_NEW_SOURCE] = { .name = "NewSource", .arguments = new_source_args, .n_arguments = 1 }, [SIGNAL_SOURCE_REMOVED] = { .name = "SourceRemoved", .arguments = source_removed_args, .n_arguments = 1 }, [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 }, + [SIGNAL_FALLBACK_SOURCE_UNSET] = { .name = "FallbackSourceUnset", .arguments = NULL, .n_arguments = 0 }, [SIGNAL_NEW_PLAYBACK_STREAM] = { .name = "NewPlaybackStream", .arguments = new_playback_stream_args, .n_arguments = 1 }, [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 }, [SIGNAL_NEW_RECORD_STREAM] = { .name = "NewRecordStream", .arguments = new_record_stream_args, .n_arguments = 1 }, @@ -1555,6 +1559,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; + + } else if (!new_fallback_sink) { + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SINK_UNSET].name))); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } @@ -1574,6 +1586,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3 pa_dbus_protocol_send_signal(c->dbus_protocol, signal); dbus_message_unref(signal); signal = NULL; + + } else if (!new_fallback_source) { + pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_FALLBACK_SOURCE_UNSET].name))); + pa_dbus_protocol_send_signal(c->dbus_protocol, signal); + dbus_message_unref(signal); + signal = NULL; } } break; -- cgit From 8a28e5de941cece0fccaac1c2babc502e2b2d46f Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 17:17:09 +0300 Subject: dbus: Change IsMuted property names to Mute. --- src/modules/dbus/iface-device.c | 40 +++++++++++++++++------------------ src/modules/dbus/iface-stream.c | 38 ++++++++++++++++----------------- src/modules/module-stream-restore.c | 42 ++++++++++++++++++------------------- 3 files changed, 60 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c index 27525113..3a747a44 100644 --- a/src/modules/dbus/iface-device.c +++ b/src/modules/dbus/iface-device.c @@ -48,8 +48,8 @@ static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, v static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -91,7 +91,7 @@ struct pa_dbusiface_device { enum device_type type; char *path; pa_cvolume volume; - pa_bool_t is_muted; + dbus_bool_t mute; union { pa_sink_state_t sink_state; pa_source_state_t source_state; @@ -119,7 +119,7 @@ enum property_handler_index { PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME, PROPERTY_HANDLER_BASE_VOLUME, PROPERTY_HANDLER_VOLUME_STEPS, - PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_MUTE, PROPERTY_HANDLER_HAS_HARDWARE_VOLUME, PROPERTY_HANDLER_HAS_HARDWARE_MUTE, PROPERTY_HANDLER_CONFIGURED_LATENCY, @@ -158,7 +158,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b", .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL }, [PROPERTY_HANDLER_BASE_VOLUME] = { .property_name = "BaseVolume", .type = "u", .get_cb = handle_get_base_volume, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME_STEPS] = { .property_name = "VolumeSteps", .type = "u", .get_cb = handle_get_volume_steps, .set_cb = NULL }, - [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute }, [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME] = { .property_name = "HasHardwareVolume", .type = "b", .get_cb = handle_get_has_hardware_volume, .set_cb = NULL }, [PROPERTY_HANDLER_HAS_HARDWARE_MUTE] = { .property_name = "HasHardwareMute", .type = "b", .get_cb = handle_get_has_hardware_mute, .set_cb = NULL }, [PROPERTY_HANDLER_CONFIGURED_LATENCY] = { .property_name = "ConfiguredLatency", .type = "t", .get_cb = handle_get_configured_latency, .set_cb = NULL }, @@ -507,31 +507,31 @@ static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps); } -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_device *d = userdata; pa_assert(conn); pa_assert(msg); pa_assert(d); - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->mute); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_device *d = userdata; - dbus_bool_t is_muted = FALSE; + dbus_bool_t mute = FALSE; pa_assert(conn); pa_assert(msg); pa_assert(iter); pa_assert(d); - dbus_message_iter_get_basic(iter, &is_muted); + dbus_message_iter_get_basic(iter, &mute); if (d->type == DEVICE_TYPE_SINK) - pa_sink_set_mute(d->sink, is_muted, TRUE); + pa_sink_set_mute(d->sink, mute, TRUE); else - pa_source_set_mute(d->source, is_muted, TRUE); + pa_source_set_mute(d->source, mute, TRUE); pa_dbus_send_empty_reply(conn, msg); } @@ -897,7 +897,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &d->mute); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency); @@ -1065,7 +1065,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t pa_dbusiface_device *d = userdata; DBusMessage *signal = NULL; const pa_cvolume *new_volume = NULL; - pa_bool_t new_muted = FALSE; + pa_bool_t new_mute = FALSE; pa_sink_state_t new_sink_state = 0; pa_source_state_t new_source_state = 0; pa_device_port *new_active_port = NULL; @@ -1111,15 +1111,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t signal = NULL; } - new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); + new_mute = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE); - if (d->is_muted != new_muted) { - d->is_muted = new_muted; + if (d->mute != new_mute) { + d->mute = new_mute; pa_assert_se(signal = dbus_message_new_signal(d->path, PA_DBUSIFACE_DEVICE_INTERFACE, signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->mute, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(d->dbus_protocol, signal); dbus_message_unref(signal); @@ -1201,7 +1201,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si d->type = DEVICE_TYPE_SINK; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index); d->volume = *pa_sink_get_volume(sink, FALSE); - d->is_muted = pa_sink_get_mute(sink, FALSE); + d->mute = pa_sink_get_mute(sink, FALSE); d->sink_state = pa_sink_get_state(sink); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); d->next_port_index = 0; @@ -1239,7 +1239,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_ d->type = DEVICE_TYPE_SOURCE; d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index); d->volume = *pa_source_get_volume(source, FALSE); - d->is_muted = pa_source_get_mute(source, FALSE); + d->mute = pa_source_get_mute(source, FALSE); d->source_state = pa_source_get_state(source); d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); d->next_port_index = 0; diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index a5f9bb51..04a45e6c 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -53,7 +53,7 @@ struct pa_dbusiface_stream { }; uint32_t sample_rate; pa_cvolume volume; - pa_bool_t is_muted; + dbus_bool_t mute; pa_proplist *proplist; pa_dbus_protocol *dbus_protocol; @@ -71,8 +71,8 @@ static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -93,7 +93,7 @@ enum property_handler_index { PROPERTY_HANDLER_SAMPLE_RATE, PROPERTY_HANDLER_CHANNELS, PROPERTY_HANDLER_VOLUME, - PROPERTY_HANDLER_IS_MUTED, + PROPERTY_HANDLER_MUTE, PROPERTY_HANDLER_BUFFER_LATENCY, PROPERTY_HANDLER_DEVICE_LATENCY, PROPERTY_HANDLER_RESAMPLE_METHOD, @@ -111,7 +111,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = { [PROPERTY_HANDLER_SAMPLE_RATE] = { .property_name = "SampleRate", .type = "u", .get_cb = handle_get_sample_rate, .set_cb = NULL }, [PROPERTY_HANDLER_CHANNELS] = { .property_name = "Channels", .type = "au", .get_cb = handle_get_channels, .set_cb = NULL }, [PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "au", .get_cb = handle_get_volume, .set_cb = handle_set_volume }, - [PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_get_is_muted, .set_cb = handle_set_is_muted }, + [PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_get_mute, .set_cb = handle_set_mute }, [PROPERTY_HANDLER_BUFFER_LATENCY] = { .property_name = "BufferLatency", .type = "t", .get_cb = handle_get_buffer_latency, .set_cb = NULL }, [PROPERTY_HANDLER_DEVICE_LATENCY] = { .property_name = "DeviceLatency", .type = "t", .get_cb = handle_get_device_latency, .set_cb = NULL }, [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s", .get_cb = handle_get_resample_method, .set_cb = NULL }, @@ -390,7 +390,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag pa_dbus_send_empty_reply(conn, msg); } -static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; pa_assert(conn); @@ -402,26 +402,26 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us return; } - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute); } -static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { pa_dbusiface_stream *s = userdata; - dbus_bool_t is_muted = FALSE; + dbus_bool_t mute = FALSE; pa_assert(conn); pa_assert(msg); pa_assert(iter); pa_assert(s); - dbus_message_iter_get_basic(iter, &is_muted); + dbus_message_iter_get_basic(iter, &mute); if (s->type == STREAM_TYPE_RECORD) { pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute."); return; } - pa_sink_input_set_mute(s->sink_input, is_muted, TRUE); + pa_sink_input_set_mute(s->sink_input, mute, TRUE); pa_dbus_send_empty_reply(conn, msg); }; @@ -561,7 +561,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat if (s->type == STREAM_TYPE_PLAYBACK) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute); } pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency); @@ -708,7 +708,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t if (s->type == STREAM_TYPE_PLAYBACK) { pa_cvolume new_volume; - pa_bool_t new_muted = FALSE; + pa_bool_t new_mute = FALSE; pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); @@ -733,15 +733,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t signal = NULL; } - new_muted = pa_sink_input_get_mute(s->sink_input); + new_mute = pa_sink_input_get_mute(s->sink_input); - if (s->is_muted != new_muted) { - s->is_muted = new_muted; + if (s->mute != new_mute) { + s->mute = new_mute; pa_assert_se(signal = dbus_message_new_signal(s->path, PA_DBUSIFACE_STREAM_INTERFACE, signals[SIGNAL_MUTE_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID)); + pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID)); pa_dbus_protocol_send_signal(s->dbus_protocol, signal); dbus_message_unref(signal); @@ -823,7 +823,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->sink = pa_sink_ref(sink_input->sink); s->sample_rate = sink_input->sample_spec.rate; pa_sink_input_get_volume(sink_input, &s->volume, TRUE); - s->is_muted = pa_sink_input_get_mute(sink_input); + s->mute = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s); @@ -851,7 +851,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ s->source = pa_source_ref(source_output->source); s->sample_rate = source_output->sample_spec.rate; pa_cvolume_init(&s->volume); - s->is_muted = FALSE; + s->mute = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 8389be67..5a6c8a3d 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -170,8 +170,8 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata); static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); -static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata); -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); +static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata); +static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata); static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata); @@ -188,7 +188,7 @@ enum entry_property_handler_index { ENTRY_PROPERTY_HANDLER_NAME, ENTRY_PROPERTY_HANDLER_DEVICE, ENTRY_PROPERTY_HANDLER_VOLUME, - ENTRY_PROPERTY_HANDLER_IS_MUTED, + ENTRY_PROPERTY_HANDLER_MUTE, ENTRY_PROPERTY_HANDLER_MAX }; @@ -202,7 +202,7 @@ static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_M [ENTRY_PROPERTY_HANDLER_NAME] = { .property_name = "Name", .type = "s", .get_cb = handle_entry_get_name, .set_cb = NULL }, [ENTRY_PROPERTY_HANDLER_DEVICE] = { .property_name = "Device", .type = "s", .get_cb = handle_entry_get_device, .set_cb = handle_entry_set_device }, [ENTRY_PROPERTY_HANDLER_VOLUME] = { .property_name = "Volume", .type = "a(uu)", .get_cb = handle_entry_get_volume, .set_cb = handle_entry_set_volume }, - [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b", .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted } + [ENTRY_PROPERTY_HANDLER_MUTE] = { .property_name = "Mute", .type = "b", .get_cb = handle_entry_get_mute, .set_cb = handle_entry_set_mute } }; enum method_handler_index { @@ -216,11 +216,11 @@ enum entry_method_handler_index { ENTRY_METHOD_HANDLER_MAX }; -static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, - { "device", "s", "in" }, - { "volume", "a(uu)", "in" }, - { "is_muted", "b", "in" }, - { "entry", "o", "out" } }; +static pa_dbus_arg_info add_entry_args[] = { { "name", "s", "in" }, + { "device", "s", "in" }, + { "volume", "a(uu)", "in" }, + { "mute", "b", "in" }, + { "entry", "o", "out" } }; static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } }; static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { @@ -837,10 +837,10 @@ static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBus pa_xfree(e); } -static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) { +static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) { struct dbus_entry *de = userdata; struct entry *e; - dbus_bool_t muted; + dbus_bool_t mute; pa_assert(conn); pa_assert(msg); @@ -848,16 +848,16 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo pa_assert_se(e = read_entry(de->userdata, de->entry_name)); - muted = e->muted_valid ? e->muted : FALSE; + mute = e->muted_valid ? e->muted : FALSE; - pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute); pa_xfree(e); } -static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { +static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) { struct dbus_entry *de = userdata; - pa_bool_t muted; + dbus_bool_t mute; struct entry *e; pa_bool_t updated; @@ -866,17 +866,17 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DB pa_assert(iter); pa_assert(de); - dbus_message_iter_get_basic(iter, &muted); + dbus_message_iter_get_basic(iter, &mute); pa_assert_se(e = read_entry(de->userdata, de->entry_name)); - updated = !e->muted_valid || e->muted != muted; + updated = !e->muted_valid || e->muted != mute; if (updated) { pa_datum key; pa_datum value; - e->muted = muted; + e->muted = mute; e->muted_valid = TRUE; key.data = de->entry_name; @@ -902,7 +902,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u DBusMessageIter dict_iter; DBusMessageIter dict_entry_iter; const char *device; - dbus_bool_t muted; + dbus_bool_t mute; pa_assert(conn); pa_assert(msg); @@ -911,7 +911,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u pa_assert_se(e = read_entry(de->userdata, de->entry_name)); device = e->device_valid ? e->device : ""; - muted = e->muted_valid ? e->muted : FALSE; + mute = e->muted_valid ? e->muted : FALSE; pa_assert_se((reply = dbus_message_new_method_return(msg))); @@ -929,7 +929,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &mute); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); -- cgit From 587131917f9129c8347c789febb7e755dfb091de Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Mon, 31 Aug 2009 18:12:55 +0300 Subject: dbus-protocol: Implement argument type checking for normal methods. --- src/modules/dbus/iface-client.c | 2 +- src/pulsecore/protocol-dbus.c | 44 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c index 54550d2c..546370f9 100644 --- a/src/modules/dbus/iface-client.c +++ b/src/modules/dbus/iface-client.c @@ -101,7 +101,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = { [METHOD_HANDLER_REMOVE_PROPERTIES] = { .method_name = "RemoveProperties", .arguments = remove_properties_args, - .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info), + .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info), .receive_cb = handle_remove_properties } }; diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c index 91022511..5c1127be 100644 --- a/src/pulsecore/protocol-dbus.c +++ b/src/pulsecore/protocol-dbus.c @@ -72,6 +72,7 @@ struct connection_entry { struct interface_entry { char *name; pa_hashmap *method_handlers; + pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */ pa_hashmap *property_handlers; pa_dbus_receive_cb_t get_all_properties_cb; pa_dbus_signal_info *signals; @@ -354,8 +355,14 @@ static enum find_result_t find_handler_by_method(struct call_info *call_info) { pa_assert(call_info); PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) { - if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) - return FOUND_METHOD; + if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) { + call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method); + + if (pa_streq(call_info->method_sig, call_info->expected_method_sig)) + return FOUND_METHOD; + else + return INVALID_METHOD_SIG; + } } return NO_SUCH_METHOD; @@ -630,6 +637,31 @@ static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) { return handlers; } +static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) { + pa_hashmap *signatures = NULL; + pa_dbus_method_handler *handler = NULL; + void *state = NULL; + pa_strbuf *sig_buf = NULL; + unsigned i = 0; + + pa_assert(method_handlers); + + signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + PA_HASHMAP_FOREACH(handler, method_handlers, state) { + sig_buf = pa_strbuf_new(); + + for (i = 0; i < handler->n_arguments; ++i) { + if (pa_streq(handler->arguments[i].direction, "in")) + pa_strbuf_puts(sig_buf, handler->arguments[i].type); + } + + pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf)); + } + + return signatures; +} + static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) { pa_hashmap *handlers; unsigned i = 0; @@ -707,6 +739,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, iface_entry = pa_xnew(struct interface_entry, 1); iface_entry->name = pa_xstrdup(info->name); iface_entry->method_handlers = create_method_handlers(info); + iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers); iface_entry->property_handlers = create_property_handlers(info); iface_entry->get_all_properties_cb = info->get_all_properties_cb; iface_entry->signals = copy_signals(info); @@ -760,6 +793,12 @@ static void method_handler_free_cb(void *p, void *userdata) { pa_xfree((pa_dbus_arg_info *) h->arguments); } +static void method_signature_free_cb(void *p, void *userdata) { + pa_assert(p); + + pa_xfree(p); +} + static void property_handler_free_cb(void *p, void *userdata) { pa_dbus_property_handler *h = p; @@ -792,6 +831,7 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con pa_xfree(iface_entry->name); pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL); + pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL); pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL); for (i = 0; i < iface_entry->n_signals; ++i) { -- cgit From 8f4940b17aa185f69a66b5a91ed6deeb3dde9226 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Tue, 22 Sep 2009 18:43:03 +0100 Subject: libpulse: Add *_NOFLAGS flags with value 0 for various enums This avoids the need for ugly casting in client implementations. --- src/pulse/def.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/pulse/def.h b/src/pulse/def.h index e839bd92..5d0a0b4b 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -110,6 +110,8 @@ typedef enum pa_operation_state { /** Some special flags for contexts. */ typedef enum pa_context_flags { + PA_CONTEXT_NOFLAGS = 0x0000U, + /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ PA_CONTEXT_NOAUTOSPAWN = 0x0001U, /**< Disabled autospawning of the PulseAudio daemon if required */ PA_CONTEXT_NOFAIL = 0x0002U @@ -140,6 +142,9 @@ typedef enum pa_stream_direction { /** Some special flags for stream connections. */ typedef enum pa_stream_flags { + PA_STREAM_NOFLAGS = 0x0000U, + /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ + PA_STREAM_START_CORKED = 0x0001U, /**< Create the stream corked, requiring an explicit * pa_stream_cork() call to uncork it. */ @@ -688,6 +693,9 @@ typedef enum pa_seek_mode { /** Special sink flags. */ typedef enum pa_sink_flags { + PA_SINK_NOFLAGS = 0x0000U, + /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ + PA_SINK_HW_VOLUME_CTRL = 0x0001U, /**< Supports hardware volume control */ @@ -775,6 +783,9 @@ static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) { /** Special source flags. */ typedef enum pa_source_flags { + PA_SOURCE_NOFLAGS = 0x0000U, + /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ + PA_SOURCE_HW_VOLUME_CTRL = 0x0001U, /**< Supports hardware volume control */ -- cgit From 5eecd8ea7dcaca7536240b8a5800c686db51eee5 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 23 Sep 2009 17:16:04 +0200 Subject: svolume: tweak constraints for 32 bits Tweak the constraints a little so that register starved 32bit systems can select a stack variable for the channel paramter instead of reusing one of the registers we're using in the code. --- src/pulsecore/svolume_mmx.c | 4 ++-- src/pulsecore/svolume_sse.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c index 62f3397e..1768eb50 100644 --- a/src/pulsecore/svolume_mmx.c +++ b/src/pulsecore/svolume_mmx.c @@ -152,7 +152,7 @@ pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi " emms \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) - : "r" ((pa_reg_x86)channels) + : "X" ((pa_reg_x86)channels) : "cc" ); } @@ -228,7 +228,7 @@ pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi " emms \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) - : "r" ((pa_reg_x86)channels) + : "X" ((pa_reg_x86)channels) : "cc" ); } diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c index 5276bda4..ab9394fb 100644 --- a/src/pulsecore/svolume_sse.c +++ b/src/pulsecore/svolume_sse.c @@ -149,7 +149,7 @@ pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns "8: \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) - : "r" ((pa_reg_x86)channels) + : "X" ((pa_reg_x86)channels) : "cc" ); } @@ -237,7 +237,7 @@ pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns "8: \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) - : "r" ((pa_reg_x86)channels) + : "X" ((pa_reg_x86)channels) : "cc" ); } -- cgit From d06f2eaf3083ff7e323bfb2d9b5dfd5a5fda3dd4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 28 Sep 2009 19:54:37 -0700 Subject: reserve: downgrade reserve logic messages, so that reusing pa in two sessions does not create spew in syslog --- src/modules/reserve-wrap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/reserve-wrap.c b/src/modules/reserve-wrap.c index 6086fc99..4be19c73 100644 --- a/src/modules/reserve-wrap.c +++ b/src/modules/reserve-wrap.c @@ -137,7 +137,7 @@ pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name) #ifdef HAVE_DBUS if (!(r->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { - pa_log_warn("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); + pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); /* We don't treat this as error here because we want allow PA * to run even when no session bus is available. */ @@ -154,10 +154,10 @@ pa_reserve_wrapper* pa_reserve_wrapper_get(pa_core *c, const char *device_name) NULL)) < 0) { if (k == -EBUSY) { - pa_log_error("Device '%s' already locked.", device_name); + pa_log_debug("Device '%s' already locked.", device_name); goto fail; } else { - pa_log_warn("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k)); + pa_log_debug("Failed to acquire reservation lock on device '%s': %s", device_name, pa_cstrerror(-k)); return r; } } @@ -280,7 +280,7 @@ pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const cha #ifdef HAVE_DBUS if (!(w->connection = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { - pa_log_warn("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); + pa_log_debug("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); /* We don't treat this as error here because we want allow PA * to run even when no session bus is available. */ @@ -294,7 +294,7 @@ pa_reserve_monitor_wrapper* pa_reserve_monitor_wrapper_get(pa_core *c, const cha change_cb, NULL)) < 0) { - pa_log_warn("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); + pa_log_debug("Failed to create watch on device '%s': %s", device_name, pa_cstrerror(-k)); goto fail; } -- cgit From eac566226ed9026347cdb415a93ad9b15fbd8b45 Mon Sep 17 00:00:00 2001 From: Nix Date: Sat, 26 Sep 2009 20:18:00 +0100 Subject: Don't refuse to start on systems using GNU stow, graft, STORE et al There are multiple package management systems out there which implement packages using symlinks. The recent (otherwise useful) check to ensure that a re-executed pulseaudio is actually reexecuting itself unfortunately breaks in the presence of all these packaging systems, because PA_BINARY refers to its installed location (e.g. /usr/local/bin/pulseaudio), which is a symlink to the binary (e.g. /usr/local/stow/pulseaudio-0.9.18/bin/pulseaudio), because /proc/self/exe always contains the canonical path of the executable, with all symlinks resolved. (At least one distribution uses a symlink-based packaging system, so will be forced to apply this locally in any case.) The fix is simple: canonicalize PA_BINARY before equality-testing. (This should be completely safe, because the OS does just that when PA_BINARY is executed.) The patch is against 0.9.18, but applies without fuzz to current master. --- src/daemon/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/daemon/main.c b/src/daemon/main.c index af59adef..2e16c187 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -416,23 +416,28 @@ int main(int argc, char *argv[]) { if (!getenv("LD_BIND_NOW")) { char *rp; + char *canonical_rp; /* We have to execute ourselves, because the libc caches the * value of $LD_BIND_NOW on initialization. */ pa_set_env("LD_BIND_NOW", "1"); + canonical_rp = pa_realpath(PA_BINARY); + if ((rp = pa_readlink("/proc/self/exe"))) { - if (pa_streq(rp, PA_BINARY)) + if (pa_streq(rp, canonical_rp)) pa_assert_se(execv(rp, argv) == 0); else - pa_log_warn("/proc/self/exe does not point to " PA_BINARY ", cannot self execute. Are you playing games?"); + pa_log_warn("/proc/self/exe does not point to %s, cannot self execute. Are you playing games?", canonical_rp); pa_xfree(rp); } else pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?"); + + pa_xfree(canonical_rp); } #endif -- cgit From b8de3bd214775acd6d24291ad65a03a8204a9ec6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 29 Sep 2009 23:47:49 +0200 Subject: polkit: drop left-over polkit policy file from git tree --- src/daemon/org.pulseaudio.policy.in | 50 ------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/daemon/org.pulseaudio.policy.in (limited to 'src') diff --git a/src/daemon/org.pulseaudio.policy.in b/src/daemon/org.pulseaudio.policy.in deleted file mode 100644 index 1d0b6a7d..00000000 --- a/src/daemon/org.pulseaudio.policy.in +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - The PulseAudio Project - http://pulseaudio.org/ - audio-card - - - <_description>Real-time scheduling for the PulseAudio daemon - <_message>System policy prevents PulseAudio from acquiring real-time scheduling. - - no - no - no - - - - - <_description>High-priority scheduling (negative Unix nice level) for the PulseAudio daemon - <_message>System policy prevents PulseAudio from acquiring high-priority scheduling. - - no - no - no - - - - -- cgit From 431555030ead3aabad83028f359849314e95065e Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 22 Jun 2009 00:36:14 -0700 Subject: module-equalizer-sink added src/Makefile.am: added module-equalizer-sink --- src/Makefile.am | 7 + src/modules/module-equalizer-sink.c | 850 ++++++++++++++++++++++++++++++++++++ 2 files changed, 857 insertions(+) create mode 100755 src/modules/module-equalizer-sink.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6544e2aa..10f9a793 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1006,6 +1006,7 @@ modlibexec_LTLIBRARIES += \ module-combine.la \ module-remap-sink.la \ module-ladspa-sink.la \ + module-equalizer-sink.la \ module-esound-sink.la \ module-tunnel-sink.la \ module-tunnel-source.la \ @@ -1200,6 +1201,7 @@ SYMDEF_FILES = \ modules/module-combine-symdef.h \ modules/module-remap-sink-symdef.h \ modules/module-ladspa-sink-symdef.h \ + modules/module-equalizer-sink-symdef.h \ modules/module-esound-compat-spawnfd-symdef.h \ modules/module-esound-compat-spawnpid-symdef.h \ modules/module-match-symdef.h \ @@ -1380,6 +1382,11 @@ module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/l module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c +module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) module_match_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c new file mode 100755 index 00000000..8b34fa0d --- /dev/null +++ b/src/modules/module-equalizer-sink.c @@ -0,0 +1,850 @@ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + + +#include "module-equalizer-sink-symdef.h" + +PA_MODULE_AUTHOR("Jason Newton"); +PA_MODULE_DESCRIPTION(_("General Purpose Equalizer")); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE(_("sink= ")); + +#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink, *master; + pa_sink_input *sink_input; + + size_t channels; + size_t fft_size; //length (res) of fft + size_t window_size;//even! + size_t overlap_size; + size_t samples_gathered; + size_t n_buffered_output; + size_t max_output; + float *H;//frequency response filter (magnitude based) + float *W;//windowing function (time domain) + float *work_buffer,**input,**overlap_accum,**output_buffer; + fftwf_complex *output_window; + fftwf_plan forward_plan,inverse_plan; + + pa_memblockq *memblockq; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", + "master", + "format", + "rate", + "channels", + "channel_map", + NULL +}; + +uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) +{ + return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - + ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); +} + +void hanning_normalized_window(float *W,size_t window_size){ + //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 ) + float c; + for(size_t i=0;iuserdata; + + switch (code) { + + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t usec = 0; + pa_sample_spec *ss=&u->sink->sample_spec; + + /* Get the latency of the master sink */ + if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) + usec = 0; + + usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss); + /* Add the latency internal to our sink input on top */ + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + *((pa_usec_t*) data) = usec; + return 0; + } + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + + +/* Called from main context */ +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (PA_SINK_IS_LINKED(state) && + u->sink_input && + PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + + return 0; +} + +/* Called from I/O thread context */ +static void sink_request_rewind(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); +} + +/* Called from I/O thread context */ +static void sink_update_requested_latency(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + pa_sink_input_set_requested_latency_within_thread( + u->sink_input, + pa_sink_get_requested_latency_within_thread(s)); +} + +/* Called from I/O thread context */ +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { + struct userdata *u; + float *src, *dst; + size_t c; + pa_memchunk tchunk; + pa_sink_input_assert_ref(i); + pa_assert(chunk); + pa_assert_se(u = i->userdata); + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t ss=pa_sample_size(&u->sink->sample_spec); + size_t fe = fs/ss; + + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + return -1; + + //output any buffered outputs first + if(u->n_buffered_output>0){ + //pa_log("outputing %ld buffered samples",u->n_buffered_output); + chunk->index = 0; + size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs); + chunk->length = n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + for(size_t j=0;jchannels;++j){ + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); + memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); + } + u->n_buffered_output-=n_outputable; + pa_memblock_release(chunk->memblock); + return 0; + } + pa_assert_se(u->n_buffered_output==0); + + //collect the minimum number of samples + while(u->samples_gathered < (u->window_size-u->overlap_size)){ + //render some new fragments to our memblockq + //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output); + size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output); + while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { + pa_memchunk nchunk; + + pa_sink_render(u->sink, desired_samples*fs, &nchunk); + pa_memblockq_push(u->memblockq, &nchunk); + pa_memblock_unref(nchunk.memblock); + } + if(tchunk.length/fs!=desired_samples){ + pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); + } + size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered); + //TODO: figure out what to do with rest of the samples when there's too many (rare?) + src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); + for (size_t c=0;cchannels;c++) { + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + } + + u->samples_gathered+=n_samples; + pa_memblock_release(tchunk.memblock); + pa_memblock_unref(tchunk.memblock); + } + //IT should be this guy if we're buffering like how its supposed to + //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs); + //This one takes into account the actual data gathered but then the dsp + //stuff is wrong when the buffer "underruns" + size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs); + /* + //debugging: tests if immediate release of freshly buffered data + //plays ok and prevents any other processing + chunk->index=0; + chunk->length=n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock);; + for (size_t c=0;cchannels;c++) { + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable); + } + u->samples_gathered=0; + pa_memblock_release(chunk->memblock); + return 0; + */ + + //pa_log("%ld dequed samples",u->samples_gathered); + + chunk->index=0; + chunk->length=n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples); + //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples); + + /* + struct timespec start, end; + uint64_t elapsed; + clock_gettime(CLOCK_MONOTONIC, &start); + */ + //use a zero-phase sliding dft and overlap-add method + + pa_assert_se(u->fft_size>=u->window_size); + //pa_assert_se(u->window_size%2==0); + pa_assert_se(u->overlap_sizewindow_size); + pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size); + size_t sample_rem=u->window_size-u->overlap_size-n_outputable; + //size_t w_mid=u->window_size/2; + //pa_log("hello world a"); + for (c=0;cchannels;c++) { + //center the data for zero phase + //zero-pad TODO: optimization if sure these zeros aren't overwritten + //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float)); + //memset(u->work_buffer,0,u->fft_size*sizeof(float)); + /* + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + u->work_buffer[j]=u->input[c][j]; + } + */ + //zero padd the data, don't worry about zerophase, shouldn't really matter + memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + /* + //recenter for zero phase + for(size_t j=0;jwork_buffer[j]; + u->work_buffer[j]=u->input[c][j+w_mid]; + u->work_buffer[j+u->fft_size-w_mid]=tmp; + } + */ + //pa_log("hello world b"); + + /* + //window and zero phase shift + for(size_t j=0;jwork_buffer[j]=u->input[c][j+w_mid]; + //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j]; + u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid]; + u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j]; + }*/ + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + ////identity transform (fft size) + //u->output_window[j][0]/=u->fft_size; + //u->output_window[j][1]/=u->fft_size; + ////identity transform (window size) + //u->output_window[j][0]/=u->window_size; + //u->output_window[j][1]/=u->window_size; + //filtered + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + + /* + //uncenter the data + for(size_t j=0;jwork_buffer[j]; + u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid]; + u->work_buffer[j+w_mid]=tmp; + } + */ + /* + //divide out fft gain (more stable here?) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]/=u->fft_size; + } + */ + /* + //debug: tests overlaping add + //and negates ALL PREVIOUS processing + //yields a perfect reconstruction if COLA is held + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + */ + /* + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + */ + + /* + //overlap add and preserve overlap component from this window (zero phase) + for(size_t j=0;joverlap_size;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; + } + */ + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;joverlap_size;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; + } + + //preseve the needed input for the next windows overlap + memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float)); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); + //buffer the rest of them + memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); + } + /* + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed=time_diff(&end, &start); + pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed); + */ + u->n_buffered_output+=sample_rem; + u->samples_gathered=0; + + + //pa_log("%ld samples queued",u->n_buffered_output); + + pa_memblock_release(chunk->memblock); + + + return 0; +} + +/* Called from I/O thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + size_t amount = 0; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + return; + + if (u->sink->thread_info.rewind_nbytes > 0) { + size_t max_rewrite; + + max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); + amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); + u->sink->thread_info.rewind_nbytes = 0; + + if (amount > 0) { + pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + pa_log_debug("Resetting equalizer"); + } + } + + pa_sink_process_rewind(u->sink, amount); + pa_memblockq_rewind(u->memblockq, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_memblockq_set_maxrewind(u->memblockq, nbytes); + pa_sink_set_max_rewind_within_thread(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_max_request_within_thread(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_detach_within_thread(u->sink); + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); +} + +/* Called from I/O thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); + pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_attach_within_thread(u->sink); + + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); +} + +/* Called from main context */ +static void sink_input_kill_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_unlink(u->sink); + pa_sink_input_unlink(u->sink_input); + + pa_sink_unref(u->sink); + u->sink = NULL; + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + + pa_module_unload_request(u->module, TRUE); +} + +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) { + pa_log_debug("Requesting rewind due to state change."); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); + } +} + +/* Called from main context */ +static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + return u->sink != dest; +} + +int pa__init(pa_module*m) { + struct userdata *u; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma; + const char *z; + pa_sink *master; + pa_sink_input_new_data sink_input_data; + pa_sink_new_data sink_data; + pa_bool_t *use_default = NULL; + size_t fs; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { + pa_log("Master sink not found"); + goto fail; + } + + ss = master->sample_spec; + ss.format = PA_SAMPLE_FLOAT32; + map = master->channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + fs=pa_frame_size(&ss); + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + m->userdata = u; + u->master = master; + u->sink = NULL; + u->sink_input = NULL; + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); + + //u->fft_size=44100; + //u->fft_size=48000; + //u->fft_size=1024; + u->channels=ss.channels; + u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); + //u->fft_size=ss.rate; + //u->fft_size=65536; + pa_log("fft size: %ld",u->fft_size); + u->window_size=8001; + u->overlap_size=(u->window_size+1)/2; + //u->overlap_size=u->window_size/2; + //u->overlap_size=0; + u->samples_gathered=0; + u->n_buffered_output=0; + u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); + u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); + u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); + u->input=(float **)malloc(sizeof(float *)*u->channels); + u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); + u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); + for(size_t c=0;cchannels;++c){ + u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + memset(u->input[c],0,u->window_size*sizeof(float)); + u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float)); + memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + } + u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); + u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); + u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + + /* + //rectangular window + for(size_t j=0;jwindow_size;++j){ + u->W[j]=1.0; + } + */ + //hanning_normalized_window(u->W,u->window_size); + hanning_window(u->W,u->window_size); + //sin_window(u->W,u->window_size); + array_out("/home/jason/window.txt",u->W,u->window_size); + //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE); + //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE); + //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE); + const int freqs[]={0,25,50,100,200,300,400,800,1500, + 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + const float coefficients[]={1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + const size_t ncoefficients=sizeof(coefficients)/sizeof(float); + pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); + freq_translated[0]=1; + //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + for(size_t i=1;ifft_size)/ss.rate; + //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); + pa_assert_se(freq_translated[i]>=freq_translated[i-1]); + } + freq_translated[ncoefficients-1]=DBL_MAX; + //Interpolate the specified frequency band values + u->H[0]=1; + for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ + pa_assert_se(j=DBL_MAX){ + for(;i<(u->fft_size/2+1);++i){ + u->H[i]=coefficients[j]; + } + break; + } + //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]); + //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]); + pa_assert_se(freq_translated[j]=freq_translated[j]); + pa_assert_se(i<=freq_translated[j+1]); + //bilinear-inerpolation of coefficients specified + float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + pa_assert_se(c0>=0&&c0<=1.0); + u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + pa_assert_se(u->H[i]>0); + while(i>=floor(freq_translated[j+1])){ + j++; + } + } + array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1); + //divide out the fft gain + for(int i=0;i<(u->fft_size/2+1);++i){ + u->H[i]/=u->fft_size; + } + free(freq_translated); + + /* Create sink */ + pa_sink_new_data_init(&sink_data); + sink_data.driver = __FILE__; + sink_data.module = m; + if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) + sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name); + sink_data.namereg_fail = FALSE; + pa_sink_new_data_set_sample_spec(&sink_data, &ss); + pa_sink_new_data_set_channel_map(&sink_data, &map); + z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer"); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); + + if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&sink_data); + goto fail; + } + + u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + pa_sink_new_data_done(&sink_data); + + if (!u->sink) { + pa_log("Failed to create sink."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->set_state = sink_set_state; + u->sink->update_requested_latency = sink_update_requested_latency; + u->sink->request_rewind = sink_request_rewind; + u->sink->userdata = u; + + pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); + pa_sink_set_rtpoll(u->sink, master->rtpoll); + + /* Create sink input */ + pa_sink_input_new_data_init(&sink_input_data); + sink_input_data.driver = __FILE__; + sink_input_data.module = m; + sink_input_data.sink = u->master; + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); + pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new_data_done(&sink_input_data); + + if (!u->sink_input) + goto fail; + + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; + u->sink_input->update_max_request = sink_input_update_max_request_cb; + u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->kill = sink_input_kill_cb; + u->sink_input->attach = sink_input_attach_cb; + u->sink_input->detach = sink_input_detach_cb; + u->sink_input->state_change = sink_input_state_change_cb; + u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->userdata = u; + + pa_sink_put(u->sink); + pa_sink_input_put(u->sink_input); + + pa_modargs_free(ma); + + pa_xfree(use_default); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa_xfree(use_default); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink) { + pa_sink_unlink(u->sink); + pa_sink_unref(u->sink); + } + + if (u->sink_input) { + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + + if (u->memblockq) + pa_memblockq_free(u->memblockq); + + fftwf_destroy_plan(u->inverse_plan); + fftwf_destroy_plan(u->forward_plan); + fftwf_free(u->output_window); + for(size_t c=0;cchannels;++c){ + fftwf_free(u->output_buffer[c]); + fftwf_free(u->overlap_accum[c]); + fftwf_free(u->input[c]); + } + free(u->output_buffer); + free(u->overlap_accum); + free(u->input); + fftwf_free(u->work_buffer); + fftwf_free(u->W); + fftwf_free(u->H); + + pa_xfree(u); +} -- cgit From 2e119060cb6b61fa59976b636300eea913c20827 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 15 Jul 2009 06:57:33 -0700 Subject: module-equalizer-sink: added temporary debugging output to track filter output removed dead code only a small amount of crackling remains --- src/modules/module-equalizer-sink.c | 268 ++++++++++++++---------------------- 1 file changed, 106 insertions(+), 162 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 8b34fa0d..cabb0dc3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -1,4 +1,29 @@ +/*** +This file is part of PulseAudio. +This module is based off Lennart Poettering's LADSPA sink and swaps out +LADSPA functionality for a STFT OLA based digital equalizer. All new work +is published under Pulseaudio's original license. +Copyright 2009 Jason Newton + +Original Author: +Copyright 2004-2008 Lennart Poettering + +PulseAudio is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation; either version 2.1 of the License, +or (at your option) any later version. + +PulseAudio 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 Lesser General Public License +along with PulseAudio; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA. +***/ #ifdef HAVE_CONFIG_H #include @@ -25,9 +50,6 @@ #include #include #include -#include -#include - #include #include @@ -50,9 +72,15 @@ struct userdata { pa_sink_input *sink_input; size_t channels; - size_t fft_size; //length (res) of fft - size_t window_size;//even! - size_t overlap_size; + size_t fft_size;//length (res) of fft + size_t window_size;/* + *sliding window size + *effectively chooses R + */ + size_t R;/* the hop size between overlapping windows + * the latency of the filter, calculated from window_size + * based on constraints of COLA and window function + */ size_t samples_gathered; size_t n_buffered_output; size_t max_output; @@ -61,6 +89,7 @@ struct userdata { float *work_buffer,**input,**overlap_accum,**output_buffer; fftwf_complex *output_window; fftwf_plan forward_plan,inverse_plan; + //size_t samplings; pa_memblockq *memblockq; }; @@ -106,8 +135,9 @@ void hamming_window(float *W,size_t window_size){ m/=(window_size-1); W[i]=.54-.46*cos(2*M_PI*m); } - W[0]/=2; - W[window_size-1]/=2; + W[window_size-1]=0; + //W[0]/=2; + //W[window_size-1]/=2; } void blackman_window(float *W,size_t window_size){ //h=.42-.5*cos(2*pi*m)+.08*cos(4*pi*m), m=(0:W-1)/(W-1) @@ -132,6 +162,10 @@ void sin_window(float *W,size_t window_size){ void array_out(const char *name,float *a,size_t length){ FILE *p=fopen(name,"w"); + if(!p){ + pa_log("opening %s failed!",name); + return; + } for(size_t i=0;in_buffered_output>0){ //pa_log("outputing %ld buffered samples",u->n_buffered_output); chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs); + size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); chunk->length = n_outputable*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); @@ -245,10 +278,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u->n_buffered_output==0); //collect the minimum number of samples - while(u->samples_gathered < (u->window_size-u->overlap_size)){ + //TODO figure out a better way of buffering the needed + //number of samples, this doesn't seem to work correctly + while(u->samples_gathered < u->R){ //render some new fragments to our memblockq - //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output); - size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output); + size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; @@ -259,137 +293,80 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if(tchunk.length/fs!=desired_samples){ pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); } - size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered); + size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); //TODO: figure out what to do with rest of the samples when there's too many (rare?) src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); } - u->samples_gathered+=n_samples; pa_memblock_release(tchunk.memblock); pa_memblock_unref(tchunk.memblock); } //IT should be this guy if we're buffering like how its supposed to - //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs); + //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); //This one takes into account the actual data gathered but then the dsp //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs); - /* - //debugging: tests if immediate release of freshly buffered data - //plays ok and prevents any other processing - chunk->index=0; - chunk->length=n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock);; - for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable); - } - u->samples_gathered=0; - pa_memblock_release(chunk->memblock); - return 0; - */ + size_t n_outputable=PA_MIN(u->R,u->max_output); - //pa_log("%ld dequed samples",u->samples_gathered); chunk->index=0; chunk->length=n_outputable*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); dst = (float*) pa_memblock_acquire(chunk->memblock); - //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples); - //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples); - - /* - struct timespec start, end; - uint64_t elapsed; - clock_gettime(CLOCK_MONOTONIC, &start); - */ - //use a zero-phase sliding dft and overlap-add method pa_assert_se(u->fft_size>=u->window_size); - //pa_assert_se(u->window_size%2==0); - pa_assert_se(u->overlap_sizewindow_size); - pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size); - size_t sample_rem=u->window_size-u->overlap_size-n_outputable; - //size_t w_mid=u->window_size/2; - //pa_log("hello world a"); - for (c=0;cchannels;c++) { - //center the data for zero phase - //zero-pad TODO: optimization if sure these zeros aren't overwritten - //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float)); + pa_assert_se(u->Rwindow_size); + pa_assert_se(u->samples_gathered>=u->R); + size_t sample_rem=u->R-n_outputable; + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + ////zero padd the data //memset(u->work_buffer,0,u->fft_size*sizeof(float)); - /* - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - u->work_buffer[j]=u->input[c][j]; - } - */ - //zero padd the data, don't worry about zerophase, shouldn't really matter - memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float)); - //window the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + ////window the data for(size_t j=0;jwindow_size;++j){ u->work_buffer[j]=u->W[j]*u->input[c][j]; } - /* - //recenter for zero phase - for(size_t j=0;jwork_buffer[j]; - u->work_buffer[j]=u->input[c][j+w_mid]; - u->work_buffer[j+u->fft_size-w_mid]=tmp; - } - */ - //pa_log("hello world b"); - - /* - //window and zero phase shift - for(size_t j=0;jwork_buffer[j]=u->input[c][j+w_mid]; - //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j]; - u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid]; - u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j]; - }*/ //Processing is done here! //do fft + //char fname[1024]; + //if(u->samplings==200){ + // pa_assert_se(0); + //} + + //this iterations input + //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c); + //array_out(fname,u->input[c]+(u->window_size-u->R),u->R); + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); //perform filtering for(size_t j=0;jfft_size/2+1;++j){ - ////identity transform (fft size) - //u->output_window[j][0]/=u->fft_size; - //u->output_window[j][1]/=u->fft_size; - ////identity transform (window size) - //u->output_window[j][0]/=u->window_size; - //u->output_window[j][1]/=u->window_size; - //filtered u->output_window[j][0]*=u->H[j]; u->output_window[j][1]*=u->H[j]; } - //inverse fft + ////inverse fft fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + //the output for the previous iteration's input + //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c); + //array_out(fname,u->work_buffer,u->window_size); - /* - //uncenter the data - for(size_t j=0;jwork_buffer[j]; - u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid]; - u->work_buffer[j+w_mid]=tmp; - } - */ - /* - //divide out fft gain (more stable here?) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]/=u->fft_size; - } - */ - /* - //debug: tests overlaping add - //and negates ALL PREVIOUS processing - //yields a perfect reconstruction if COLA is held - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; + + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j]; } - */ + + /* //debug: tests if basic buffering works //shouldn't modify the signal AT ALL @@ -398,40 +375,20 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } */ - /* - //overlap add and preserve overlap component from this window (zero phase) - for(size_t j=0;joverlap_size;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; - } - */ - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;joverlap_size;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; - } - //preseve the needed input for the next windows overlap - memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float)); + memmove(u->input[c],u->input[c]+u->R, + (u->window_size-u->R)*sizeof(float) + ); //output the samples that are outputable now pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); //buffer the rest of them memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); + } - /* - clock_gettime(CLOCK_MONOTONIC, &end); - elapsed=time_diff(&end, &start); - pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed); - */ + //u->samplings++; u->n_buffered_output+=sample_rem; u->samples_gathered=0; - - - //pa_log("%ld samples queued",u->n_buffered_output); - pa_memblock_release(chunk->memblock); - - return 0; } @@ -456,6 +413,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (amount > 0) { pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); + u->n_buffered_output=0; + u->samples_gathered=0; } } @@ -621,18 +580,12 @@ int pa__init(pa_module*m) { u->sink_input = NULL; u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); - //u->fft_size=44100; - //u->fft_size=48000; - //u->fft_size=1024; + //u->samplings=0; u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); - //u->fft_size=ss.rate; - //u->fft_size=65536; pa_log("fft size: %ld",u->fft_size); - u->window_size=8001; - u->overlap_size=(u->window_size+1)/2; - //u->overlap_size=u->window_size/2; - //u->overlap_size=0; + u->window_size=7999; + u->R=(u->window_size+1)/2; u->samples_gathered=0; u->n_buffered_output=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); @@ -645,34 +598,26 @@ int pa__init(pa_module*m) { for(size_t c=0;cchannels;++c){ u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); memset(u->input[c],0,u->window_size*sizeof(float)); - u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float)); - memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + memset(u->overlap_accum[c],0,u->R*sizeof(float)); u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); - /* - //rectangular window for(size_t j=0;jwindow_size;++j){ - u->W[j]=1.0; + u->W[j]=.5; } */ - //hanning_normalized_window(u->W,u->window_size); hanning_window(u->W,u->window_size); - //sin_window(u->W,u->window_size); - array_out("/home/jason/window.txt",u->W,u->window_size); - //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE); - //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); - //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE); - //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE); + const int freqs[]={0,25,50,100,200,300,400,800,1500, 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1, + .1,.1,.1,.1,.1,.1,.1,.1, + .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1}; const size_t ncoefficients=sizeof(coefficients)/sizeof(float); pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); @@ -683,13 +628,13 @@ int pa__init(pa_module*m) { //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); pa_assert_se(freq_translated[i]>=freq_translated[i-1]); } - freq_translated[ncoefficients-1]=DBL_MAX; + freq_translated[ncoefficients-1]=FLT_MAX; //Interpolate the specified frequency band values u->H[0]=1; for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ pa_assert_se(j=DBL_MAX){ + if(freq_translated[j+1]>=FLT_MAX){ for(;i<(u->fft_size/2+1);++i){ u->H[i]=coefficients[j]; } @@ -709,9 +654,8 @@ int pa__init(pa_module*m) { j++; } } - array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1); //divide out the fft gain - for(int i=0;i<(u->fft_size/2+1);++i){ + for(size_t i=0;i<(u->fft_size/2+1);++i){ u->H[i]/=u->fft_size; } free(freq_translated); -- cgit From 182c9c7dcb3c7e6b8d273ab85e6f3e67070a4694 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 16 Jul 2009 18:18:14 -0700 Subject: module-equalizer-sink: added more assertions to aid in debugging --- src/modules/module-equalizer-sink.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index cabb0dc3..d4cec3cd 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -251,8 +251,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); - size_t fs = pa_frame_size(&u->sink->sample_spec); - size_t ss=pa_sample_size(&u->sink->sample_spec); + pa_assert_se(u->sink); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t ss=pa_sample_size(&(u->sink->sample_spec)); size_t fe = fs/ss; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) @@ -597,10 +598,14 @@ int pa__init(pa_module*m) { u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + pa_assert_se(u->input[c]); memset(u->input[c],0,u->window_size*sizeof(float)); + pa_assert_se(u->input[c]); u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + pa_assert_se(u->overlap_accum[c]); memset(u->overlap_accum[c],0,u->R*sizeof(float)); u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + pa_assert_se(u->output_buffer[c]); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); @@ -615,9 +620,9 @@ int pa__init(pa_module*m) { const int freqs[]={0,25,50,100,200,300,400,800,1500, 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1, - .1,.1,.1,.1,.1,.1,.1,.1, - .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1}; + const float coefficients[]={1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; const size_t ncoefficients=sizeof(coefficients)/sizeof(float); pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); -- cgit From d4fe5bfce988765fd51d291c61217ffef9df7698 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 16 Jul 2009 22:00:38 -0700 Subject: module-equalizer-sink: attempt different buffering strategy --- src/modules/module-equalizer-sink.c | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d4cec3cd..d6e28f3d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -259,29 +259,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - //output any buffered outputs first - if(u->n_buffered_output>0){ - //pa_log("outputing %ld buffered samples",u->n_buffered_output); - chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); - chunk->length = n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - for(size_t j=0;jchannels;++j){ - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); - memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); - } - u->n_buffered_output-=n_outputable; - pa_memblock_release(chunk->memblock); - return 0; - } - pa_assert_se(u->n_buffered_output==0); - //collect the minimum number of samples //TODO figure out a better way of buffering the needed //number of samples, this doesn't seem to work correctly - while(u->samples_gathered < u->R){ + //most of the itme + if(u->samples_gathered < u->R){ //render some new fragments to our memblockq size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { @@ -295,7 +277,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); } size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); - //TODO: figure out what to do with rest of the samples when there's too many (rare?) + pa_assert_se(n_samples<=u->R-u->samples_gathered); src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); for (size_t c=0;cchannels;c++) { pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); @@ -304,12 +286,33 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_release(tchunk.memblock); pa_memblock_unref(tchunk.memblock); } + //output any buffered outputs first + if(u->n_buffered_output>0){ + //pa_log("outputing %ld buffered samples",u->n_buffered_output); + chunk->index = 0; + size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); + chunk->length = n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + for(size_t j=0;jchannels;++j){ + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); + memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); + } + u->n_buffered_output-=n_outputable; + pa_memblock_release(chunk->memblock); + return 0; + } + pa_assert_se(u->n_buffered_output==0); + + if(u->samples_gatheredR){ + return -1; + } //IT should be this guy if we're buffering like how its supposed to //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); //This one takes into account the actual data gathered but then the dsp //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->R,u->max_output); - + size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered); chunk->index=0; chunk->length=n_outputable*fs; @@ -319,7 +322,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u->fft_size>=u->window_size); pa_assert_se(u->Rwindow_size); - pa_assert_se(u->samples_gathered>=u->R); size_t sample_rem=u->R-n_outputable; //use a linear-phase sliding STFT and overlap-add method (for each channel) for (size_t c=0;cchannels;c++) { @@ -389,6 +391,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //u->samplings++; u->n_buffered_output+=sample_rem; u->samples_gathered=0; +end: pa_memblock_release(chunk->memblock); return 0; } -- cgit From cf8331a0da9df5e4eff6105a002f4912be673d0a Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 18 Jul 2009 01:00:35 -0700 Subject: module-equalizer-sink: trying new buffering strategies --- src/modules/module-equalizer-sink.c | 303 +++++++++++++++++++----------------- src/pulsecore/memblock.c | 2 +- 2 files changed, 160 insertions(+), 145 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d6e28f3d..1d4a423d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -81,9 +81,10 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ + size_t overlap_size;//window_size-R size_t samples_gathered; - size_t n_buffered_output; size_t max_output; + size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer,**input,**overlap_accum,**output_buffer; @@ -91,7 +92,8 @@ struct userdata { fftwf_plan forward_plan,inverse_plan; //size_t samplings; - pa_memblockq *memblockq; + pa_memchunk conv_buffer; + pa_memblockq *rendered_q; }; static const char* const valid_modargs[] = { @@ -186,12 +188,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; + size_t fs=pa_frame_size(ss); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss); + usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -227,7 +231,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -246,153 +250,159 @@ static void sink_update_requested_latency(pa_sink *s) { /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; - float *src, *dst; - pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); pa_assert_se(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); size_t ss=pa_sample_size(&(u->sink->sample_spec)); size_t fe = fs/ss; + size_t samples_requested=nbytes/fs; + pa_memchunk tchunk; + chunk->memblock=NULL; + size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - //collect the minimum number of samples - //TODO figure out a better way of buffering the needed - //number of samples, this doesn't seem to work correctly - //most of the itme - if(u->samples_gathered < u->R){ - //render some new fragments to our memblockq - size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); - while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { - pa_memchunk nchunk; - - pa_sink_render(u->sink, desired_samples*fs, &nchunk); - pa_memblockq_push(u->memblockq, &nchunk); - pa_memblock_unref(nchunk.memblock); - } - if(tchunk.length/fs!=desired_samples){ - pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); + pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered); + //collect samples + size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs; + size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs; + size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing); + if(desired_samples>0){ + u->conv_buffer.index=0; + //if we still had buffered output, + //or can gather any more in the buffer + //politely request (optimistic) + if(buffered_samples>=samples_requested || + (u->samples_gathered/u->R)*u->R>=samples_requested){ + u->conv_buffer.length=desired_samples*fs; + pa_log("trying to buffer %ld samples",desired_samples); + pa_sink_render_into(u->sink, &u->conv_buffer); + }else{//we need it now! force it + //TODO: minimum amount or the whole buffer better? + desired_samples=u->R-u->samples_gathered%u->R; + u->conv_buffer.length=desired_samples*fs; + pa_log("force-buffer %ld samples",desired_samples); + pa_sink_render_into_full(u->sink, &u->conv_buffer); + pa_assert_se(u->conv_buffer.length==desired_samples*fs); } - size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); - pa_assert_se(n_samples<=u->R-u->samples_gathered); - src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); + size_t n_samples=u->conv_buffer.length/fs; + float *src; + pa_log("received %ld samples",n_samples); + + pa_assert_se(n_samples<=u->target_samples-u->samples_gathered); + src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index); for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + //buffer with an offset after the overlap from previous + //iterations + pa_assert_se( + u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); } u->samples_gathered+=n_samples; - pa_memblock_release(tchunk.memblock); - pa_memblock_unref(tchunk.memblock); - } - //output any buffered outputs first - if(u->n_buffered_output>0){ - //pa_log("outputing %ld buffered samples",u->n_buffered_output); - chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); - chunk->length = n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - for(size_t j=0;jchannels;++j){ - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); - memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); - } - u->n_buffered_output-=n_outputable; - pa_memblock_release(chunk->memblock); - return 0; - } - pa_assert_se(u->n_buffered_output==0); - - if(u->samples_gatheredR){ - return -1; + pa_memblock_release(u->conv_buffer.memblock); } - //IT should be this guy if we're buffering like how its supposed to - //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); - //This one takes into account the actual data gathered but then the dsp - //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered); - - chunk->index=0; - chunk->length=n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - + //pa_assert_se(u->samples_gathered>=u->R); pa_assert_se(u->fft_size>=u->window_size); pa_assert_se(u->Rwindow_size); - size_t sample_rem=u->R-n_outputable; - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - ////zero padd the data - //memset(u->work_buffer,0,u->fft_size*sizeof(float)); - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - ////window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - //char fname[1024]; - //if(u->samplings==200){ - // pa_assert_se(0); - //} - - //this iterations input - //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c); - //array_out(fname,u->input[c]+(u->window_size-u->R),u->R); - - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - ////inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - //the output for the previous iteration's input - //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c); - //array_out(fname,u->work_buffer,u->window_size); - - - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} - - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j]; - } - - - /* - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; + //process every complete block on hand + while(u->samples_gathered>=u->R&&buffer_missing>=u->R){ + float *dst; + //pa_log("iter gathered: %ld",u->samples_gathered); + tchunk.index=0; + tchunk.length=u->R*fs; + tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + //pa_memblockq_drop(u->rendered_q, tchunk.length); + pa_assert_se(tchunk.length==u->R*fs); + dst=(float*)pa_memblock_acquire(tchunk.memblock); + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + //zero padd the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; + } + + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL (beyond roundoff) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + + //preseve the needed input for the next window's overlap + memmove(u->input[c],u->input[c]+u->R, + (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) + ); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R); } - */ - - //preseve the needed input for the next windows overlap - memmove(u->input[c],u->input[c]+u->R, - (u->window_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); - //buffer the rest of them - memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); - + pa_memblock_release(tchunk.memblock); + pa_memblockq_push(u->rendered_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + u->samples_gathered-=u->R; + buffer_missing-=u->R; } - //u->samplings++; - u->n_buffered_output+=sample_rem; - u->samples_gathered=0; -end: - pa_memblock_release(chunk->memblock); + //deque from renderq and output + //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs); + pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0); + if(tchunk.length>=nbytes){ + *chunk=tchunk; + chunk->length=samples_requested*fs; + pa_memblock_ref(chunk->memblock); + pa_memblock_unref(tchunk.memblock); + pa_memblockq_drop(u->rendered_q, chunk->length); + }else{ + size_t copied=0; + chunk->length=nbytes; + chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); + uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); + do{ + size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied); + uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); + memmove(dst+copied,src,l); + copied+=l; + pa_memblock_release(tchunk.memblock); + pa_memblock_unref(tchunk.memblock); + pa_memblockq_drop(u->rendered_q,l); + if(copiedrendered_q)==0){ + chunk->length=copied; + break; + } + pa_memblockq_peek(u->rendered_q,&tchunk); + } + }while(copiedmemblock); + } + pa_assert_se(chunk->memblock); + pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs); + //pa_log("end pop"); return 0; } @@ -410,20 +420,19 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; - max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); + max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); - u->n_buffered_output=0; u->samples_gathered=0; } } pa_sink_process_rewind(u->sink, amount); - pa_memblockq_rewind(u->memblockq, nbytes); + pa_memblockq_rewind(u->rendered_q, nbytes); } /* Called from I/O thread context */ @@ -436,7 +445,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_memblockq_set_maxrewind(u->memblockq, nbytes); + pa_memblockq_set_maxrewind(u->rendered_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -582,17 +591,20 @@ int pa__init(pa_module*m) { u->master = master; u->sink = NULL; u->sink_input = NULL; - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); - //u->samplings=0; u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld",u->fft_size); u->window_size=7999; u->R=(u->window_size+1)/2; + u->overlap_size=u->window_size-u->R; + u->target_samples=5*u->R; u->samples_gathered=0; - u->n_buffered_output=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); + u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); + + u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); @@ -600,9 +612,9 @@ int pa__init(pa_module*m) { u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ - u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c],0,u->window_size*sizeof(float)); + memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); pa_assert_se(u->overlap_accum[c]); @@ -780,8 +792,11 @@ void pa__done(pa_module*m) { pa_sink_input_unref(u->sink_input); } - if (u->memblockq) - pa_memblockq_free(u->memblockq); + if(u->conv_buffer.memblock) + pa_memblock_unref(u->conv_buffer.memblock); + + if (u->rendered_q) + pa_memblockq_free(u->rendered_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index f38b17c6..eac4a59b 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -54,7 +54,7 @@ * stored in SHM and our OS does not commit the memory before we use * it for the first time. */ #define PA_MEMPOOL_SLOTS_MAX 1024 -#define PA_MEMPOOL_SLOT_SIZE (64*1024) +#define PA_MEMPOOL_SLOT_SIZE (128*1024) #define PA_MEMEXPORT_SLOTS_MAX 128 -- cgit From 09d9096069360d1eecd30b11df7b4c7d2c39ac35 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 21 Jul 2009 03:24:57 -0700 Subject: module-equalizer-sink: simplified sink_input pop callback and introduced new variables that simplify different strategies. --- src/modules/module-equalizer-sink.c | 341 +++++++++++++++++++++--------------- 1 file changed, 198 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 1d4a423d..970b20d0 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -65,6 +65,7 @@ PA_MODULE_USAGE(_("sink= ")); #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + struct userdata { pa_core *core; pa_module *module; @@ -107,6 +108,21 @@ static const char* const valid_modargs[] = { NULL }; +uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); +void hanning_normalized_window(float *W,size_t window_size); +void hanning_window(float *W,size_t window_size); +void hamming_window(float *W,size_t window_size); +void blackman_window(float *W,size_t window_size); +void sin_window(float *W,size_t window_size); +void array_out(const char *name,float *a,size_t length); + +static void dsp_logic(float *dst,struct userdata *u); +static void process_samples(struct userdata *u); +void input_buffer(struct userdata *u,pa_memchunk *in); + +#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) +#define tdiff(x,y) time_diff(&x,&y) + uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - @@ -188,13 +204,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; - size_t fs=pa_frame_size(ss); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + usec+=pa_bytes_to_usec(u->R*fs,ss); + //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); @@ -247,6 +264,90 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } +static void process_samples(struct userdata *u){ + pa_memchunk tchunk; + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + while(u->samples_gathered>=u->R){ + float *dst; + //pa_log("iter gathered: %ld",u->samples_gathered); + //pa_memblockq_drop(u->rendered_q, tchunk.length); + tchunk.index=0; + tchunk.length=u->R*fs; + tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + dst=((float*)pa_memblock_acquire(tchunk.memblock)); + dsp_logic(dst,u); + pa_memblock_release(tchunk.memblock); + pa_memblockq_push(u->rendered_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + u->samples_gathered-=u->R; + } +} + +static void dsp_logic(float *dst,struct userdata *u){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + //zero padd the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; + } + + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL (beyond roundoff) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + + //preseve the needed input for the next window's overlap + memmove(u->input[c],u->input[c]+u->R, + (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) + ); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + } +} + +void input_buffer(struct userdata *u,pa_memchunk *in){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t samples=in->length/fs; + pa_assert_se(samples<=u->target_samples-u->samples_gathered); + float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + for (size_t c=0;cchannels;c++) { + //buffer with an offset after the overlap from previous + //iterations + pa_assert_se( + u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples); + } + u->samples_gathered+=samples; + pa_memblock_release(in->memblock); +} + /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; @@ -255,153 +356,98 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u = i->userdata); pa_assert_se(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t ss=pa_sample_size(&(u->sink->sample_spec)); - size_t fe = fs/ss; size_t samples_requested=nbytes/fs; + size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; chunk->memblock=NULL; - size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered); - //collect samples - size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs; - size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs; - size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing); - if(desired_samples>0){ - u->conv_buffer.index=0; - //if we still had buffered output, - //or can gather any more in the buffer - //politely request (optimistic) - if(buffered_samples>=samples_requested || - (u->samples_gathered/u->R)*u->R>=samples_requested){ - u->conv_buffer.length=desired_samples*fs; - pa_log("trying to buffer %ld samples",desired_samples); - pa_sink_render_into(u->sink, &u->conv_buffer); - }else{//we need it now! force it - //TODO: minimum amount or the whole buffer better? - desired_samples=u->R-u->samples_gathered%u->R; - u->conv_buffer.length=desired_samples*fs; - pa_log("force-buffer %ld samples",desired_samples); - pa_sink_render_into_full(u->sink, &u->conv_buffer); - pa_assert_se(u->conv_buffer.length==desired_samples*fs); - } - size_t n_samples=u->conv_buffer.length/fs; - float *src; - pa_log("received %ld samples",n_samples); - - pa_assert_se(n_samples<=u->target_samples-u->samples_gathered); - src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index); - for (size_t c=0;cchannels;c++) { - //buffer with an offset after the overlap from previous - //iterations - pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size - ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); - } - u->samples_gathered+=n_samples; - pa_memblock_release(u->conv_buffer.memblock); - } - //pa_assert_se(u->samples_gathered>=u->R); - pa_assert_se(u->fft_size>=u->window_size); - pa_assert_se(u->Rwindow_size); - //process every complete block on hand - while(u->samples_gathered>=u->R&&buffer_missing>=u->R){ - float *dst; - //pa_log("iter gathered: %ld",u->samples_gathered); - tchunk.index=0; - tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); - //pa_memblockq_drop(u->rendered_q, tchunk.length); - pa_assert_se(tchunk.length==u->R*fs); - dst=(float*)pa_memblock_acquire(tchunk.memblock); - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - //zero padd the data - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - //window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} - - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; - } - - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL (beyond roundoff) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; - } - - //preseve the needed input for the next window's overlap - memmove(u->input[c],u->input[c]+u->R, - (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R); - } - pa_memblock_release(tchunk.memblock); - pa_memblockq_push(u->rendered_q, &tchunk); - pa_memblock_unref(tchunk.memblock); - u->samples_gathered-=u->R; - buffer_missing-=u->R; - } - //deque from renderq and output - //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs); - pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0); - if(tchunk.length>=nbytes){ + pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + struct timespec start,end; + + if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ *chunk=tchunk; - chunk->length=samples_requested*fs; - pa_memblock_ref(chunk->memblock); - pa_memblock_unref(tchunk.memblock); pa_memblockq_drop(u->rendered_q, chunk->length); - }else{ - size_t copied=0; - chunk->length=nbytes; - chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); - uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); - do{ - size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied); - uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); - memmove(dst+copied,src,l); - copied+=l; - pa_memblock_release(tchunk.memblock); - pa_memblock_unref(tchunk.memblock); - pa_memblockq_drop(u->rendered_q,l); - if(copiedrendered_q)==0){ - chunk->length=copied; - break; - } - pa_memblockq_peek(u->rendered_q,&tchunk); - } - }while(copiedmemblock); + return 0; } + do{ + pa_memchunk *buffer; + size_t input_remaining=u->target_samples-u->samples_gathered; + pa_assert(input_remaining>0); + //collect samples + + //buffer=&u->conv_buffer; + //buffer->length=input_remaining*fs; + //buffer->index=0; + //pa_memblock_ref(buffer); + //pa_sink_render_into(u->sink,buffer); + + if(u->sink->thread_info.rewind_requested) + sink_request_rewind(u->sink); + + pa_memchunk p; + buffer=&p; + pa_sink_render(u->sink,u->R*fs,buffer); + buffer->length=PA_MIN(input_remaining*fs,buffer->length); + + //debug block + //pa_memblockq_push(u->rendered_q,buffer); + //pa_memblock_unref(buffer->memblock); + //goto END; + + pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + //copy new input + gettime(start); + input_buffer(u,buffer); + gettime(end); + pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + + pa_memblock_unref(buffer->memblock); + + pa_assert_se(u->fft_size>=u->window_size); + pa_assert_se(u->Rwindow_size); + //process every complete block on hand + + gettime(start); + process_samples(u); + gettime(end); + pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + + buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; + }while(buffered_samplesR); + + //deque from rendered_q and output + pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); + *chunk=tchunk; + pa_memblockq_drop(u->rendered_q, chunk->length); + //if(tchunk.length>=nbytes){ + //chunk->length=PA_MIN(tchunk.length,nbytes); + //}else{ + // size_t copied=0; + // chunk->index=0; + // chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q)); + // chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); + // uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); + // for(;;){ + // size_t l=PA_MIN(tchunk.length,nbytes-copied); + // pa_assert_se(l>0); + // uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); + // memmove(dst+copied,src,l); + // copied+=l; + // pa_memblock_release(tchunk.memblock); + // pa_memblock_unref(tchunk.memblock); + // pa_memblockq_drop(u->rendered_q,l); + // if(copiedlength){ + // pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); + // }else{ + // break; + // } + // } + // pa_memblock_release(chunk->memblock); + //} pa_assert_se(chunk->memblock); - pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs); + pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); return 0; } @@ -411,6 +457,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; size_t amount = 0; + pa_log_debug("Rewind callback!"); pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -425,6 +472,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { + //pa_sample_spec *ss=&u->sink->sample_spec; pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered=0; @@ -459,7 +507,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_sink_set_max_request_within_thread(u->sink, nbytes); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -472,7 +521,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs ); + //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ @@ -504,7 +555,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -598,7 +651,7 @@ int pa__init(pa_module*m) { u->window_size=7999; u->R=(u->window_size+1)/2; u->overlap_size=u->window_size-u->R; - u->target_samples=5*u->R; + u->target_samples=1*u->R; u->samples_gathered=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); @@ -716,6 +769,8 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); + pa_sink_set_max_request(u->sink,u->R*fs); + //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss)); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); -- cgit From 702480a8836eefb95cb41d7d4a05d4065d6560dc Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 23 Jul 2009 03:26:35 -0700 Subject: module-equalizer-sink: first commit of a working state (cpu speed dependant) added noop processing for filter debugability --- src/modules/module-equalizer-sink.c | 56 +++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 970b20d0..4d595e1c 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -82,6 +82,7 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ + size_t latency; size_t overlap_size;//window_size-R size_t samples_gathered; size_t max_output; @@ -210,9 +211,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->R*fs,ss); + usec+=pa_bytes_to_usec(u->latency*fs,ss); //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -316,11 +317,11 @@ static void dsp_logic(float *dst,struct userdata *u){ u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; } - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL (beyond roundoff) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; - } + ////debug: tests if basic buffering works + ////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->input[c][j]; + //} //preseve the needed input for the next window's overlap memmove(u->input[c],u->input[c]+u->R, @@ -363,7 +364,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start,end; if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ @@ -377,31 +378,31 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(input_remaining>0); //collect samples - //buffer=&u->conv_buffer; - //buffer->length=input_remaining*fs; - //buffer->index=0; - //pa_memblock_ref(buffer); - //pa_sink_render_into(u->sink,buffer); + buffer=&u->conv_buffer; + buffer->length=input_remaining*fs; + buffer->index=0; + pa_memblock_ref(buffer->memblock); + pa_sink_render_into(u->sink,buffer); - if(u->sink->thread_info.rewind_requested) - sink_request_rewind(u->sink); + //if(u->sink->thread_info.rewind_requested) + // sink_request_rewind(u->sink); - pa_memchunk p; - buffer=&p; - pa_sink_render(u->sink,u->R*fs,buffer); - buffer->length=PA_MIN(input_remaining*fs,buffer->length); + //pa_memchunk p; + //buffer=&p; + //pa_sink_render(u->sink,u->R*fs,buffer); + //buffer->length=PA_MIN(input_remaining*fs,buffer->length); //debug block //pa_memblockq_push(u->rendered_q,buffer); //pa_memblock_unref(buffer->memblock); //goto END; - pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); //copy new input gettime(start); input_buffer(u,buffer); gettime(end); - pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); pa_memblock_unref(buffer->memblock); @@ -412,7 +413,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk gettime(start); process_samples(u); gettime(end); - pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; }while(buffered_samplesR); @@ -447,7 +448,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk // pa_memblock_release(chunk->memblock); //} pa_assert_se(chunk->memblock); - pa_log("gave %ld",chunk->length/fs); + //pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); return 0; } @@ -522,7 +523,7 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs ); + pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -556,7 +557,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_attach_within_thread(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs); + pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } @@ -656,6 +657,7 @@ int pa__init(pa_module*m) { u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); + u->latency=u->R; u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); @@ -676,8 +678,8 @@ int pa__init(pa_module*m) { pa_assert_se(u->output_buffer[c]); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); - u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); - u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); + u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); /* for(size_t j=0;jwindow_size;++j){ u->W[j]=.5; -- cgit From c7fcc9cc01c807c30b6c96f9995ef2c596c74146 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 27 Jul 2009 01:22:26 -0700 Subject: module-equalizer-sink: removed liboil added sse2 optimized dsp logic implementation cleaned up a bit --- src/Makefile.am | 4 +- src/modules/module-equalizer-sink.c | 397 ++++++++++++++++++++++-------------- 2 files changed, 244 insertions(+), 157 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 10f9a793..281bdf14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1383,9 +1383,9 @@ module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c -module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 4d595e1c..e20e07f0 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -29,11 +29,13 @@ USA. #include #endif +#include #include +#include #include #include -#include - +#include +#include #include #include @@ -55,6 +57,14 @@ USA. #include +//#undef __SSE2__ +#ifdef __SSE2__ +#include +#include +#endif + + + #include "module-equalizer-sink-symdef.h" PA_MODULE_AUTHOR("Jason Newton"); @@ -82,10 +92,12 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ - size_t latency; + size_t latency;//Really just R but made into it's own variable + //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; - size_t max_output; + size_t max_output;//max amount of samples outputable in a single + //message size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) @@ -109,76 +121,39 @@ static const char* const valid_modargs[] = { NULL }; -uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -void hanning_normalized_window(float *W,size_t window_size); -void hanning_window(float *W,size_t window_size); -void hamming_window(float *W,size_t window_size); -void blackman_window(float *W,size_t window_size); -void sin_window(float *W,size_t window_size); -void array_out(const char *name,float *a,size_t length); - -static void dsp_logic(float *dst,struct userdata *u); +static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); +static void hanning_window(float *W,size_t window_size); +static void array_out(const char *name,float *a,size_t length); static void process_samples(struct userdata *u); -void input_buffer(struct userdata *u,pa_memchunk *in); - +static void input_buffer(struct userdata *u,pa_memchunk *in); + +void dsp_logic( + float * __restrict__ dst, + float * __restrict__ src, + float * __restrict__ overlap, + const float * __restrict__ H, + const float * __restrict__ W, + fftwf_complex * __restrict__ output_window, + struct userdata *u); + +#define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) #define tdiff(x,y) time_diff(&x,&y) +#define mround(x,y) (x%y==0?x:(x/y+1)*y) uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { - return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - - ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); + return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) - + ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -void hanning_normalized_window(float *W,size_t window_size){ - //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 ) - float c; - for(size_t i=0;imaster)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->latency*fs,ss); + //usec+=pa_bytes_to_usec(u->latency*fs,ss); //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); - //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -276,7 +251,18 @@ static void process_samples(struct userdata *u){ tchunk.length=u->R*fs; tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); dst=((float*)pa_memblock_acquire(tchunk.memblock)); - dsp_logic(dst,u); + for (size_t c=0;cchannels;c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + } pa_memblock_release(tchunk.memblock); pa_memblockq_push(u->rendered_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -284,54 +270,166 @@ static void process_samples(struct userdata *u){ } } -static void dsp_logic(float *dst,struct userdata *u){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - //zero padd the data - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - //window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} +typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); +typedef union float_vector { + float f[v_size]; + v4sf v; +#ifdef __SSE2__ + __m128 m; +#endif +} float_vector_t; + +////reference implementation +//void dsp_logic( +// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! +// float * __restrict__ src,/*input data w/ overlap at start, +// *automatically cycled in routine +// */ +// float * __restrict__ overlap,//The size of the overlap +// const float * __restrict__ H,//The freq. magnitude scalers filter +// const float * __restrict__ W,//The windowing function +// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// struct userdata *u){ +// //use a linear-phase sliding STFT and overlap-add method (for each channel) +// //zero padd the data +// memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); +// //window the data +// for(size_t j=0;jwindow_size;++j){ +// dst[j]=W[j]*src[j]; +// } +// //Processing is done here! +// //do fft +// fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); +// //perform filtering +// for(size_t j=0;jfft_size/2+1;++j){ +// u->output_window[j][0]*=u->H[j]; +// u->output_window[j][1]*=u->H[j]; +// } +// //inverse fft +// fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); +// ////debug: tests overlaping add +// ////and negates ALL PREVIOUS processing +// ////yields a perfect reconstruction if COLA is held +// //for(size_t j=0;jwindow_size;++j){ +// // u->work_buffer[j]=u->W[j]*u->input[c][j]; +// //} +// +// //overlap add and preserve overlap component from this window (linear phase) +// for(size_t j=0;joverlap_size;++j){ +// u->work_buffer[j]+=overlap[j]; +// overlap[j]=dst[u->R+j]; +// } +// ////debug: tests if basic buffering works +// ////shouldn't modify the signal AT ALL (beyond roundoff) +// //for(size_t j=0;jwindow_size;++j){ +// // u->work_buffer[j]=u->input[c][j]; +// //} +// +// //preseve the needed input for the next window's overlap +// memmove(src,src+u->R, +// (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) +// ); +//} + +//regardless of sse enabled, the loops in here assume +//16 byte aligned addresses and memory allocations divisible by v_size +void dsp_logic( + float * __restrict__ dst,//used as a temp array too, needs to be fft_length! + float * __restrict__ src,/*input data w/ overlap at start, + *automatically cycled in routine + */ + float * __restrict__ overlap,//The size of the overlap + const float * __restrict__ H,//The freq. magnitude scalers filter + const float * __restrict__ W,//The windowing function + fftwf_complex * __restrict__ output_window,//The transformed window'd src + struct userdata *u){//Collection of constants + + const size_t window_size=mround(u->window_size,v_size); + const size_t fft_h=mround(u->fft_size/2+1,v_size/2); + const size_t R=mround(u->R,v_size); + const size_t overlap_size=mround(u->overlap_size,v_size); + + //assert(u->samples_gathered>=u->R); + //zero out the bit beyond the real overlap so we don't add garbage + for(size_t j=overlap_size;j>u->overlap_size;--j){ + overlap[j-1]=0; + } + //use a linear-phase sliding STFT and overlap-add method + //zero padd the data + memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jm=_mm_mul_ps(w->m,s->m); +#else + d->v=w->v*s->v; +#endif + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); + + + //perform filtering - purely magnitude based + for(size_t j=0;jm=_mm_mul_ps(d->m,h.m); +#else + d->v=d->v*h->v; +#endif + } - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; - } - ////debug: tests if basic buffering works - ////shouldn't modify the signal AT ALL (beyond roundoff) - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->input[c][j]; - //} + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); - //preseve the needed input for the next window's overlap - memmove(u->input[c],u->input[c]+u->R, - (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // dst[j]=W[j]*src[j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jm=_mm_add_ps(d->m,o->m); + o->m=((float_vector_t*)(dst+u->R+j))->m; +#else + d->v=d->v+o->v; + o->v=((float_vector_t*)(dst+u->R+j))->v; +#endif } + //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float)); + + //////debug: tests if basic buffering works + //////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j=0;jwindow_size;++j){ + // dst[j]=src[j]; + //} + + //preseve the needed input for the next window's overlap + memmove(src,src+u->R, + (u->overlap_size+u->samples_gathered-u->R)*sizeof(float) + ); } + + void input_buffer(struct userdata *u,pa_memchunk *in){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); size_t samples=in->length/fs; @@ -422,31 +520,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); *chunk=tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); - //if(tchunk.length>=nbytes){ - //chunk->length=PA_MIN(tchunk.length,nbytes); - //}else{ - // size_t copied=0; - // chunk->index=0; - // chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q)); - // chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); - // uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); - // for(;;){ - // size_t l=PA_MIN(tchunk.length,nbytes-copied); - // pa_assert_se(l>0); - // uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); - // memmove(dst+copied,src,l); - // copied+=l; - // pa_memblock_release(tchunk.memblock); - // pa_memblock_unref(tchunk.memblock); - // pa_memblockq_drop(u->rendered_q,l); - // if(copiedlength){ - // pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); - // }else{ - // break; - // } - // } - // pa_memblock_release(chunk->memblock); - //} pa_assert_se(chunk->memblock); //pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); @@ -509,7 +582,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + pa_sink_set_max_request_within_thread(u->sink, nbytes); + //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -523,7 +597,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -557,7 +632,12 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_attach_within_thread(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency); + //TODO: setting this guy minimizes drop outs but doesn't get rid + //of them completely, figure out why + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } @@ -605,6 +685,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } + +//ensure's memory allocated is a multiple of v_size +//and aligned +static void * alloc(size_t x,size_t s){ + size_t f=mround(x*s,sizeof(float)*v_size); + //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16); + //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16); + return fftwf_malloc(f*s); +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -649,7 +739,7 @@ int pa__init(pa_module*m) { u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld",u->fft_size); - u->window_size=7999; + u->window_size=15999; u->R=(u->window_size+1)/2; u->overlap_size=u->window_size-u->R; u->target_samples=1*u->R; @@ -659,32 +749,28 @@ int pa__init(pa_module*m) { u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); u->latency=u->R; - - u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); - u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); - u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); + u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); + u->W=alloc(u->window_size,sizeof(float)); + u->work_buffer=alloc(u->fft_size,sizeof(float)); + memset(u->work_buffer,0,u->fft_size*sizeof(float)); u->input=(float **)malloc(sizeof(float *)*u->channels); u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ - u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float)); + u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float)); pa_assert_se(u->input[c]); memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); - u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float)); pa_assert_se(u->overlap_accum[c]); - memset(u->overlap_accum[c],0,u->R*sizeof(float)); - u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->output_buffer[c]=alloc(u->window_size,sizeof(float)); pa_assert_se(u->output_buffer[c]); } - u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); + u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); - /* - for(size_t j=0;jwindow_size;++j){ - u->W[j]=.5; - } - */ + hanning_window(u->W,u->window_size); const int freqs[]={0,25,50,100,200,300,400,800,1500, @@ -735,6 +821,7 @@ int pa__init(pa_module*m) { } free(freq_translated); + /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -857,18 +944,18 @@ void pa__done(pa_module*m) { fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); - fftwf_free(u->output_window); + free(u->output_window); for(size_t c=0;cchannels;++c){ - fftwf_free(u->output_buffer[c]); - fftwf_free(u->overlap_accum[c]); - fftwf_free(u->input[c]); + free(u->output_buffer[c]); + free(u->overlap_accum[c]); + free(u->input[c]); } free(u->output_buffer); free(u->overlap_accum); free(u->input); - fftwf_free(u->work_buffer); - fftwf_free(u->W); - fftwf_free(u->H); + free(u->work_buffer); + free(u->W); + free(u->H); pa_xfree(u); } -- cgit From 8934c314f6401b953b871bbf5b6810b5fe05a9ac Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 31 Jul 2009 18:10:11 -0700 Subject: module-equalizer-sink: added dbus support removed cruft from inherited from ladspa module and improved clarity switched dsp processing to reference implementation until project is more mature tsched=0 seems to help with the micro-dropouts/crackling! oh my! reformatting/spaces --- src/Makefile.am | 2 +- src/modules/module-equalizer-sink.c | 747 ++++++++++++++++++++++-------------- 2 files changed, 461 insertions(+), 288 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 281bdf14..82bc2f9c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1385,7 +1385,7 @@ module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMIN module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index e20e07f0..d8eb5f3d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -35,7 +35,6 @@ USA. #include #include #include -#include #include #include @@ -52,6 +51,8 @@ USA. #include #include #include +#include +#include #include #include @@ -101,13 +102,18 @@ struct userdata { size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) - float *work_buffer,**input,**overlap_accum,**output_buffer; + float *work_buffer, **input, **overlap_accum; fftwf_complex *output_window; - fftwf_plan forward_plan,inverse_plan; + fftwf_plan forward_plan, inverse_plan; //size_t samplings; + float *Hs[2];//thread updatable copies + pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *rendered_q; + + pa_dbus_protocol *dbus_protocol; + char *dbus_path; }; static const char* const valid_modargs[] = { @@ -122,10 +128,10 @@ static const char* const valid_modargs[] = { }; static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -static void hanning_window(float *W,size_t window_size); -static void array_out(const char *name,float *a,size_t length); +static void hanning_window(float *W, size_t window_size); +static void array_out(const char *name, float *a, size_t length); static void process_samples(struct userdata *u); -static void input_buffer(struct userdata *u,pa_memchunk *in); +static void input_buffer(struct userdata *u, pa_memchunk *in); void dsp_logic( float * __restrict__ dst, @@ -136,10 +142,17 @@ void dsp_logic( fftwf_complex * __restrict__ output_window, struct userdata *u); +static void dbus_init(struct userdata *u); +static void dbus_done(struct userdata *u); +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); + #define v_size 4 -#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) -#define tdiff(x,y) time_diff(&x,&y) -#define mround(x,y) (x%y==0?x:(x/y+1)*y) +#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) +#define tdiff(x, y) time_diff(&x, &y) +#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -147,26 +160,33 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -void hanning_window(float *W,size_t window_size){ +static void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 - for(size_t i=0;isink->sample_spec; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + //size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec+=pa_bytes_to_usec(u->latency*fs,ss); - //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + //usec+=pa_bytes_to_usec(u->latency * fs, ss); + //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); @@ -243,15 +263,15 @@ static void sink_update_requested_latency(pa_sink *s) { static void process_samples(struct userdata *u){ pa_memchunk tchunk; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - while(u->samples_gathered>=u->R){ + while(u->samples_gathered >= u->R){ float *dst; - //pa_log("iter gathered: %ld",u->samples_gathered); + //pa_log("iter gathered: %ld", u->samples_gathered); //pa_memblockq_drop(u->rendered_q, tchunk.length); tchunk.index=0; tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length); dst=((float*)pa_memblock_acquire(tchunk.memblock)); - for (size_t c=0;cchannels;c++) { + for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, u->input[c], @@ -261,7 +281,7 @@ static void process_samples(struct userdata *u){ u->output_window, u ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } pa_memblock_release(tchunk.memblock); pa_memblockq_push(u->rendered_q, &tchunk); @@ -279,60 +299,7 @@ typedef union float_vector { #endif } float_vector_t; -////reference implementation -//void dsp_logic( -// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! -// float * __restrict__ src,/*input data w/ overlap at start, -// *automatically cycled in routine -// */ -// float * __restrict__ overlap,//The size of the overlap -// const float * __restrict__ H,//The freq. magnitude scalers filter -// const float * __restrict__ W,//The windowing function -// fftwf_complex * __restrict__ output_window,//The transformed window'd src -// struct userdata *u){ -// //use a linear-phase sliding STFT and overlap-add method (for each channel) -// //zero padd the data -// memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); -// //window the data -// for(size_t j=0;jwindow_size;++j){ -// dst[j]=W[j]*src[j]; -// } -// //Processing is done here! -// //do fft -// fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); -// //perform filtering -// for(size_t j=0;jfft_size/2+1;++j){ -// u->output_window[j][0]*=u->H[j]; -// u->output_window[j][1]*=u->H[j]; -// } -// //inverse fft -// fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); -// ////debug: tests overlaping add -// ////and negates ALL PREVIOUS processing -// ////yields a perfect reconstruction if COLA is held -// //for(size_t j=0;jwindow_size;++j){ -// // u->work_buffer[j]=u->W[j]*u->input[c][j]; -// //} -// -// //overlap add and preserve overlap component from this window (linear phase) -// for(size_t j=0;joverlap_size;++j){ -// u->work_buffer[j]+=overlap[j]; -// overlap[j]=dst[u->R+j]; -// } -// ////debug: tests if basic buffering works -// ////shouldn't modify the signal AT ALL (beyond roundoff) -// //for(size_t j=0;jwindow_size;++j){ -// // u->work_buffer[j]=u->input[c][j]; -// //} -// -// //preseve the needed input for the next window's overlap -// memmove(src,src+u->R, -// (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) -// ); -//} - -//regardless of sse enabled, the loops in here assume -//16 byte aligned addresses and memory allocations divisible by v_size +//reference implementation void dsp_logic( float * __restrict__ dst,//used as a temp array too, needs to be fft_length! float * __restrict__ src,/*input data w/ overlap at start, @@ -342,106 +309,159 @@ void dsp_logic( const float * __restrict__ H,//The freq. magnitude scalers filter const float * __restrict__ W,//The windowing function fftwf_complex * __restrict__ output_window,//The transformed window'd src - struct userdata *u){//Collection of constants - - const size_t window_size=mround(u->window_size,v_size); - const size_t fft_h=mround(u->fft_size/2+1,v_size/2); - const size_t R=mround(u->R,v_size); - const size_t overlap_size=mround(u->overlap_size,v_size); - - //assert(u->samples_gathered>=u->R); - //zero out the bit beyond the real overlap so we don't add garbage - for(size_t j=overlap_size;j>u->overlap_size;--j){ - overlap[j-1]=0; - } - //use a linear-phase sliding STFT and overlap-add method + struct userdata *u){ + //use a linear-phase sliding STFT and overlap-add method (for each channel) //zero padd the data - memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data - for(size_t j=0;jm=_mm_mul_ps(w->m,s->m); -#else - d->v=w->v*s->v; -#endif + for(size_t j = 0;j < u->window_size; ++j){ + dst[j] = W[j] * src[j]; } //Processing is done here! //do fft - fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); - - - //perform filtering - purely magnitude based - for(size_t j=0;jm=_mm_mul_ps(d->m,h.m); -#else - d->v=d->v*h->v; -#endif + fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); + //perform filtering + for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){ + u->output_window[j][0] *= u->H[j]; + u->output_window[j][1] *= u->H[j]; } - - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); - + fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); ////debug: tests overlaping add ////and negates ALL PREVIOUS processing ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // dst[j]=W[j]*src[j]; + //for(size_t j = 0; j < u->window_size; ++j){ + // u->work_buffer[j] = u->W[j] * u->input[c][j]; //} //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jm=_mm_add_ps(d->m,o->m); - o->m=((float_vector_t*)(dst+u->R+j))->m; -#else - d->v=d->v+o->v; - o->v=((float_vector_t*)(dst+u->R+j))->v; -#endif + for(size_t j = 0;j < u->overlap_size; ++j){ + u->work_buffer[j] += overlap[j]; + overlap[j] = dst[u->R+j]; } - //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float)); - - //////debug: tests if basic buffering works - //////shouldn't modify the signal AT ALL (beyond roundoff) - //for(size_t j=0;jwindow_size;++j){ - // dst[j]=src[j]; + ////debug: tests if basic buffering works + ////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j = 0; j < u->window_size;++j){ + // u->work_buffer[j] = u->input[c][j]; //} //preseve the needed input for the next window's overlap - memmove(src,src+u->R, - (u->overlap_size+u->samples_gathered-u->R)*sizeof(float) + memmove(src, src+u->R, + ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float) ); } +////regardless of sse enabled, the loops in here assume +////16 byte aligned addresses and memory allocations divisible by v_size +//void dsp_logic( +// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! +// float * __restrict__ src,/*input data w/ overlap at start, +// *automatically cycled in routine +// */ +// float * __restrict__ overlap,//The size of the overlap +// const float * __restrict__ H,//The freq. magnitude scalers filter +// const float * __restrict__ W,//The windowing function +// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// struct userdata *u){//Collection of constants +// +// const size_t window_size = mround(u->window_size,v_size); +// const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2); +// //const size_t R = mround(u->R, v_size); +// const size_t overlap_size = mround(u->overlap_size, v_size); +// +// //assert(u->samples_gathered >= u->R); +// //zero out the bit beyond the real overlap so we don't add garbage +// for(size_t j = overlap_size; j > u->overlap_size; --j){ +// overlap[j-1] = 0; +// } +// //use a linear-phase sliding STFT and overlap-add method +// //zero padd the data +// memset(dst + u->window_size, 0, (u->fft_size - u->window_size)*sizeof(float)); +// //window the data +// for(size_t j = 0; j < window_size; j += v_size){ +// //dst[j] = W[j]*src[j]; +// float_vector_t *d = (float_vector_t*) (dst+j); +// float_vector_t *w = (float_vector_t*) (W+j); +// float_vector_t *s = (float_vector_t*) (src+j); +//#if __SSE2__ +// d->m = _mm_mul_ps(w->m, s->m); +//#else +// d->v = w->v * s->v; +//#endif +// } +// //Processing is done here! +// //do fft +// fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); +// +// +// //perform filtering - purely magnitude based +// for(size_t j = 0;j < fft_h; j+=v_size/2){ +// //output_window[j][0]*=H[j]; +// //output_window[j][1]*=H[j]; +// float_vector_t *d = (float_vector_t*)(output_window+j); +// float_vector_t h; +// h.f[0] = h.f[1] = H[j]; +// h.f[2] = h.f[3] = H[j+1]; +//#if __SSE2__ +// d->m = _mm_mul_ps(d->m, h.m); +//#else +// d->v = d->v*h->v; +//#endif +// } +// +// +// //inverse fft +// fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); +// +// ////debug: tests overlaping add +// ////and negates ALL PREVIOUS processing +// ////yields a perfect reconstruction if COLA is held +// //for(size_t j = 0; j < u->window_size; ++j){ +// // dst[j] = W[j]*src[j]; +// //} +// +// //overlap add and preserve overlap component from this window (linear phase) +// for(size_t j = 0; j < overlap_size; j+=v_size){ +// //dst[j]+=overlap[j]; +// //overlap[j]+=dst[j+R]; +// float_vector_t *d = (float_vector_t*)(dst+j); +// float_vector_t *o = (float_vector_t*)(overlap+j); +//#if __SSE2__ +// d->m = _mm_add_ps(d->m, o->m); +// o->m = ((float_vector_t*)(dst+u->R+j))->m; +//#else +// d->v = d->v+o->v; +// o->v = ((float_vector_t*)(dst+u->R+j))->v; +//#endif +// } +// //memcpy(overlap, dst+u->R, u->overlap_size*sizeof(float)); +// +// //////debug: tests if basic buffering works +// //////shouldn't modify the signal AT ALL (beyond roundoff) +// //for(size_t j = 0; j < u->window_size; ++j){ +// // dst[j] = src[j]; +// //} +// +// //preseve the needed input for the next window's overlap +// memmove(src, src+u->R, +// ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float) +// ); +//} -void input_buffer(struct userdata *u,pa_memchunk *in){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t samples=in->length/fs; - pa_assert_se(samples<=u->target_samples-u->samples_gathered); + +void input_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t samples = in->length/fs; + pa_assert_se(samples <= u->target_samples-u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - for (size_t c=0;cchannels;c++) { + for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size + u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered+=samples; pa_memblock_release(in->memblock); @@ -454,74 +474,81 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); pa_assert_se(u->sink); - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t samples_requested=nbytes/fs; - size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //size_t samples_requested = nbytes/fs; + size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; - chunk->memblock=NULL; + chunk->memblock = NULL; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - struct timespec start,end; + struct timespec start, end; - if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ - *chunk=tchunk; + if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){ + *chunk = tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); return 0; } + + /* + Set the H filter + */ + unsigned H_i = pa_aupdate_read_begin(u->a_H); + u->H = u->Hs[H_i]; + do{ pa_memchunk *buffer; - size_t input_remaining=u->target_samples-u->samples_gathered; + size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); //collect samples - buffer=&u->conv_buffer; - buffer->length=input_remaining*fs; - buffer->index=0; + buffer = &u->conv_buffer; + buffer->length = input_remaining*fs; + buffer->index = 0; pa_memblock_ref(buffer->memblock); - pa_sink_render_into(u->sink,buffer); + pa_sink_render_into(u->sink, buffer); //if(u->sink->thread_info.rewind_requested) // sink_request_rewind(u->sink); //pa_memchunk p; - //buffer=&p; - //pa_sink_render(u->sink,u->R*fs,buffer); - //buffer->length=PA_MIN(input_remaining*fs,buffer->length); + //buffer = &p; + //pa_sink_render(u->sink, u->R*fs, buffer); + //buffer->length = PA_MIN(input_remaining*fs, buffer->length); //debug block - //pa_memblockq_push(u->rendered_q,buffer); + //pa_memblockq_push(u->rendered_q, buffer); //pa_memblock_unref(buffer->memblock); //goto END; //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); //copy new input gettime(start); - input_buffer(u,buffer); + input_buffer(u, buffer); gettime(end); - //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(buffer->memblock); - pa_assert_se(u->fft_size>=u->window_size); - pa_assert_se(u->Rwindow_size); + pa_assert_se(u->fft_size >= u->window_size); + pa_assert_se(u->R < u->window_size); //process every complete block on hand gettime(start); process_samples(u); gettime(end); - //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); - buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; - }while(buffered_samplesR); + buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; + }while(buffered_samples < u->R); //deque from rendered_q and output - pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); - *chunk=tchunk; + pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0); + *chunk = tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); pa_assert_se(chunk->memblock); - //pa_log("gave %ld",chunk->length/fs); + //pa_log("gave %ld", chunk->length/fs); //pa_log("end pop"); return 0; } @@ -546,10 +573,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - //pa_sample_spec *ss=&u->sink->sample_spec; + //pa_sample_spec *ss = &u->sink->sample_spec; pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); - u->samples_gathered=0; + u->samples_gathered = 0; } } @@ -581,9 +608,9 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, nbytes); - //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //pa_sink_set_max_request_within_thread(u->sink, nbytes); + pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -596,9 +623,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -631,9 +658,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid //of them completely, figure out why pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); @@ -689,10 +716,13 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { //ensure's memory allocated is a multiple of v_size //and aligned static void * alloc(size_t x,size_t s){ - size_t f=mround(x*s,sizeof(float)*v_size); - //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16); - //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16); - return fftwf_malloc(f*s); + size_t f = mround(x*s, sizeof(float)*v_size); + pa_assert_se(f >= x*s); + //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); + //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); + float *t = fftwf_malloc(f); + memset(t, 0, f); + return t; } int pa__init(pa_module*m) { @@ -726,7 +756,7 @@ int pa__init(pa_module*m) { pa_log("Invalid sample format specification or channel map"); goto fail; } - fs=pa_frame_size(&ss); + fs = pa_frame_size(&ss); u = pa_xnew0(struct userdata, 1); u->core = m->core; @@ -736,90 +766,96 @@ int pa__init(pa_module*m) { u->sink = NULL; u->sink_input = NULL; - u->channels=ss.channels; - u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); - pa_log("fft size: %ld",u->fft_size); - u->window_size=15999; - u->R=(u->window_size+1)/2; - u->overlap_size=u->window_size-u->R; - u->target_samples=1*u->R; - u->samples_gathered=0; - u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); - u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); - u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); - u->latency=u->R; - - u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); - u->W=alloc(u->window_size,sizeof(float)); - u->work_buffer=alloc(u->fft_size,sizeof(float)); - memset(u->work_buffer,0,u->fft_size*sizeof(float)); - u->input=(float **)malloc(sizeof(float *)*u->channels); - u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); - u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); - for(size_t c=0;cchannels;++c){ - u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float)); + u->channels = ss.channels; + u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); + pa_log("fft size: %ld", u->fft_size); + u->window_size = 7999; + u->R = (u->window_size+1)/2; + u->overlap_size = u->window_size-u->R; + u->target_samples = 1*u->R; + u->samples_gathered = 0; + u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL); + u->a_H = pa_aupdate_new(); + u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); + u->latency = u->R; + for(size_t i = 0; i < 2; ++i){ + u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); + } + u->W = alloc(u->window_size, sizeof(float)); + u->work_buffer = alloc(u->fft_size, sizeof(float)); + memset(u->work_buffer, 0, u->fft_size*sizeof(float)); + u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); + memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float)); pa_assert_se(u->input[c]); - u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float)); + u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); pa_assert_se(u->overlap_accum[c]); - memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); - u->output_buffer[c]=alloc(u->window_size,sizeof(float)); - pa_assert_se(u->output_buffer[c]); - } - u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); - u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); - u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); - - hanning_window(u->W,u->window_size); - - const int freqs[]={0,25,50,100,200,300,400,800,1500, - 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - const size_t ncoefficients=sizeof(coefficients)/sizeof(float); - pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); - freq_translated[0]=1; - //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - for(size_t i=1;ifft_size)/ss.rate; - //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); - pa_assert_se(freq_translated[i]>=freq_translated[i-1]); + memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } - freq_translated[ncoefficients-1]=FLT_MAX; - //Interpolate the specified frequency band values - u->H[0]=1; - for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ - pa_assert_se(j=FLT_MAX){ - for(;i<(u->fft_size/2+1);++i){ - u->H[i]=coefficients[j]; - } - break; - } - //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]); - //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]); - pa_assert_se(freq_translated[j]=freq_translated[j]); - pa_assert_se(i<=freq_translated[j+1]); - //bilinear-inerpolation of coefficients specified - float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - pa_assert_se(c0>=0&&c0<=1.0); - u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - pa_assert_se(u->H[i]>0); - while(i>=floor(freq_translated[j+1])){ - j++; - } - } - //divide out the fft gain - for(size_t i=0;i<(u->fft_size/2+1);++i){ - u->H[i]/=u->fft_size; + u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); + u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); + u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); + + hanning_window(u->W, u->window_size); + + unsigned H_i = pa_aupdate_write_begin(u->a_H); + u->H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + u->H[i] = 1.0; } - free(freq_translated); + + //TODO cut this out and leave it for the client side + //const int freqs[] = {0,25,50,100,200,300,400,800,1500, + // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); + //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); + //freq_translated[0] = 1; + ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + //for(size_t i = 1; i < ncoefficients-1; ++i){ + // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; + // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); + // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); + //} + //freq_translated[ncoefficients-1] = FLT_MAX; + // + ////Interpolate the specified frequency band values + //u->H[0] = 1; + //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ + // pa_assert_se(j < ncoefficients); + // //max frequency range passed, consider the rest as one band + // if(freq_translated[j+1] >= FLT_MAX){ + // for(; i < (u->fft_size / 2 + 1); ++i){ + // u->H[i] = coefficients[j]; + // } + // break; + // } + // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); + // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); + // pa_assert_se(freq_translated[j] < freq_translated[j+1]); + // pa_assert_se(i >= freq_translated[j]); + // pa_assert_se(i <= freq_translated[j+1]); + // //bilinear-inerpolation of coefficients specified + // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + // pa_assert_se(c0 >= 0&&c0 <= 1.0); + // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + // pa_assert_se(u->H[i]>0); + // while(i >= floor(freq_translated[j+1])){ + // j++; + // } + //} + //pa_xfree(freq_translated); + fix_filter(u->H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); /* Create sink */ @@ -858,8 +894,8 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); - pa_sink_set_max_request(u->sink,u->R*fs); - //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss)); + pa_sink_set_max_request(u->sink, u->R*fs); + //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); @@ -896,6 +932,8 @@ int pa__init(pa_module*m) { pa_xfree(use_default); + dbus_init(u); + return 0; fail: @@ -925,6 +963,7 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + dbus_done(u); if (u->sink) { pa_sink_unlink(u->sink); @@ -944,18 +983,152 @@ void pa__done(pa_module*m) { fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); - free(u->output_window); - for(size_t c=0;cchannels;++c){ - free(u->output_buffer[c]); - free(u->overlap_accum[c]); - free(u->input[c]); + pa_xfree(u->output_window); + for(size_t c=0; c < u->channels; ++c){ + pa_xfree(u->overlap_accum[c]); + pa_xfree(u->input[c]); + } + pa_xfree(u->overlap_accum); + pa_xfree(u->input); + pa_xfree(u->work_buffer); + pa_xfree(u->W); + for(size_t i = 0; i < 2; ++i){ + pa_xfree(u->Hs[i]); } - free(u->output_buffer); - free(u->overlap_accum); - free(u->input); - free(u->work_buffer); - free(u->W); - free(u->H); pa_xfree(u); } + +enum property_handler_index { + PROPERTY_HANDLER_N_COEFS, + PROPERTY_HANDLER_COEFS, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ + [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, + [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} +}; + +//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; +//static pa_dbus_signal_info signals[SIGNAL_MAX] = { +// [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} +//}; + +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" + +static pa_dbus_interface_info interface_info={ + .name=EXTNAME ".Equalizer", + .method_handlers=NULL, + .n_method_handlers=0, + .property_handlers=property_handlers, + .n_property_handlers=PROPERTY_HANDLER_MAX, + .get_all_properties_cb=handle_get_all, + .signals=NULL, + .n_signals=0 +}; + + +void dbus_init(struct userdata *u){ + u->dbus_protocol=pa_dbus_protocol_get(u->core); + u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); + + pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u); + pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); +} + +void dbus_done(struct userdata *u){ + pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME); + + pa_xfree(u->dbus_path); + pa_dbus_protocol_unref(u->dbus_protocol); +} + +void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); +} + +void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + + unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1); + double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + + unsigned H_i=pa_aupdate_read_begin(u->a_H); + float *H=u->Hs[H_i]; + for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ + H_[i]=H[i]; + } + pa_aupdate_read_end(u->a_H); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs); + pa_xfree(H_); +} + +void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + double *H_; + unsigned _n_coefs; + pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs); + if(_n_coefs!=u->fft_size / 2 + 1){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); + return; + } + unsigned H_i = pa_aupdate_write_begin(u->a_H); + float *H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H[i] = (float)H_[i]; + } + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + + pa_dbus_send_empty_reply(conn, msg); +} + +void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u = (struct userdata *)_u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + + int n_coefs=(unsigned)(u->fft_size / 2 + 1); + double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + + unsigned H_i=pa_aupdate_read_begin(u->a_H); + float *H=u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H_[i] = H[i]; + } + pa_aupdate_read_end(u->a_H); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + + pa_xfree(H_); +} -- cgit From 66a6cc693bcf441d86ed56a3b15be948008e9de7 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 1 Aug 2009 20:23:49 -0700 Subject: module-equalizer-sink: added support for suspend/resume of filter coefficients unregister the correct dbus interface. made equalizer state file sink index dependent expanded dbus properties whitespace --- src/modules/module-equalizer-sink.c | 219 ++++++++++++++++++++++++------------ 1 file changed, 147 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d8eb5f3d..6ea59514 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -50,7 +50,7 @@ USA. #include #include #include -#include +#include #include #include @@ -114,6 +114,8 @@ struct userdata { pa_dbus_protocol *dbus_protocol; char *dbus_path; + + pa_database *database; }; static const char* const valid_modargs[] = { @@ -145,14 +147,18 @@ void dsp_logic( static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void save_state(struct userdata *u); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) #define tdiff(x, y) time_diff(&x, &y) #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) +#define COEFKEY "coefficients" uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -321,7 +327,7 @@ void dsp_logic( //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering - for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){ + for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){ u->output_window[j][0] *= u->H[j]; u->output_window[j][1] *= u->H[j]; } @@ -702,6 +708,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } +void save_state(struct userdata *u){ + const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); + for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ + H_n[i] = H[i] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); + pa_datum key, data; + key.data = (char *) COEFKEY; + key.size = strlen(key.data); + data.data = H_n; + data.size = (u->fft_size / 2 + 1) * sizeof(float); + pa_database_set(u->database, &key, &data, TRUE); + pa_database_sync(u->database); +} + /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -802,62 +824,6 @@ int pa__init(pa_module*m) { hanning_window(u->W, u->window_size); - unsigned H_i = pa_aupdate_write_begin(u->a_H); - u->H = u->Hs[H_i]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->H[i] = 1.0; - } - - //TODO cut this out and leave it for the client side - //const int freqs[] = {0,25,50,100,200,300,400,800,1500, - // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); - //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); - //freq_translated[0] = 1; - ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - //for(size_t i = 1; i < ncoefficients-1; ++i){ - // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; - // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); - // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); - //} - //freq_translated[ncoefficients-1] = FLT_MAX; - // - ////Interpolate the specified frequency band values - //u->H[0] = 1; - //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ - // pa_assert_se(j < ncoefficients); - // //max frequency range passed, consider the rest as one band - // if(freq_translated[j+1] >= FLT_MAX){ - // for(; i < (u->fft_size / 2 + 1); ++i){ - // u->H[i] = coefficients[j]; - // } - // break; - // } - // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); - // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); - // pa_assert_se(freq_translated[j] < freq_translated[j+1]); - // pa_assert_se(i >= freq_translated[j]); - // pa_assert_se(i <= freq_translated[j+1]); - // //bilinear-inerpolation of coefficients specified - // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - // pa_assert_se(c0 >= 0&&c0 <= 1.0); - // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - // pa_assert_se(u->H[i]>0); - // while(i >= floor(freq_translated[j+1])){ - // j++; - // } - //} - //pa_xfree(freq_translated); - fix_filter(u->H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - - /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -932,6 +898,82 @@ int pa__init(pa_module*m) { pa_xfree(use_default); + char *dbname; + char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name); + pa_assert_se(dbname = pa_state_path(pref, TRUE)); + pa_xfree(pref); + pa_assert_se(u->database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + + unsigned H_i = pa_aupdate_write_begin(u->a_H); + u->H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + u->H[i] = 1.0; + } + + //TODO cut this out and leave it for the client side + //const int freqs[] = {0,25,50,100,200,300,400,800,1500, + // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); + //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); + //freq_translated[0] = 1; + ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + //for(size_t i = 1; i < ncoefficients-1; ++i){ + // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; + // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); + // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); + //} + //freq_translated[ncoefficients-1] = FLT_MAX; + // + ////Interpolate the specified frequency band values + //u->H[0] = 1; + //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ + // pa_assert_se(j < ncoefficients); + // //max frequency range passed, consider the rest as one band + // if(freq_translated[j+1] >= FLT_MAX){ + // for(; i < (u->fft_size / 2 + 1); ++i){ + // u->H[i] = coefficients[j]; + // } + // break; + // } + // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); + // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); + // pa_assert_se(freq_translated[j] < freq_translated[j+1]); + // pa_assert_se(i >= freq_translated[j]); + // pa_assert_se(i <= freq_translated[j+1]); + // //bilinear-inerpolation of coefficients specified + // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + // pa_assert_se(c0 >= 0&&c0 <= 1.0); + // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + // pa_assert_se(u->H[i]>0); + // while(i >= floor(freq_translated[j+1])){ + // j++; + // } + //} + //pa_xfree(freq_translated); + + //load old parameters + pa_datum key,value; + key.data = (char *) COEFKEY; + key.size = strlen(key.data); + if (pa_database_get(u->database, &key, &value) != NULL){ + if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ + memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float)); + } + pa_datum_free(&value); + } + + fix_filter(u->H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + + + dbus_init(u); return 0; @@ -963,6 +1005,10 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + + save_state(u); + pa_database_close(u->database); + dbus_done(u); if (u->sink) { @@ -1000,14 +1046,18 @@ void pa__done(pa_module*m) { } enum property_handler_index { + PROPERTY_HANDLER_SAMPLERATE, + PROPERTY_HANDLER_FILTERSAMPLERATE, PROPERTY_HANDLER_N_COEFS, PROPERTY_HANDLER_COEFS, PROPERTY_HANDLER_MAX }; static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ - [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, - [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} + [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL}, + [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL}, + [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, + [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} }; //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; @@ -1039,7 +1089,7 @@ void dbus_init(struct userdata *u){ void dbus_done(struct userdata *u){ pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); - pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name); pa_xfree(u->dbus_path); pa_dbus_protocol_unref(u->dbus_protocol); @@ -1056,23 +1106,41 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } +void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); +} +void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + uint32_t fft_size=(uint32_t) u->fft_size; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); +} + void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); - struct userdata *u=(struct userdata *)_u; + struct userdata *u = (struct userdata *)_u; - unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1); - double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1); + double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double)); - unsigned H_i=pa_aupdate_read_begin(u->a_H); - float *H=u->Hs[H_i]; + float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - H_[i]=H[i]; + H_[i] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } @@ -1089,14 +1157,17 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; } - unsigned H_i = pa_aupdate_write_begin(u->a_H); - float *H = u->Hs[H_i]; + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float)H_[i]; + H[i] = (float) H_[i]; } + fix_filter(H, u->fft_size); pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); + //Stupid for IO reasons? Add a save signal to dbus instead + save_state(u); + pa_dbus_send_empty_reply(conn, msg); } @@ -1109,7 +1180,9 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - int n_coefs=(unsigned)(u->fft_size / 2 + 1); + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + uint32_t fft_size=(uint32_t) u->fft_size; + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); unsigned H_i=pa_aupdate_read_begin(u->a_H); @@ -1123,6 +1196,8 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); -- cgit From 144f1c4f31fe3cde73a0d9ea08d2ae34cc24bdf6 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 5 Aug 2009 00:52:16 -0700 Subject: module-equalizer-sink: dbus properties and manager so that multiple sinks can be loaded and mixers can be equalizer-sink aware functionality to seed new filters quickly (rteq guis) profile support extra checking in client->server dbus messages --- src/modules/module-equalizer-sink.c | 773 ++++++++++++++++++++++++++++-------- 1 file changed, 613 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 6ea59514..285fddbf 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -50,6 +50,8 @@ USA. #include #include #include +#include +#include #include #include #include @@ -82,6 +84,7 @@ struct userdata { pa_module *module; pa_sink *sink, *master; pa_sink_input *sink_input; + char *name; size_t channels; size_t fft_size;//length (res) of fft @@ -131,9 +134,17 @@ static const char* const valid_modargs[] = { static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); static void hanning_window(float *W, size_t window_size); +void fix_filter(float *H, size_t fft_size); +void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); +static int is_monotonic(uint32_t *xs, size_t length); static void process_samples(struct userdata *u); static void input_buffer(struct userdata *u, pa_memchunk *in); +static void save_profile(struct userdata *u,char *name); +static void save_state(struct userdata *u); +static void remove_profile(pa_core *u,char *name); +static const char * load_profile(struct userdata *u,char *name); +static void load_state(struct userdata *u); void dsp_logic( float * __restrict__ dst, @@ -144,21 +155,37 @@ void dsp_logic( fftwf_complex * __restrict__ output_window, struct userdata *u); + +/* + * DBus Routines and Callbacks + */ +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" +#define MANAGER_PATH "/org/pulseaudio/equalizing1" static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); -static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void save_state(struct userdata *u); +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) #define tdiff(x, y) time_diff(&x, &y) #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) -#define COEFKEY "coefficients" +#define SINKLIST "equalized_sinklist" +#define EQDB "equalizer_db" uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -166,20 +193,43 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -static void hanning_window(float *W, size_t window_size){ +void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 for(size_t i=0; i < window_size;++i){ W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1))); } } -static void fix_filter(float *H, size_t fft_size){ +void fix_filter(float *H, size_t fft_size){ //divide out the fft gain - for(size_t i = 0; i < (fft_size / 2 + 1); ++i){ + for(size_t i = 0; i < fft_size / 2 + 1; ++i){ H[i] /= fft_size; } } +void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ + //Note that xs must be monotonically increasing! + pa_assert_se(n_points>=2); + pa_assert_se(xs[0] == 0); + pa_assert_se(xs[n_points - 1] == length - 1); + for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){ + pa_assert(x_range_lower_i < n_points-1); + float x_range_lower = (float) (xs[x_range_lower_i]); + float x_range_upper = (float) (xs[x_range_lower_i+1]); + pa_assert_se(x_range_lower < x_range_upper); + pa_assert_se(x >= x_range_lower); + pa_assert_se(x <= x_range_upper); + //bilinear-interpolation of coefficients specified + float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); + pa_assert_se(c0 >= 0&&c0 <= 1.0); + signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]); + while(x >= xs[x_range_lower_i + 1]){ + x_range_lower_i++; + } + } + signal[length-1]=ys[n_points-1]; +} + void array_out(const char *name, float *a, size_t length){ FILE *p=fopen(name, "w"); if(!p){ @@ -195,6 +245,17 @@ void array_out(const char *name, float *a, size_t length){ fprintf(p, "\n"); fclose(p); } +static int is_monotonic(uint32_t *xs,size_t length){ + if(length<2){ + return 1; + } + for(size_t i = 1; i < length; ++i){ + if(xs[i]<=xs[i-1]){ + return 0; + } + } + return 1; +} /* Called from I/O thread context */ @@ -708,15 +769,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -void save_state(struct userdata *u){ - const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; +void save_profile(struct userdata *u, char *name){ float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); + const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ - H_n[i] = H[i] * u->fft_size; + //H_n[i] = H[i] * u->fft_size; + H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); pa_datum key, data; - key.data = (char *) COEFKEY; + key.data=name; key.size = strlen(key.data); data.data = H_n; data.size = (u->fft_size / 2 + 1) * sizeof(float); @@ -724,6 +786,49 @@ void save_state(struct userdata *u){ pa_database_sync(u->database); } +void save_state(struct userdata *u){ + char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); + save_profile(u, state_name); + pa_xfree(state_name); +} + +void remove_profile(pa_core *c,char *name){ + pa_datum key; + key.data = name; + key.size = strlen(key.data); + pa_database *database; + pa_assert_se(database = pa_shared_get(c,EQDB)); + pa_database_unset(database,&key); + pa_database_sync(database); +} + +const char* load_profile(struct userdata *u,char *name){ + pa_datum key,value; + key.data = name; + key.size = strlen(key.data); + if(pa_database_get(u->database, &key, &value) != NULL){ + if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ + float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; + memcpy(H, value.data, value.size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + }else{ + return "incompatible size"; + } + pa_datum_free(&value); + }else{ + return "profile doesn't exist"; + } + return NULL; + //fix_filter(u->H, u->fft_size); +} +void load_state(struct userdata *u){ + char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); + load_profile(u,state_name); + pa_xfree(state_name); +} + + /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -791,7 +896,7 @@ int pa__init(pa_module*m) { u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld", u->fft_size); - u->window_size = 7999; + u->window_size = 15999; u->R = (u->window_size+1)/2; u->overlap_size = u->window_size-u->R; u->target_samples = 1*u->R; @@ -851,7 +956,7 @@ int pa__init(pa_module*m) { pa_log("Failed to create sink."); goto fail; } - + u->name=pa_xstrdup(u->sink->name); u->sink->parent.process_msg = sink_process_msg; u->sink->set_state = sink_set_state; u->sink->update_requested_latency = sink_update_requested_latency; @@ -898,83 +1003,14 @@ int pa__init(pa_module*m) { pa_xfree(use_default); - char *dbname; - char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name); - pa_assert_se(dbname = pa_state_path(pref, TRUE)); - pa_xfree(pref); - pa_assert_se(u->database = pa_database_open(dbname, TRUE)); - pa_xfree(dbname); + dbus_init(u); - unsigned H_i = pa_aupdate_write_begin(u->a_H); - u->H = u->Hs[H_i]; + //default filter to these for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->H[i] = 1.0; + u->Hs[1][i]=u->Hs[0][i] = 1.0; } - - //TODO cut this out and leave it for the client side - //const int freqs[] = {0,25,50,100,200,300,400,800,1500, - // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); - //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); - //freq_translated[0] = 1; - ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - //for(size_t i = 1; i < ncoefficients-1; ++i){ - // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; - // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); - // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); - //} - //freq_translated[ncoefficients-1] = FLT_MAX; - // - ////Interpolate the specified frequency band values - //u->H[0] = 1; - //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ - // pa_assert_se(j < ncoefficients); - // //max frequency range passed, consider the rest as one band - // if(freq_translated[j+1] >= FLT_MAX){ - // for(; i < (u->fft_size / 2 + 1); ++i){ - // u->H[i] = coefficients[j]; - // } - // break; - // } - // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); - // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); - // pa_assert_se(freq_translated[j] < freq_translated[j+1]); - // pa_assert_se(i >= freq_translated[j]); - // pa_assert_se(i <= freq_translated[j+1]); - // //bilinear-inerpolation of coefficients specified - // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - // pa_assert_se(c0 >= 0&&c0 <= 1.0); - // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - // pa_assert_se(u->H[i]>0); - // while(i >= floor(freq_translated[j+1])){ - // j++; - // } - //} - //pa_xfree(freq_translated); - //load old parameters - pa_datum key,value; - key.data = (char *) COEFKEY; - key.size = strlen(key.data); - if (pa_database_get(u->database, &key, &value) != NULL){ - if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ - memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float)); - } - pa_datum_free(&value); - } - - fix_filter(u->H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - - - - dbus_init(u); + load_state(u); return 0; @@ -1007,7 +1043,6 @@ void pa__done(pa_module*m) { return; save_state(u); - pa_database_close(u->database); dbus_done(u); @@ -1042,22 +1077,130 @@ void pa__done(pa_module*m) { pa_xfree(u->Hs[i]); } + pa_xfree(u->name); + pa_xfree(u); } -enum property_handler_index { - PROPERTY_HANDLER_SAMPLERATE, - PROPERTY_HANDLER_FILTERSAMPLERATE, - PROPERTY_HANDLER_N_COEFS, - PROPERTY_HANDLER_COEFS, - PROPERTY_HANDLER_MAX +enum manager_method_index { + MANAGER_METHOD_REMOVE_PROFILE, + MANAGER_METHOD_MAX +}; + +pa_dbus_arg_info remove_profile_args[]={ + {"name", "s","in"}, +}; + +static pa_dbus_method_handler manager_methods[MANAGER_METHOD_MAX]={ + [MANAGER_METHOD_REMOVE_PROFILE]{ + .method_name="RemoveProfile", + .arguments=remove_profile_args, + .n_arguments=sizeof(remove_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=manager_handle_remove_profile} +}; + +enum manager_handler_index { + MANAGER_HANDLER_REVISION, + MANAGER_HANDLER_EQUALIZED_SINKS, + MANAGER_HANDLER_PROFILES, + MANAGER_HANDLER_MAX +}; + +static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={ + [MANAGER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=manager_get_revision,.set_cb=NULL}, + [MANAGER_HANDLER_EQUALIZED_SINKS]={.property_name="EqualizedSinks",.type="ao",.get_cb=manager_get_sinks,.set_cb=NULL}, + [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL} +}; + +static pa_dbus_interface_info manager_info={ + .name=EXTNAME ".Manager", + .method_handlers=manager_methods, + .n_method_handlers=MANAGER_METHOD_MAX, + .property_handlers=manager_handlers, + .n_property_handlers=MANAGER_HANDLER_MAX, + .get_all_properties_cb=manager_get_all, + .signals=NULL, + .n_signals=0 +}; + + +enum equalizer_method_index { + EQUALIZER_METHOD_FILTER_POINTS, + EQUALIZER_METHOD_SEED_FILTER, + EQUALIZER_METHOD_SAVE_PROFILE, + EQUALIZER_METHOD_LOAD_PROFILE, + EQUALIZER_METHOD_SET_FILTER, + EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_MAX +}; + +enum equalizer_handler_index { + EQUALIZER_HANDLER_REVISION, + EQUALIZER_HANDLER_SAMPLERATE, + EQUALIZER_HANDLER_FILTERSAMPLERATE, + EQUALIZER_HANDLER_N_COEFS, + EQUALIZER_HANDLER_MAX +}; + +pa_dbus_arg_info filter_points_args[]={ + {"xs", "au","in"}, + {"ys", "ad","out"}, +}; +pa_dbus_arg_info seed_filter_args[]={ + {"xs", "au","in"}, + {"ys", "ad","in"}, +}; +pa_dbus_arg_info save_profile_args[]={ + {"name", "s","in"}, +}; +pa_dbus_arg_info load_profile_args[]={ + {"name", "s","in"}, +}; +pa_dbus_arg_info set_filter_args[]={ + {"coefficients", "ad","in"}, +}; +pa_dbus_arg_info get_filter_args[]={ + {"coefficients", "ad","out"}, +}; + +static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ + [EQUALIZER_METHOD_SEED_FILTER]{ + .method_name="SeedFilter", + .arguments=seed_filter_args, + .n_arguments=sizeof(seed_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_seed_filter}, + [EQUALIZER_METHOD_FILTER_POINTS]{ + .method_name="FilterAtPoints", + .arguments=filter_points_args, + .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_filter_points}, + [EQUALIZER_METHOD_SAVE_PROFILE]{ + .method_name="SaveProfile", + .arguments=save_profile_args, + .n_arguments=sizeof(save_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_save_profile}, + [EQUALIZER_METHOD_LOAD_PROFILE]{ + .method_name="LoadProfile", + .arguments=load_profile_args, + .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_SET_FILTER]{ + .method_name="SetFilterCoefficients", + .arguments=set_filter_args, + .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_set_filter}, + [EQUALIZER_METHOD_GET_FILTER]{ + .method_name="GetFilterCoefficients", + .arguments=get_filter_args, + .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_get_filter} }; -static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ - [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL}, - [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL}, - [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, - [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} +static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ + [EQUALIZER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=equalizer_get_revision,.set_cb=NULL}, + [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, + [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, + [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, }; //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; @@ -1065,37 +1208,354 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ // [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} //}; -#define EXTNAME "org.PulseAudio.Ext.Equalizing1" - -static pa_dbus_interface_info interface_info={ +static pa_dbus_interface_info equalizer_info={ .name=EXTNAME ".Equalizer", - .method_handlers=NULL, - .n_method_handlers=0, - .property_handlers=property_handlers, - .n_property_handlers=PROPERTY_HANDLER_MAX, - .get_all_properties_cb=handle_get_all, + .method_handlers=equalizer_methods, + .n_method_handlers=EQUALIZER_METHOD_MAX, + .property_handlers=equalizer_handlers, + .n_property_handlers=EQUALIZER_HANDLER_MAX, + .get_all_properties_cb=equalizer_get_all, .signals=NULL, .n_signals=0 }; - void dbus_init(struct userdata *u){ u->dbus_protocol=pa_dbus_protocol_get(u->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); - pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u); - pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); + pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST); + u->database=pa_shared_get(u->core,EQDB); + if(sink_list==NULL){ + sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); + pa_shared_set(u->core, SINKLIST, sink_list); + char *dbname; + pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); + pa_assert_se(u->database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + pa_shared_set(u->core,EQDB,u->database); + pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core); + pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); + } + uint32_t dummy; + pa_idxset_put(sink_list,u,&dummy); } void dbus_done(struct userdata *u){ - pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); - pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name); - + pa_idxset *sink_list; + uint32_t dummy; + + pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); + pa_idxset_remove_by_data(sink_list,u,&dummy); + if(pa_idxset_size(sink_list)==0){ + pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name); + pa_shared_remove(u->core, EQDB); + pa_database_close(u->database); + pa_shared_remove(u->core, SINKLIST); + pa_xfree(sink_list); + } + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name); pa_xfree(u->dbus_path); pa_dbus_protocol_unref(u->dbus_protocol); } -void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + DBusError error; + pa_core *c = (pa_core *)_u; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + dbus_error_init(&error); + char *name; + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + remove_profile(c,name); + pa_dbus_send_empty_reply(conn, msg); +} + +void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ + uint32_t rev=1; + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); +} + +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *c = (pa_core *)_u; + pa_idxset *sink_list; + uint32_t dummy; + + pa_assert_se(sink_list = pa_shared_get(c, SINKLIST)); + unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); + char **names = NULL; + void *iter = NULL; + struct userdata *sink_u = NULL; + pa_assert_se(names = pa_xnew0(char *,n_sinks)); + for(uint32_t i = 0; i < n_sinks; ++i){ + sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + names[i] = sink_u->dbus_path; + } + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks); + pa_xfree(names); +} + +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *core=_u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, array_iter; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter)); + + char *name; + pa_datum key,next_key; + int done; + pa_database *database; + pa_assert_se(database=pa_shared_get(core, EQDB)); + done = !pa_database_first(database, &key, NULL); + + while(!done){ + done = !pa_database_next(database, &key, &next_key, NULL); + name=pa_xmalloc(key.size + 1); + memcpy(name, key.data, key.size); + name[key.size] = '\0'; + pa_datum_free(&key); + dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name); + pa_xfree(name); + key = next_key; + } + dbus_message_iter_close_container(&msg_iter, &array_iter); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *u=(pa_core *) _u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter; + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + uint32_t rev=1; + pa_idxset *sink_list; + pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); + unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); + char **names = NULL; + void *iter = NULL; + struct userdata *sink_u = NULL; + pa_assert_se(names = pa_xnew0(char *,n_sinks)); + for(uint32_t i = 0; i < n_sinks; ++i){ + unsigned dummy; + sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + names[i] = sink_u->dbus_path; + } + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks); + pa_xfree(names); + + pa_database *database; + pa_assert_se(database=pa_shared_get(u, EQDB)); + + pa_datum key,next_key; + char *profile_name; + int done; + done = !pa_database_first(database, &key, NULL); + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter); + dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name); + + dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter); + while(!done){ + done = !pa_database_next(database, &key, &next_key, NULL); + profile_name=pa_xmalloc(key.size + 1); + memcpy(profile_name, key.data, key.size); + profile_name[key.size] = '\0'; + pa_datum_free(&key); + dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name); + pa_xfree(profile_name); + key = next_key; + } + pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter)); + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + float *ys; + uint32_t *xs; + double *_ys; + unsigned x_npoints,y_npoints; + + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + int points_good=1; + for(size_t i = 0; i < x_npoints; ++i){ + if(xs[i] >= u->fft_size / 2 + 1){ + points_good=0; + break; + } + } + if(!is_monotonic(xs,x_npoints) || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0fft_size / 2); + dbus_error_free(&error); + return; + + } + else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); + dbus_error_free(&error); + return; + } + + ys = pa_xmalloc(x_npoints * sizeof(float)); + for(uint32_t i = 0; i < x_npoints; ++i){ + ys[i] = (float) _ys[i]; + } + + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + pa_xfree(ys); + + //Stupid for IO reasons? Add a save signal to dbus instead + //save_state(u); + + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + uint32_t *xs; + double *ys; + unsigned x_npoints; + + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + int points_good=1; + for(size_t i = 0; i < x_npoints; ++i){ + if(xs[i] >= u->fft_size / 2 + 1){ + points_good=0; + break; + } + } + + if(x_npoints > u->fft_size / 2 +1 || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1); + dbus_error_free(&error); + return; + } + + ys = pa_xmalloc(x_npoints * sizeof(double)); + float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + for(uint32_t i = 0; i < x_npoints; ++i){ + ys[i] = H[xs[i]] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_xfree(ys); +} + +void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + char *name; + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + save_profile(u,name); + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + char *name; + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + const char *err_msg=load_profile(u,name); + if(err_msg!=NULL){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg); + dbus_error_free(&error); + return; + } + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ + uint32_t rev=1; + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); +} + +void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1106,7 +1566,7 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1115,7 +1575,8 @@ void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rate=(uint32_t) u->sink->sample_spec.rate; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + +void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1125,7 +1586,34 @@ void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + uint32_t rev=1; + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + uint32_t fft_size=(uint32_t) u->fft_size; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1144,7 +1632,7 @@ void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(H_); } -void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1152,7 +1640,9 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u=(struct userdata *)_u; double *H_; unsigned _n_coefs; - pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs); + if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){ + return; + } if(_n_coefs!=u->fft_size / 2 + 1){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; @@ -1166,44 +1656,7 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_aupdate_write_end(u->a_H); //Stupid for IO reasons? Add a save signal to dbus instead - save_state(u); + //save_state(u); pa_dbus_send_empty_reply(conn, msg); } - -void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - struct userdata *u = (struct userdata *)_u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter; - - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; - uint32_t fft_size=(uint32_t) u->fft_size; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); - double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); - - unsigned H_i=pa_aupdate_read_begin(u->a_H); - float *H=u->Hs[H_i]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H_[i] = H[i]; - } - pa_aupdate_read_end(u->a_H); - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); - - pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); - - pa_xfree(H_); -} -- cgit From 857eea062129b4b9e04950a9187ebc47fc84899f Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 7 Aug 2009 15:08:57 -0700 Subject: module-equalizer-sink: add lennard's fix for piggy-back sinks in pop_cb fixed some tsched issues --- src/modules/module-equalizer-sink.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 285fddbf..eb9d6ef6 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -549,6 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; + /* Hmm, process any reweind request that might be queued up */ + pa_sink_process_rewind(u->sink, 0); + //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; @@ -559,11 +562,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } /* - Set the H filter - */ + * Set the H filter + */ unsigned H_i = pa_aupdate_read_begin(u->a_H); u->H = u->Hs[H_i]; - + do{ pa_memchunk *buffer; size_t input_remaining = u->target_samples-u->samples_gathered; @@ -641,14 +644,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (amount > 0) { //pa_sample_spec *ss = &u->sink->sample_spec; - pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered = 0; } } pa_sink_process_rewind(u->sink, amount); - pa_memblockq_rewind(u->rendered_q, nbytes); + //pa_memblockq_rewind(u->rendered_q, nbytes); } /* Called from I/O thread context */ @@ -677,7 +680,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); - pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); } /* Called from I/O thread context */ @@ -692,8 +696,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); - //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ @@ -924,8 +928,8 @@ int pa__init(pa_module*m) { memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); - u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); - u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); + u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); + u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); @@ -965,7 +969,9 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); - pa_sink_set_max_request(u->sink, u->R*fs); + pa_sink_set_max_request(u->sink, + ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) + ); //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ -- cgit From 4231ac444fc1492bab58fe12d439c4479ef1f648 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Aug 2009 00:53:40 -0700 Subject: module-equalizer-sink: reverted buffering logic back to how the ladspa sink did it --- src/modules/module-equalizer-sink.c | 178 +++++++++++++++--------------------- 1 file changed, 75 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index eb9d6ef6..63111810 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -113,7 +113,7 @@ struct userdata { float *Hs[2];//thread updatable copies pa_aupdate *a_H; pa_memchunk conv_buffer; - pa_memblockq *rendered_q; + pa_memblockq *input_q; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -138,7 +138,7 @@ void fix_filter(float *H, size_t fft_size); void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); static int is_monotonic(uint32_t *xs, size_t length); -static void process_samples(struct userdata *u); +static void process_samples(struct userdata *u, pa_memchunk *tchunk); static void input_buffer(struct userdata *u, pa_memchunk *in); static void save_profile(struct userdata *u,char *name); static void save_state(struct userdata *u); @@ -273,9 +273,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec+=pa_bytes_to_usec(u->latency * fs, ss); - //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + //usec += pa_bytes_to_usec(u->latency * fs, ss); + //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -310,8 +310,10 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q)); + /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -327,34 +329,28 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -static void process_samples(struct userdata *u){ - pa_memchunk tchunk; +static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); - while(u->samples_gathered >= u->R){ - float *dst; - //pa_log("iter gathered: %ld", u->samples_gathered); - //pa_memblockq_drop(u->rendered_q, tchunk.length); - tchunk.index=0; - tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length); - dst=((float*)pa_memblock_acquire(tchunk.memblock)); - for(size_t c=0;c < u->channels; c++) { - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - u->H, - u->W, - u->output_window, - u - ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); - } - pa_memblock_release(tchunk.memblock); - pa_memblockq_push(u->rendered_q, &tchunk); - pa_memblock_unref(tchunk.memblock); - u->samples_gathered-=u->R; + pa_assert(u->samples_gathered >= u->R); + float *dst; + tchunk->index=0; + tchunk->length=u->R*fs; + tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length); + dst=((float*)pa_memblock_acquire(tchunk->memblock)); + for(size_t c=0;c < u->channels; c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } + pa_memblock_release(tchunk->memblock); + u->samples_gathered-=u->R; } typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); @@ -539,85 +535,59 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; pa_sink_input_assert_ref(i); pa_assert(chunk); - pa_assert_se(u = i->userdata); - pa_assert_se(u->sink); + pa_assert(u = i->userdata); + pa_assert(u->sink); size_t fs = pa_frame_size(&(u->sink->sample_spec)); - //size_t samples_requested = nbytes/fs; - size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; chunk->memblock = NULL; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - /* Hmm, process any reweind request that might be queued up */ + /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; - if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){ - *chunk = tchunk; - pa_memblockq_drop(u->rendered_q, chunk->length); - return 0; - } - - /* - * Set the H filter - */ - unsigned H_i = pa_aupdate_read_begin(u->a_H); - u->H = u->Hs[H_i]; - do{ - pa_memchunk *buffer; size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); //collect samples - buffer = &u->conv_buffer; - buffer->length = input_remaining*fs; - buffer->index = 0; - pa_memblock_ref(buffer->memblock); - pa_sink_render_into(u->sink, buffer); - - //if(u->sink->thread_info.rewind_requested) - // sink_request_rewind(u->sink); - - //pa_memchunk p; - //buffer = &p; - //pa_sink_render(u->sink, u->R*fs, buffer); - //buffer->length = PA_MIN(input_remaining*fs, buffer->length); - - //debug block - //pa_memblockq_push(u->rendered_q, buffer); - //pa_memblock_unref(buffer->memblock); - //goto END; - + //buffer = &u->conv_buffer; + //buffer->length = input_remaining*fs; + //buffer->index = 0; + //pa_memblock_ref(buffer->memblock); + //pa_sink_render_into(u->sink, buffer); + while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ + pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_memblockq_push(u->input_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + } + tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); + pa_memblockq_drop(u->input_q,tchunk.length); //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); - //copy new input + /* copy new input */ gettime(start); - input_buffer(u, buffer); + input_buffer(u, &tchunk); gettime(end); //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + pa_memblock_unref(tchunk.memblock); + }while(u->samples_gathered < u->R); - pa_memblock_unref(buffer->memblock); - - pa_assert_se(u->fft_size >= u->window_size); - pa_assert_se(u->R < u->window_size); - //process every complete block on hand - - gettime(start); - process_samples(u); - gettime(end); - //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); - - buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; - }while(buffered_samples < u->R); + pa_assert(u->fft_size >= u->window_size); + pa_assert(u->R < u->window_size); + /* set the H filter */ + unsigned H_i = pa_aupdate_read_begin(u->a_H); + u->H = u->Hs[H_i]; + gettime(start); + /* process a block */ + process_samples(u,chunk); + gettime(end); + //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); + pa_aupdate_read_end(u->a_H); - //deque from rendered_q and output - pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0); - *chunk = tchunk; - pa_memblockq_drop(u->rendered_q, chunk->length); - pa_assert_se(chunk->memblock); + pa_assert(chunk->memblock); //pa_log("gave %ld", chunk->length/fs); //pa_log("end pop"); return 0; @@ -638,20 +608,25 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; - max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q); + //max_rewrite = nbytes; + max_rewrite = nbytes + pa_memblockq_get_length(u->input_q); + //PA_MIN(pa_memblockq_get_length(u->input_q), nbytes); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { //pa_sample_spec *ss = &u->sink->sample_spec; - //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //invalidate the output q + pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); + //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered = 0; } } pa_sink_process_rewind(u->sink, amount); - //pa_memblockq_rewind(u->rendered_q, nbytes); + pa_memblockq_rewind(u->input_q, nbytes); } /* Called from I/O thread context */ @@ -664,7 +639,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_memblockq_set_maxrewind(u->rendered_q, nbytes); + pa_memblockq_set_maxrewind(u->input_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -694,7 +669,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); @@ -729,14 +703,14 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid //of them completely, figure out why - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -906,7 +880,7 @@ int pa__init(pa_module*m) { u->target_samples = 1*u->R; u->samples_gathered = 0; u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); - u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL); + u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); u->latency = u->R; @@ -1062,11 +1036,9 @@ void pa__done(pa_module*m) { pa_sink_input_unref(u->sink_input); } - if(u->conv_buffer.memblock) - pa_memblock_unref(u->conv_buffer.memblock); - - if (u->rendered_q) - pa_memblockq_free(u->rendered_q); + pa_aupdate_free(u->a_H); + pa_memblock_unref(u->conv_buffer.memblock); + pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); -- cgit From 1e3c7d326f4feb2e8dca8626afa58e88bca40fcd Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Aug 2009 22:20:05 -0700 Subject: module-equalizer-sink: dbus: eliminated some redundant code in dbus handlers/getall switched filter back to being a property signals for changed profiles, added/removed sinks, filter updates and sink reconfigurations fixed timing routines --- src/modules/module-equalizer-sink.c | 330 +++++++++++++++++++++--------------- 1 file changed, 194 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 63111810..88c10745 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -52,6 +52,7 @@ USA. #include #include #include +#include #include #include #include @@ -161,10 +162,14 @@ void dsp_logic( */ #define EXTNAME "org.PulseAudio.Ext.Equalizing1" #define MANAGER_PATH "/org/pulseaudio/equalizing1" +#define MANAGER_IFACE EXTNAME ".Manager" +#define EQUALIZER_IFACE EXTNAME ".Equalizer" static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); @@ -179,6 +184,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(struct userdata *u, double **H_); +static void set_filter(struct userdata *u, double **H_); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) @@ -471,8 +478,6 @@ void dsp_logic( // d->v = d->v*h->v; //#endif // } -// -// // //inverse fft // fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); // @@ -511,8 +516,6 @@ void dsp_logic( // ); //} - - void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; @@ -546,9 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); - //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; - + gettime(start); do{ size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); @@ -565,15 +568,17 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_unref(tchunk.memblock); } tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); - pa_memblockq_drop(u->input_q,tchunk.length); - //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + pa_memblockq_drop(u->input_q, tchunk.length); + //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - gettime(start); + //gettime(start); input_buffer(u, &tchunk); - gettime(end); - //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + //gettime(end); + //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->R); + gettime(end); + pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); @@ -584,12 +589,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* process a block */ process_samples(u,chunk); gettime(end); - //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); + pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); - //pa_log("gave %ld", chunk->length/fs); - //pa_log("end pop"); + //pa_log_debug("gave %ld", chunk->length/fs); + //pa_log_debug("end pop"); return 0; } @@ -1090,25 +1095,39 @@ static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={ [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL} }; +pa_dbus_arg_info sink_args[]={ + {"sink", "o", NULL} +}; + +enum manager_signal_index{ + MANAGER_SIGNAL_SINK_ADDED, + MANAGER_SIGNAL_SINK_REMOVED, + MANAGER_SIGNAL_PROFILES_CHANGED, + MANAGER_SIGNAL_MAX +}; + +static pa_dbus_signal_info manager_signals[MANAGER_SIGNAL_MAX]={ + [MANAGER_SIGNAL_SINK_ADDED]={.name="SinkAdded", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)}, + [MANAGER_SIGNAL_SINK_REMOVED]={.name="SinkRemoved", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)}, + [MANAGER_SIGNAL_PROFILES_CHANGED]={.name="ProfilesChanged", .arguments=NULL, .n_arguments=0} +}; + static pa_dbus_interface_info manager_info={ - .name=EXTNAME ".Manager", + .name=MANAGER_IFACE, .method_handlers=manager_methods, .n_method_handlers=MANAGER_METHOD_MAX, .property_handlers=manager_handlers, .n_property_handlers=MANAGER_HANDLER_MAX, .get_all_properties_cb=manager_get_all, - .signals=NULL, - .n_signals=0 + .signals=manager_signals, + .n_signals=MANAGER_SIGNAL_MAX }; - enum equalizer_method_index { EQUALIZER_METHOD_FILTER_POINTS, EQUALIZER_METHOD_SEED_FILTER, EQUALIZER_METHOD_SAVE_PROFILE, EQUALIZER_METHOD_LOAD_PROFILE, - EQUALIZER_METHOD_SET_FILTER, - EQUALIZER_METHOD_GET_FILTER, EQUALIZER_METHOD_MAX }; @@ -1117,6 +1136,7 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_SAMPLERATE, EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, + EQUALIZER_HANDLER_FILTER, EQUALIZER_HANDLER_MAX }; @@ -1134,12 +1154,6 @@ pa_dbus_arg_info save_profile_args[]={ pa_dbus_arg_info load_profile_args[]={ {"name", "s","in"}, }; -pa_dbus_arg_info set_filter_args[]={ - {"coefficients", "ad","in"}, -}; -pa_dbus_arg_info get_filter_args[]={ - {"coefficients", "ad","out"}, -}; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ [EQUALIZER_METHOD_SEED_FILTER]{ @@ -1162,16 +1176,6 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_load_profile}, - [EQUALIZER_METHOD_SET_FILTER]{ - .method_name="SetFilterCoefficients", - .arguments=set_filter_args, - .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_set_filter}, - [EQUALIZER_METHOD_GET_FILTER]{ - .method_name="GetFilterCoefficients", - .arguments=get_filter_args, - .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_get_filter} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1179,22 +1183,29 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, + [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}, +}; + +enum equalizer_signal_index{ + EQUALIZER_SIGNAL_FILTER_CHANGED, + EQUALIZER_SIGNAL_SINK_RECONFIGURED, + EQUALIZER_SIGNAL_MAX }; -//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; -//static pa_dbus_signal_info signals[SIGNAL_MAX] = { -// [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} -//}; +static pa_dbus_signal_info equalizer_signals[EQUALIZER_SIGNAL_MAX]={ + [EQUALIZER_SIGNAL_FILTER_CHANGED]={.name="FilterChanged", .arguments=NULL, .n_arguments=0}, + [EQUALIZER_SIGNAL_SINK_RECONFIGURED]={.name="SinkReconfigured", .arguments=NULL, .n_arguments=0}, +}; static pa_dbus_interface_info equalizer_info={ - .name=EXTNAME ".Equalizer", + .name=EQUALIZER_IFACE, .method_handlers=equalizer_methods, .n_method_handlers=EQUALIZER_METHOD_MAX, .property_handlers=equalizer_handlers, .n_property_handlers=EQUALIZER_HANDLER_MAX, .get_all_properties_cb=equalizer_get_all, - .signals=NULL, - .n_signals=0 + .signals=equalizer_signals, + .n_signals=EQUALIZER_SIGNAL_MAX }; void dbus_init(struct userdata *u){ @@ -1202,8 +1213,8 @@ void dbus_init(struct userdata *u){ u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST); - u->database=pa_shared_get(u->core,EQDB); + pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST); + u->database=pa_shared_get(u->core, EQDB); if(sink_list==NULL){ sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->core, SINKLIST, sink_list); @@ -1216,13 +1227,25 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } uint32_t dummy; - pa_idxset_put(sink_list,u,&dummy); + pa_idxset_put(sink_list, u, &dummy); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name))); + dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_REMOVED].name))); + dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); + pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); pa_idxset_remove_by_data(sink_list,u,&dummy); if(pa_idxset_size(sink_list)==0){ @@ -1255,6 +1278,13 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void } remove_profile(c,name); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); + pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c); + pa_dbus_protocol_send_signal(dbus_protocol, signal); + pa_dbus_protocol_unref(dbus_protocol); + dbus_message_unref(signal); } void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ @@ -1262,62 +1292,85 @@ void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - pa_core *c = (pa_core *)_u; - pa_idxset *sink_list; - uint32_t dummy; +void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ + pa_assert(u); + pa_assert(names); + pa_assert(n_sinks); - pa_assert_se(sink_list = pa_shared_get(c, SINKLIST)); - unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); - char **names = NULL; void *iter = NULL; struct userdata *sink_u = NULL; - pa_assert_se(names = pa_xnew0(char *,n_sinks)); - for(uint32_t i = 0; i < n_sinks; ++i){ - sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); - names[i] = sink_u->dbus_path; + uint32_t dummy; + pa_idxset *sink_list; + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); + *n_sinks = (unsigned) pa_idxset_size(sink_list); + pa_assert_se(*names = pa_xnew0(char *,*n_sinks)); + for(uint32_t i = 0; i < *n_sinks; ++i){ + sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + (*names)[i] = pa_xstrdup(sink_u->dbus_path); } - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks); - pa_xfree(names); } -void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); - pa_core *core=_u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, array_iter; - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter)); + unsigned n; + char **names = NULL; + get_sinks((pa_core *) _u, &names, &n); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); + } + pa_xfree(names); +} +void get_profiles(pa_core *c, char ***names, unsigned *n){ + pa_assert(c); + pa_assert(names); + pa_assert(n); char *name; - pa_datum key,next_key; - int done; pa_database *database; - pa_assert_se(database=pa_shared_get(core, EQDB)); - done = !pa_database_first(database, &key, NULL); + pa_assert_se(database = pa_shared_get(c, EQDB)); + pa_datum key, next_key; + pa_strlist *head=NULL, *iter; + int done; + done = !pa_database_first(database, &key, NULL); + *n = 0; while(!done){ done = !pa_database_next(database, &key, &next_key, NULL); name=pa_xmalloc(key.size + 1); memcpy(name, key.data, key.size); name[key.size] = '\0'; pa_datum_free(&key); - dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name); + head = pa_strlist_prepend(head, name); pa_xfree(name); key = next_key; + (*n)++; } - dbus_message_iter_close_container(&msg_iter, &array_iter); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); + (*names) = pa_xnew0(char *, *n); + iter=head; + for(unsigned i = 0; i < *n; ++i){ + (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); + iter=pa_strlist_next(iter); + } + pa_strlist_free(head); +} + +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + char **names; + unsigned n; + get_profiles((pa_core *)_u, &names, &n); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); + } + pa_xfree(names); } void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ @@ -1325,54 +1378,31 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(msg); pa_assert(_u); - pa_core *u=(pa_core *) _u; + pa_core *c = (pa_core *)_u; + char **names = NULL; + unsigned n; DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter; + DBusMessageIter msg_iter, dict_iter; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); uint32_t rev=1; - pa_idxset *sink_list; pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); - pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); - unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); - char **names = NULL; - void *iter = NULL; - struct userdata *sink_u = NULL; - pa_assert_se(names = pa_xnew0(char *,n_sinks)); - for(uint32_t i = 0; i < n_sinks; ++i){ - unsigned dummy; - sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); - names[i] = sink_u->dbus_path; + get_sinks(c, &names, &n); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter,manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); } - pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks); pa_xfree(names); - pa_database *database; - pa_assert_se(database=pa_shared_get(u, EQDB)); - - pa_datum key,next_key; - char *profile_name; - int done; - done = !pa_database_first(database, &key, NULL); - dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter); - dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name); - - dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter); - while(!done){ - done = !pa_database_next(database, &key, &next_key, NULL); - profile_name=pa_xmalloc(key.size + 1); - memcpy(profile_name, key.data, key.size); - profile_name[key.size] = '\0'; - pa_datum_free(&key); - dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name); - pa_xfree(profile_name); - key = next_key; + get_profiles(c, &names, &n); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_PROFILES].property_name, DBUS_TYPE_STRING, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); } - pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter)); - pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter)); + pa_xfree(names); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); dbus_message_unref(reply); @@ -1412,11 +1442,14 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; - } - else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ + }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); dbus_error_free(&error); return; + }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2"); + dbus_error_free(&error); + return; } ys = pa_xmalloc(x_npoints * sizeof(float)); @@ -1435,6 +1468,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * //save_state(u); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { @@ -1500,6 +1538,11 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void } save_profile(u,name); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { @@ -1564,6 +1607,15 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u) pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } +void get_filter(struct userdata *u, double **H_){ + *H_ = pa_xnew0(double, u->fft_size / 2 + 1); + float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; + for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ + (*H_)[i] = H[i] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); +} + void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); @@ -1585,6 +1637,10 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + double *H; + get_filter(u, &H); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); + pa_xfree(H); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); @@ -1597,44 +1653,46 @@ void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(_u); struct userdata *u = (struct userdata *)_u; - - unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1); - double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double)); - - float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; - for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - H_[i] = H[i] * u->fft_size; - } - pa_aupdate_read_end(u->a_H); + unsigned n_coefs = u->fft_size / 2 + 1; + double *H_; + get_filter(u, &H_); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } +void set_filter(struct userdata *u, double **H_){ + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H[i] = (float) (*H_)[i]; + } + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); +} + void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); struct userdata *u=(struct userdata *)_u; - double *H_; + double *H; unsigned _n_coefs; - if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){ + if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } if(_n_coefs!=u->fft_size / 2 + 1){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; } - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float) H_[i]; - } - fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - + set_filter(u, &H); //Stupid for IO reasons? Add a save signal to dbus instead //save_state(u); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } -- cgit From 684ad6ddb7ab0e8ba11bf6df620b3cf9c1facf8a Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 10 Aug 2009 15:33:55 -0700 Subject: module-equalizer-sink: proper fix for pa_xmalloc(0) given that 0 is illegal fix coefficients in case there's no resume state loadprofile now signals filterchanged --- src/modules/module-equalizer-sink.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 88c10745..8049a99b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -991,9 +991,13 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these + float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->Hs[1][i]=u->Hs[0][i] = 1.0; + H[i] = 1.0 / sqrtf(2.0f); } + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1303,7 +1307,7 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ pa_idxset *sink_list; pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); *n_sinks = (unsigned) pa_idxset_size(sink_list); - pa_assert_se(*names = pa_xnew0(char *,*n_sinks)); + *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL; for(uint32_t i = 0; i < *n_sinks; ++i){ sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); (*names)[i] = pa_xstrdup(sink_u->dbus_path); @@ -1349,7 +1353,7 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){ key = next_key; (*n)++; } - (*names) = pa_xnew0(char *, *n); + (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL; iter=head; for(unsigned i = 0; i < *n; ++i){ (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); @@ -1569,6 +1573,11 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void return; } pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ -- cgit From 8c2f9763df96550e06e94d8e985b24a9f6675677 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 11 Aug 2009 03:00:28 -0700 Subject: module-equalizer-sink: fix for peek returning a null memblock pa_log -> pa_log_debug for fft size updated module description fixed a comment in dbus error for incorrect x positions --- src/modules/module-equalizer-sink.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 8049a99b..270e73e3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -2,8 +2,8 @@ This file is part of PulseAudio. This module is based off Lennart Poettering's LADSPA sink and swaps out -LADSPA functionality for a STFT OLA based digital equalizer. All new work -is published under Pulseaudio's original license. +LADSPA functionality for a dbus-aware STFT OLA based digital equalizer. +All new work is published under Pulseaudio's original license. Copyright 2009 Jason Newton Original Author: @@ -562,11 +562,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //buffer->index = 0; //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); - while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ + while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); } + pa_assert(tchunk.memblock); tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); @@ -878,7 +880,7 @@ int pa__init(pa_module*m) { u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); - pa_log("fft size: %ld", u->fft_size); + pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; u->R = (u->window_size+1)/2; u->overlap_size = u->window_size-u->R; @@ -1442,7 +1444,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * } } if(!is_monotonic(xs,x_npoints) || !points_good){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0fft_size / 2); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; -- cgit From 38d608ad5a0a3fa0ff7f93007dde46d8661348c3 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Aug 2009 05:22:29 -0700 Subject: module-equalizer-sink: reworked processing so we don't have input->output delay of R samples --- src/modules/module-equalizer-sink.c | 97 +++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 270e73e3..5f16cd65 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -103,7 +103,6 @@ struct userdata { size_t samples_gathered; size_t max_output;//max amount of samples outputable in a single //message - size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -115,6 +114,7 @@ struct userdata { pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; + int first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -135,11 +135,13 @@ static const char* const valid_modargs[] = { static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); static void hanning_window(float *W, size_t window_size); -void fix_filter(float *H, size_t fft_size); -void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); +static void fix_filter(float *H, size_t fft_size); +static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); static int is_monotonic(uint32_t *xs, size_t length); +static void reset_filter(struct userdata *u); static void process_samples(struct userdata *u, pa_memchunk *tchunk); +static void initialize_buffer(struct userdata *u, pa_memchunk *in); static void input_buffer(struct userdata *u, pa_memchunk *in); static void save_profile(struct userdata *u,char *name); static void save_state(struct userdata *u); @@ -274,13 +276,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; - //size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec += pa_bytes_to_usec(u->latency * fs, ss); + usec += pa_bytes_to_usec(u->latency * fs, ss); //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); /* Add the latency internal to our sink input on top */ @@ -340,10 +342,10 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); pa_assert(u->samples_gathered >= u->R); float *dst; - tchunk->index=0; - tchunk->length=u->R*fs; - tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length); - dst=((float*)pa_memblock_acquire(tchunk->memblock)); + tchunk->index = 0; + tchunk->length = u->R * fs; + tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length); + dst = ((float*)pa_memblock_acquire(tchunk->memblock)); for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, @@ -354,13 +356,21 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ u->output_window, u ); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0;i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i]; + } + } pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } pa_memblock_release(tchunk->memblock); - u->samples_gathered-=u->R; + u->samples_gathered-= u->R; } -typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); +typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); typedef union float_vector { float f[v_size]; v4sf v; @@ -516,20 +526,34 @@ void dsp_logic( // ); //} +void initialize_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t samples = in->length/fs; + pa_assert_se(u->samples_gathered + samples == u->window_size); + float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + for(size_t c = 0; c < u->channels; c++) { + //buffer with an offset after the overlap from previous + //iterations + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + } + u->samples_gathered+=samples; + pa_memblock_release(in->memblock); +} + void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(samples <= u->target_samples-u->samples_gathered); + pa_assert_se(samples <= u->window_size - u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples + u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size ); pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } - u->samples_gathered+=samples; + u->samples_gathered += samples; pa_memblock_release(in->memblock); } @@ -553,8 +577,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct timespec start, end; gettime(start); do{ - size_t input_remaining = u->target_samples-u->samples_gathered; - pa_assert(input_remaining>0); + size_t input_remaining = u->window_size - u->samples_gathered; + pa_assert(input_remaining > 0); //collect samples //buffer = &u->conv_buffer; @@ -563,7 +587,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ - pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -574,11 +598,15 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ //gettime(start); - input_buffer(u, &tchunk); + if(u->first_iteration){ + initialize_buffer(u, &tchunk); + }else{ + input_buffer(u, &tchunk); + } //gettime(end); //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered < u->R); + }while(u->samples_gathered <= u->window_size); gettime(end); pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); @@ -597,6 +625,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); + if(u->first_iteration){ + u->first_iteration = 0; + } return 0; } @@ -627,8 +658,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); - pa_log_debug("Resetting equalizer"); - u->samples_gathered = 0; + pa_log("Resetting filter"); + reset_filter(u); } } @@ -636,6 +667,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->input_q, nbytes); } +void reset_filter(struct userdata *u){ + u->samples_gathered = 0; + for(size_t i = 0;i < u->channels; ++i){ + memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); + } + u->first_iteration = 1; +} + /* Called from I/O thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -882,15 +921,13 @@ int pa__init(pa_module*m) { u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; - u->R = (u->window_size+1)/2; - u->overlap_size = u->window_size-u->R; - u->target_samples = 1*u->R; + u->R = (u->window_size + 1) / 2; + u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss); u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); - u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); - u->latency = u->R; + u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); } @@ -900,9 +937,9 @@ int pa__init(pa_module*m) { u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); for(size_t c = 0; c < u->channels; ++c){ - u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float)); + u->input[c] = alloc(u->window_size, sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float)); + memset(u->input[c], 0, (u->window_size)*sizeof(float)); pa_assert_se(u->input[c]); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); pa_assert_se(u->overlap_accum[c]); @@ -913,6 +950,7 @@ int pa__init(pa_module*m) { u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); + u->first_iteration = 1; /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -1048,7 +1086,6 @@ void pa__done(pa_module*m) { } pa_aupdate_free(u->a_H); - pa_memblock_unref(u->conv_buffer.memblock); pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); -- cgit From 0e6711ddd0659d62613e8812b33e36d581875ba0 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Aug 2009 06:17:40 -0700 Subject: module-equalizer-sink: merging in upstream changes whitespace fix and fix for first iteration un-windowing --- src/modules/module-equalizer-sink.c | 184 ++++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 5f16cd65..944aa7ab 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -81,9 +81,8 @@ PA_MODULE_USAGE(_("sink= ")); struct userdata { - pa_core *core; pa_module *module; - pa_sink *sink, *master; + pa_sink *sink; pa_sink_input *sink_input; char *name; @@ -101,7 +100,6 @@ struct userdata { //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; - size_t max_output;//max amount of samples outputable in a single //message float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) @@ -274,20 +272,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t usec = 0; - pa_sample_spec *ss=&u->sink->sample_spec; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - - /* Get the latency of the master sink */ - if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - usec = 0; - - usec += pa_bytes_to_usec(u->latency * fs, ss); - //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); - /* Add the latency internal to our sink input on top */ - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); - *((pa_usec_t*) data) = usec; + size_t fs=pa_frame_size(&u->sink->sample_spec); + + /* The sink is _put() before the sink input is, so let's + * make sure we don't access it in that time. Also, the + * sink input is first shut down, the sink second. */ + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { + *((pa_usec_t*) data) = 0; + return 0; + } + + *((pa_usec_t*) data) = + /* Get the latency of the master sink */ + pa_sink_get_latency_within_thread(u->sink_input->sink) + + + /* Add the latency internal to our sink input on top */ + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) + + pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); + //+ pa_bytes_to_usec(u->latency * fs, ss) + //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); return 0; } } @@ -303,12 +307,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_IS_LINKED(state) && - u->sink_input && - PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) - - pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + if (!PA_SINK_IS_LINKED(state) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return 0; + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; } @@ -319,7 +322,9 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q)); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; /* Just hand this one over to the master sink */ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE); @@ -332,6 +337,10 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->sink_input, @@ -344,7 +353,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ float *dst; tchunk->index = 0; tchunk->length = u->R * fs; - tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length); + tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); for(size_t c=0;c < u->channels; c++) { dsp_logic( @@ -361,7 +370,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ * undo the windowing (for non-zero window values) */ for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i]; + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; } } pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); @@ -529,7 +538,7 @@ void dsp_logic( void initialize_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(u->samples_gathered + samples == u->window_size); + pa_assert_se(u->samples_gathered + samples <= u->window_size); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous @@ -549,7 +558,7 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size + u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size ); pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } @@ -561,14 +570,12 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_assert(chunk); - pa_assert(u = i->userdata); pa_assert(u->sink); size_t fs = pa_frame_size(&(u->sink->sample_spec)); pa_memchunk tchunk; chunk->memblock = NULL; - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return -1; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); @@ -587,13 +594,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ - pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); + pa_sink_render(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); } pa_assert(tchunk.memblock); - tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); + tchunk.length = PA_MIN(input_remaining * fs, tchunk.length); pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ @@ -606,7 +613,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //gettime(end); //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered <= u->window_size); + }while(u->samples_gathered < u->window_size); gettime(end); pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); @@ -617,7 +624,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk u->H = u->Hs[H_i]; gettime(start); /* process a block */ - process_samples(u,chunk); + process_samples(u, chunk); gettime(end); pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); pa_aupdate_read_end(u->a_H); @@ -640,9 +647,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return; - if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; @@ -682,9 +686,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_memblockq_set_maxrewind(u->input_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -696,9 +697,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); @@ -712,26 +710,30 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ -static void sink_input_detach_cb(pa_sink_input *i) { +static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); - pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } @@ -742,11 +744,10 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); pa_sink_attach_within_thread(u->sink); //size_t fs = pa_frame_size(&(u->sink->sample_spec)); @@ -756,7 +757,8 @@ static void sink_input_attach_cb(pa_sink_input *i) { //of them completely, figure out why //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_attach_within_thread(u->sink); } /* Called from main context */ @@ -766,14 +768,18 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_unlink(u->sink); + /* The order here matters! We first kill the sink input, followed + * by the sink. That means the sink callbacks must be protected + * against an unconnected sink input! */ pa_sink_input_unlink(u->sink_input); + pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; pa_sink_input_unref(u->sink_input); u->sink_input = NULL; + pa_sink_unref(u->sink); + u->sink = NULL; + pa_module_unload_request(u->module, TRUE); } @@ -863,6 +869,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } +/* Called from main context */ +static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); +} //ensure's memory allocated is a multiple of v_size //and aligned @@ -910,12 +926,8 @@ int pa__init(pa_module*m) { fs = pa_frame_size(&ss); u = pa_xnew0(struct userdata, 1); - u->core = m->core; u->module = m; m->userdata = u; - u->master = master; - u->sink = NULL; - u->sink_input = NULL; u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); @@ -924,7 +936,6 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss); u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; @@ -958,11 +969,10 @@ int pa__init(pa_module*m) { sink_data.module = m; if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name); - sink_data.namereg_fail = FALSE; pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer"); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer on %s",z? z: master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); @@ -972,7 +982,7 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -987,7 +997,6 @@ int pa__init(pa_module*m) { u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_rtpoll(u->sink, master->rtpoll); pa_sink_set_max_request(u->sink, ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) ); @@ -997,13 +1006,13 @@ int pa__init(pa_module*m) { pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; - sink_input_data.sink = u->master; + sink_input_data.sink = master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -1014,11 +1023,13 @@ int pa__init(pa_module*m) { u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -1075,15 +1086,20 @@ void pa__done(pa_module*m) { dbus_done(u); - if (u->sink) { - pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - } + /* See comments in sink_input_kill_cb() above regarding + * destruction order! */ - if (u->sink_input) { + if (u->sink_input) pa_sink_input_unlink(u->sink_input); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->sink_input) pa_sink_input_unref(u->sink_input); - } + + if (u->sink) + pa_sink_unref(u->sink); pa_aupdate_free(u->a_H); pa_memblockq_free(u->input_q); @@ -1252,21 +1268,21 @@ static pa_dbus_interface_info equalizer_info={ }; void dbus_init(struct userdata *u){ - u->dbus_protocol=pa_dbus_protocol_get(u->core); + u->dbus_protocol=pa_dbus_protocol_get(u->sink->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST); - u->database=pa_shared_get(u->core, EQDB); + pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST); + u->database=pa_shared_get(u->sink->core, EQDB); if(sink_list==NULL){ sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); - pa_shared_set(u->core, SINKLIST, sink_list); + pa_shared_set(u->sink->core, SINKLIST, sink_list); char *dbname; pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); - pa_shared_set(u->core,EQDB,u->database); - pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core); + pa_shared_set(u->sink->core,EQDB,u->database); + pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } uint32_t dummy; @@ -1289,14 +1305,14 @@ void dbus_done(struct userdata *u){ pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); - pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); + pa_assert_se(sink_list=pa_shared_get(u->sink->core,SINKLIST)); pa_idxset_remove_by_data(sink_list,u,&dummy); if(pa_idxset_size(sink_list)==0){ pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name); - pa_shared_remove(u->core, EQDB); + pa_shared_remove(u->sink->core, EQDB); pa_database_close(u->database); - pa_shared_remove(u->core, SINKLIST); + pa_shared_remove(u->sink->core, SINKLIST); pa_xfree(sink_list); } pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name); -- cgit From 1c1a812b32897a6aa217bbe381e9e82f3b2728fc Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 11:38:12 -0700 Subject: module-equalizer-sink exchanged improper usage of memblockq_peek'd memchunk for silence block dropped unneeded function prototypes changed mround to be slightly more elegant __restrict__ -> restrict for c99 removed unneeded pa_aupdate_swap calls first_iteration -> pa_bool_t cleaned up some usage of pa_malloc0 where pa_new0 was more appropriate cruft removal, whitespace fixes and reordering of variables --- src/modules/module-equalizer-sink.c | 556 +++++++++++++++++------------------- 1 file changed, 257 insertions(+), 299 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 944aa7ab..b7f61c6a 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -38,7 +38,9 @@ USA. #include #include +#include +#include #include #include #include @@ -112,7 +114,7 @@ struct userdata { pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; - int first_iteration; + pa_bool_t first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -131,103 +133,43 @@ static const char* const valid_modargs[] = { NULL }; -static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -static void hanning_window(float *W, size_t window_size); -static void fix_filter(float *H, size_t fft_size); -static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); -static void array_out(const char *name, float *a, size_t length); -static int is_monotonic(uint32_t *xs, size_t length); -static void reset_filter(struct userdata *u); -static void process_samples(struct userdata *u, pa_memchunk *tchunk); -static void initialize_buffer(struct userdata *u, pa_memchunk *in); -static void input_buffer(struct userdata *u, pa_memchunk *in); -static void save_profile(struct userdata *u,char *name); -static void save_state(struct userdata *u); -static void remove_profile(pa_core *u,char *name); -static const char * load_profile(struct userdata *u,char *name); -static void load_state(struct userdata *u); - -void dsp_logic( - float * __restrict__ dst, - float * __restrict__ src, - float * __restrict__ overlap, - const float * __restrict__ H, - const float * __restrict__ W, - fftwf_complex * __restrict__ output_window, - struct userdata *u); - - -/* - * DBus Routines and Callbacks - */ -#define EXTNAME "org.PulseAudio.Ext.Equalizing1" -#define MANAGER_PATH "/org/pulseaudio/equalizing1" -#define MANAGER_IFACE EXTNAME ".Manager" -#define EQUALIZER_IFACE EXTNAME ".Equalizer" -static void dbus_init(struct userdata *u); -static void dbus_done(struct userdata *u); -static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); -static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); -static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); -static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(struct userdata *u, double **H_); -static void set_filter(struct userdata *u, double **H_); #define v_size 4 -#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) -#define tdiff(x, y) time_diff(&x, &y) -#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) +#define mround(x, y) ((x + y - 1) / y) * y #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +static void dbus_init(struct userdata *u); +static void dbus_done(struct userdata *u); -uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) -{ - return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) - - ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); -} - -void hanning_window(float *W, size_t window_size){ +static void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 for(size_t i=0; i < window_size;++i){ W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1))); } } -void fix_filter(float *H, size_t fft_size){ +static void fix_filter(float *H, size_t fft_size){ //divide out the fft gain for(size_t i = 0; i < fft_size / 2 + 1; ++i){ H[i] /= fft_size; } } -void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ +static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ //Note that xs must be monotonically increasing! + float x_range_lower, x_range_upper, c0; pa_assert_se(n_points>=2); pa_assert_se(xs[0] == 0); pa_assert_se(xs[n_points - 1] == length - 1); for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){ pa_assert(x_range_lower_i < n_points-1); - float x_range_lower = (float) (xs[x_range_lower_i]); - float x_range_upper = (float) (xs[x_range_lower_i+1]); + x_range_lower = (float) (xs[x_range_lower_i]); + x_range_upper = (float) (xs[x_range_lower_i+1]); pa_assert_se(x_range_lower < x_range_upper); pa_assert_se(x >= x_range_lower); pa_assert_se(x <= x_range_upper); //bilinear-interpolation of coefficients specified - float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); + c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); pa_assert_se(c0 >= 0&&c0 <= 1.0); signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]); while(x >= xs[x_range_lower_i + 1]){ @@ -237,22 +179,7 @@ void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n signal[length-1]=ys[n_points-1]; } -void array_out(const char *name, float *a, size_t length){ - FILE *p=fopen(name, "w"); - if(!p){ - pa_log("opening %s failed!", name); - return; - } - for(size_t i = 0; i < length; ++i){ - fprintf(p, "%e,", a[i]); - //if(i%1000==0){ - // fprintf(p, "\n"); - //} - } - fprintf(p, "\n"); - fclose(p); -} -static int is_monotonic(uint32_t *xs,size_t length){ +static int is_monotonic(const uint32_t *xs,size_t length){ if(length<2){ return 1; } @@ -347,38 +274,6 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -static void process_samples(struct userdata *u, pa_memchunk *tchunk){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_assert(u->samples_gathered >= u->R); - float *dst; - tchunk->index = 0; - tchunk->length = u->R * fs; - tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); - dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - for(size_t c=0;c < u->channels; c++) { - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - u->H, - u->W, - u->output_window, - u - ); - if(u->first_iteration){ - /* The windowing function will make the audio ramped in, as a cheap fix we can - * undo the windowing (for non-zero window values) - */ - for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; - } - } - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); - } - pa_memblock_release(tchunk->memblock); - u->samples_gathered-= u->R; -} - typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); typedef union float_vector { float f[v_size]; @@ -389,15 +284,15 @@ typedef union float_vector { } float_vector_t; //reference implementation -void dsp_logic( - float * __restrict__ dst,//used as a temp array too, needs to be fft_length! - float * __restrict__ src,/*input data w/ overlap at start, +static void dsp_logic( + float * restrict dst,//used as a temp array too, needs to be fft_length! + float * restrict src,/*input data w/ overlap at start, *automatically cycled in routine */ - float * __restrict__ overlap,//The size of the overlap - const float * __restrict__ H,//The freq. magnitude scalers filter - const float * __restrict__ W,//The windowing function - fftwf_complex * __restrict__ output_window,//The transformed window'd src + float * restrict overlap,//The size of the overlap + const float * restrict H,//The freq. magnitude scalers filter + const float * restrict W,//The windowing function + fftwf_complex * restrict output_window,//The transformed window'd src struct userdata *u){ //use a linear-phase sliding STFT and overlap-add method (for each channel) //zero padd the data @@ -443,14 +338,14 @@ void dsp_logic( ////regardless of sse enabled, the loops in here assume ////16 byte aligned addresses and memory allocations divisible by v_size //void dsp_logic( -// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! -// float * __restrict__ src,/*input data w/ overlap at start, +// float * restrict dst,//used as a temp array too, needs to be fft_length! +// float * restrict src,/*input data w/ overlap at start, // *automatically cycled in routine // */ -// float * __restrict__ overlap,//The size of the overlap -// const float * __restrict__ H,//The freq. magnitude scalers filter -// const float * __restrict__ W,//The windowing function -// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// float * restrict overlap,//The size of the overlap +// const float * restrict H,//The freq. magnitude scalers filter +// const float * restrict W,//The windowing function +// fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants // // const size_t window_size = mround(u->window_size,v_size); @@ -535,25 +430,57 @@ void dsp_logic( // ); //} -void initialize_buffer(struct userdata *u, pa_memchunk *in){ - size_t fs = pa_frame_size(&(u->sink->sample_spec)); - size_t samples = in->length/fs; - pa_assert_se(u->samples_gathered + samples <= u->window_size); +static void process_samples(struct userdata *u, pa_memchunk *tchunk){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + float *dst; + pa_assert(u->samples_gathered >= u->R); + tchunk->index = 0; + tchunk->length = u->R * fs; + tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); + dst = ((float*)pa_memblock_acquire(tchunk->memblock)); + for(size_t c=0;c < u->channels; c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0;i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + } + } + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); + } + pa_memblock_release(tchunk->memblock); + u->samples_gathered -= u->R; +} + +static void initialize_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t samples = in->length / fs; float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + pa_assert_se(u->samples_gathered + samples <= u->window_size); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); } - u->samples_gathered+=samples; + u->samples_gathered += samples; pa_memblock_release(in->memblock); } -void input_buffer(struct userdata *u, pa_memchunk *in){ +static void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(samples <= u->window_size - u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + pa_assert_se(samples <= u->window_size - u->samples_gathered); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations @@ -569,20 +496,21 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; + size_t fs; + struct timeval start, end; + pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); pa_assert(chunk); pa_assert(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); - pa_memchunk tchunk; + fs = pa_frame_size(&(u->sink->sample_spec)); chunk->memblock = NULL; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - struct timespec start, end; - gettime(start); + pa_timeval_load(&start); do{ size_t input_remaining = u->window_size - u->samples_gathered; pa_assert(input_remaining > 0); @@ -593,7 +521,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //buffer->index = 0; //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); - while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ + while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); @@ -604,40 +532,47 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - //gettime(start); + //pa_timeval_load(start); if(u->first_iteration){ initialize_buffer(u, &tchunk); }else{ input_buffer(u, &tchunk); } - //gettime(end); - //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + //pa_timeval_load(&end); + //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); - gettime(end); - pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); + pa_timeval_load(&end); + pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - unsigned H_i = pa_aupdate_read_begin(u->a_H); - u->H = u->Hs[H_i]; - gettime(start); + u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + pa_timeval_load(&start); /* process a block */ process_samples(u, chunk); - gettime(end); - pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); + pa_timeval_load(&end); + pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); if(u->first_iteration){ - u->first_iteration = 0; + u->first_iteration = FALSE; } return 0; } +static void reset_filter(struct userdata *u){ + u->samples_gathered = 0; + for(size_t i = 0;i < u->channels; ++i){ + memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); + } + u->first_iteration = TRUE; +} + /* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -671,14 +606,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->input_q, nbytes); } -void reset_filter(struct userdata *u){ - u->samples_gathered = 0; - for(size_t i = 0;i < u->channels; ++i){ - memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); - } - u->first_iteration = 1; -} - /* Called from I/O thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -693,11 +620,11 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { /* Called from I/O thread context */ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; - + size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); @@ -740,7 +667,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { /* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct userdata *u; - + size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -748,9 +675,10 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); + fs = pa_frame_size(&(u->sink->sample_spec)); pa_sink_attach_within_thread(u->sink); + pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs)); - //size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid @@ -758,7 +686,6 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); - pa_sink_attach_within_thread(u->sink); } /* Called from main context */ @@ -799,15 +726,15 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -void save_profile(struct userdata *u, char *name){ +static void save_profile(struct userdata *u, char *name){ float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + pa_datum key, data; for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ //H_n[i] = H[i] * u->fft_size; H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); - pa_datum key, data; key.data=name; key.size = strlen(key.data); data.data = H_n; @@ -816,23 +743,23 @@ void save_profile(struct userdata *u, char *name){ pa_database_sync(u->database); } -void save_state(struct userdata *u){ +static void save_state(struct userdata *u){ char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); save_profile(u, state_name); pa_xfree(state_name); } -void remove_profile(pa_core *c,char *name){ +static void remove_profile(pa_core *c, char *name){ pa_datum key; + pa_database *database; key.data = name; key.size = strlen(key.data); - pa_database *database; - pa_assert_se(database = pa_shared_get(c,EQDB)); - pa_database_unset(database,&key); + pa_assert_se(database = pa_shared_get(c, EQDB)); + pa_database_unset(database, &key); pa_database_sync(database); } -const char* load_profile(struct userdata *u,char *name){ +static const char* load_profile(struct userdata *u, char *name){ pa_datum key,value; key.data = name; key.size = strlen(key.data); @@ -840,7 +767,6 @@ const char* load_profile(struct userdata *u,char *name){ if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; memcpy(H, value.data, value.size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); }else{ return "incompatible size"; @@ -852,13 +778,13 @@ const char* load_profile(struct userdata *u,char *name){ return NULL; //fix_filter(u->H, u->fft_size); } -void load_state(struct userdata *u){ + +static void load_state(struct userdata *u){ char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); load_profile(u,state_name); pa_xfree(state_name); } - /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -884,10 +810,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { //and aligned static void * alloc(size_t x,size_t s){ size_t f = mround(x*s, sizeof(float)*v_size); + float *t; pa_assert_se(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); - float *t = fftwf_malloc(f); + t = fftwf_malloc(f); memset(t, 0, f); return t; } @@ -903,6 +830,7 @@ int pa__init(pa_module*m) { pa_sink_new_data sink_data; pa_bool_t *use_default = NULL; size_t fs; + float *H; pa_assert(m); @@ -936,7 +864,6 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ @@ -945,8 +872,8 @@ int pa__init(pa_module*m) { u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); memset(u->work_buffer, 0, u->fft_size*sizeof(float)); - u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); - u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + u->input = pa_xnew0(float *, u->channels); + u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->input[c] = alloc(u->window_size, sizeof(float)); pa_assert_se(u->input[c]); @@ -961,7 +888,7 @@ int pa__init(pa_module*m) { u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); - u->first_iteration = 1; + u->first_iteration = TRUE; /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -995,11 +922,9 @@ int pa__init(pa_module*m) { u->sink->update_requested_latency = sink_update_requested_latency; u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; + u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence); pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_max_request(u->sink, - ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) - ); //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ @@ -1042,12 +967,11 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; + H=u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ H[i] = 1.0 / sqrtf(2.0f); } fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1124,6 +1048,33 @@ void pa__done(pa_module*m) { pa_xfree(u); } +/* + * DBus Routines and Callbacks + */ +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" +#define MANAGER_PATH "/org/pulseaudio/equalizing1" +#define MANAGER_IFACE EXTNAME ".Manager" +#define EQUALIZER_IFACE EXTNAME ".Equalizer" +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(struct userdata *u, double **H_); +static void set_filter(struct userdata *u, double **H_); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1267,17 +1218,20 @@ static pa_dbus_interface_info equalizer_info={ .n_signals=EQUALIZER_SIGNAL_MAX }; -void dbus_init(struct userdata *u){ +static void dbus_init(struct userdata *u){ + uint32_t dummy; + DBusMessage *signal = NULL; + pa_idxset *sink_list = NULL; u->dbus_protocol=pa_dbus_protocol_get(u->sink->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST); + sink_list = pa_shared_get(u->sink->core, SINKLIST); u->database=pa_shared_get(u->sink->core, EQDB); if(sink_list==NULL){ + char *dbname; sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->sink->core, SINKLIST, sink_list); - char *dbname; pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); @@ -1285,17 +1239,15 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } - uint32_t dummy; pa_idxset_put(sink_list, u, &dummy); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name))); dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void dbus_done(struct userdata *u){ +static void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; @@ -1320,14 +1272,16 @@ void dbus_done(struct userdata *u){ pa_dbus_protocol_unref(u->dbus_protocol); } -void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { DBusError error; pa_core *c = (pa_core *)_u; + DBusMessage *signal = NULL; + pa_dbus_protocol *dbus_protocol; + char *name; pa_assert(conn); pa_assert(msg); pa_assert(c); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ @@ -1338,28 +1292,27 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void remove_profile(c,name); pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); - pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c); + dbus_protocol = pa_dbus_protocol_get(c); pa_dbus_protocol_send_signal(dbus_protocol, signal); pa_dbus_protocol_unref(dbus_protocol); dbus_message_unref(signal); } -void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ - pa_assert(u); - pa_assert(names); - pa_assert(n_sinks); - +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ void *iter = NULL; struct userdata *sink_u = NULL; uint32_t dummy; pa_idxset *sink_list; + pa_assert(u); + pa_assert(names); + pa_assert(n_sinks); + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); *n_sinks = (unsigned) pa_idxset_size(sink_list); *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL; @@ -1369,13 +1322,13 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ } } -void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ + unsigned n; + char **names = NULL; pa_assert(conn); pa_assert(msg); pa_assert(_u); - unsigned n; - char **names = NULL; get_sinks((pa_core *) _u, &names, &n); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n); for(unsigned i = 0; i < n; ++i){ @@ -1384,17 +1337,17 @@ void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(names); } -void get_profiles(pa_core *c, char ***names, unsigned *n){ - pa_assert(c); - pa_assert(names); - pa_assert(n); +static void get_profiles(pa_core *c, char ***names, unsigned *n){ char *name; pa_database *database; - pa_assert_se(database = pa_shared_get(c, EQDB)); pa_datum key, next_key; pa_strlist *head=NULL, *iter; + pa_bool_t done; + pa_assert_se(database = pa_shared_get(c, EQDB)); - int done; + pa_assert(c); + pa_assert(names); + pa_assert(n); done = !pa_database_first(database, &key, NULL); *n = 0; while(!done){ @@ -1411,19 +1364,19 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){ (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL; iter=head; for(unsigned i = 0; i < *n; ++i){ - (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); - iter=pa_strlist_next(iter); + (*names)[*n - 1 - i] = pa_xstrdup(pa_strlist_data(iter)); + iter = pa_strlist_next(iter); } pa_strlist_free(head); } -void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + char **names; + unsigned n; pa_assert(conn); pa_assert(msg); pa_assert(_u); - char **names; - unsigned n; get_profiles((pa_core *)_u, &names, &n); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n); for(unsigned i = 0; i < n; ++i){ @@ -1432,21 +1385,22 @@ void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(names); } -void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - pa_core *c = (pa_core *)_u; +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_core *c; char **names = NULL; unsigned n; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; + uint32_t rev; + pa_assert(conn); + pa_assert(msg); + pa_assert_se(c = _u); + pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - uint32_t rev=1; + rev = 1; pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); get_sinks(c, &names, &n); @@ -1467,17 +1421,19 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; DBusError error; - - pa_assert(conn); - pa_assert(msg); - pa_assert(u); + DBusMessage *signal = NULL; float *ys; uint32_t *xs; double *_ys; - unsigned x_npoints,y_npoints; + unsigned x_npoints, y_npoints; + float *H; + pa_bool_t points_good = TRUE; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); dbus_error_init(&error); @@ -1489,14 +1445,13 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; } - int points_good=1; for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= u->fft_size / 2 + 1){ - points_good=0; + points_good = FALSE; break; } } - if(!is_monotonic(xs,x_npoints) || !points_good){ + if(!is_monotonic(xs, x_npoints) || !points_good){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; @@ -1505,7 +1460,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); dbus_error_free(&error); return; - }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){ + }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2"); dbus_error_free(&error); return; @@ -1516,10 +1471,9 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * ys[i] = (float) _ys[i]; } - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + H = u->Hs[pa_aupdate_write_begin(u->a_H)]; interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); pa_xfree(ys); @@ -1528,22 +1482,23 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; DBusError error; + uint32_t *xs; + double *ys; + unsigned x_npoints; + float *H; + pa_bool_t points_good=TRUE; pa_assert(conn); pa_assert(msg); pa_assert(u); - uint32_t *xs; - double *ys; - unsigned x_npoints; dbus_error_init(&error); @@ -1554,10 +1509,9 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, dbus_error_free(&error); return; } - int points_good=1; for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= u->fft_size / 2 + 1){ - points_good=0; + points_good=FALSE; break; } } @@ -1569,7 +1523,7 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, } ys = pa_xmalloc(x_npoints * sizeof(double)); - float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } @@ -1579,14 +1533,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, pa_xfree(ys); } -void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; + char *name; + DBusMessage *signal = NULL; DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, @@ -1598,21 +1553,22 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void save_profile(u,name); pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; + char *name; DBusError error; + const char *err_msg = NULL; + DBusMessage *signal = NULL; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, @@ -1621,77 +1577,80 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_free(&error); return; } - const char *err_msg=load_profile(u,name); - if(err_msg!=NULL){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg); + err_msg = load_profile(u,name); + if(err_msg != NULL){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); return; } pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t n_coefs; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - - struct userdata *u=(struct userdata *)_u; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + n_coefs = (uint32_t) (u->fft_size / 2 + 1); pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t rate; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *) _u; - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + rate = (uint32_t) u->sink->sample_spec.rate; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t fft_size; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *) _u; - uint32_t fft_size=(uint32_t) u->fft_size; + fft_size = (uint32_t) u->fft_size; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -void get_filter(struct userdata *u, double **H_){ +static void get_filter(struct userdata *u, double **H_){ + float *H; *H_ = pa_xnew0(double, u->fft_size / 2 + 1); - float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; + H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ (*H_)[i] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); } -void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - struct userdata *u=(struct userdata *) _u; +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - uint32_t rev=1; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; - uint32_t fft_size=(uint32_t) u->fft_size; + uint32_t rev, n_coefs, rate, fft_size; + double *H; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(msg); + + rev = 1; + n_coefs = (uint32_t) (u->fft_size / 2 + 1); + rate = (uint32_t) u->sink->sample_spec.rate; + fft_size = (uint32_t) u->fft_size; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1701,7 +1660,6 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - double *H; get_filter(u, &H); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); pa_xfree(H); @@ -1711,37 +1669,38 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + unsigned n_coefs; + double *H_; + pa_assert_se(u = (struct userdata *) _u); + + n_coefs = u->fft_size / 2 + 1; pa_assert(conn); pa_assert(msg); - pa_assert(_u); - - struct userdata *u = (struct userdata *)_u; - unsigned n_coefs = u->fft_size / 2 + 1; - double *H_; get_filter(u, &H_); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } -void set_filter(struct userdata *u, double **H_){ +static void set_filter(struct userdata *u, double **H_){ float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ H[i] = (float) (*H_)[i]; } fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); } -void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + double *H; + unsigned _n_coefs; + DBusMessage *signal = NULL; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *)_u; - double *H; - unsigned _n_coefs; if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } @@ -1755,7 +1714,6 @@ void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); -- cgit From 07cd6a4c3d357e9406bbb0444995c8c21f3a3cab Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 14:29:21 -0700 Subject: module-equalizer-sink.c i->sink -> i in pa_get_sink_max_request* --- src/modules/module-equalizer-sink.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index b7f61c6a..d4707748 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -677,7 +677,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); pa_sink_attach_within_thread(u->sink); - pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); @@ -811,7 +811,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { static void * alloc(size_t x,size_t s){ size_t f = mround(x*s, sizeof(float)*v_size); float *t; - pa_assert_se(f >= x*s); + pa_assert(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); t = fftwf_malloc(f); @@ -876,11 +876,8 @@ int pa__init(pa_module*m) { u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->input[c] = alloc(u->window_size, sizeof(float)); - pa_assert_se(u->input[c]); memset(u->input[c], 0, (u->window_size)*sizeof(float)); - pa_assert_se(u->input[c]); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); - pa_assert_se(u->overlap_accum[c]); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); -- cgit From 7bd7ce6deca125d9061ea8d29f6a17e65ba36116 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 15:41:40 -0700 Subject: module-equalizer-sink.c: swapped order of attach_within_thread and set_max_request within sink_input_attach_cb --- src/modules/module-equalizer-sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d4707748..9a4740f3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -543,7 +543,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); pa_timeval_load(&end); - pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); + pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); @@ -553,7 +553,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* process a block */ process_samples(u, chunk); pa_timeval_load(&end); - pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); + pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); @@ -676,7 +676,6 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_attach_within_thread(u->sink); pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); @@ -686,6 +685,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_attach_within_thread(u->sink); } /* Called from main context */ -- cgit From ab0e20ab2c0014e495cadb9c4f8e188df125fa7f Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 16:14:30 -0700 Subject: module-equalizer-sink: fixed timeval initialization --- src/modules/module-equalizer-sink.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 9a4740f3..a8ccff8b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -40,6 +40,7 @@ USA. #include #include +#include #include #include #include @@ -510,7 +511,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_sink_process_rewind(u->sink, 0); //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - pa_timeval_load(&start); + pa_rtclock_get(&start); do{ size_t input_remaining = u->window_size - u->samples_gathered; pa_assert(input_remaining > 0); @@ -532,27 +533,27 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - //pa_timeval_load(start); + //pa_rtclock_get(start); if(u->first_iteration){ initialize_buffer(u, &tchunk); }else{ input_buffer(u, &tchunk); } - //pa_timeval_load(&end); + //pa_rtclock_get(&end); //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); - pa_timeval_load(&end); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; - pa_timeval_load(&start); + pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); - pa_timeval_load(&end); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); -- cgit From cd54ecdc8c6b7947bd06a7db69055cfba078f1bf Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 17 Aug 2009 10:52:43 -0700 Subject: module-equalizer-sink: drop old macros for new library based ones --- src/modules/module-equalizer-sink.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index a8ccff8b..5152b646 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -136,7 +136,6 @@ static const char* const valid_modargs[] = { #define v_size 4 -#define mround(x, y) ((x + y - 1) / y) * y #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" static void dbus_init(struct userdata *u); @@ -349,10 +348,10 @@ static void dsp_logic( // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants // -// const size_t window_size = mround(u->window_size,v_size); -// const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2); -// //const size_t R = mround(u->R, v_size); -// const size_t overlap_size = mround(u->overlap_size, v_size); +// const size_t window_size = PA_ROUND_UP(u->window_size,v_size); +// const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2); +// //const size_t R = PA_ROUND_UP(u->R, v_size); +// const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size); // // //assert(u->samples_gathered >= u->R); // //zero out the bit beyond the real overlap so we don't add garbage @@ -677,7 +676,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); @@ -810,7 +809,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { //ensure's memory allocated is a multiple of v_size //and aligned static void * alloc(size_t x,size_t s){ - size_t f = mround(x*s, sizeof(float)*v_size); + size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); float *t; pa_assert(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); -- cgit From 735c8ab6fbd9b673c05a2506f3f3c1d64d4d3970 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 18 Aug 2009 22:11:37 -0700 Subject: module-equalizer-sink: added support for preamp --- src/modules/module-equalizer-sink.c | 279 ++++++++++++++++++++---------------- 1 file changed, 159 insertions(+), 120 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 5152b646..0d82e018 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -104,6 +104,7 @@ struct userdata { size_t overlap_size;//window_size-R size_t samples_gathered; //message + float X; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -111,6 +112,7 @@ struct userdata { fftwf_plan forward_plan, inverse_plan; //size_t samplings; + float Xs[2]; float *Hs[2];//thread updatable copies pa_aupdate *a_H; pa_memchunk conv_buffer; @@ -138,6 +140,8 @@ static const char* const valid_modargs[] = { #define v_size 4 #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +#define FILTER_SIZE (u->fft_size / 2 + 1) +#define PROFILE_SIZE (FILTER_SIZE + 1) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -199,7 +203,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { case PA_SINK_MESSAGE_GET_LATENCY: { - size_t fs=pa_frame_size(&u->sink->sample_spec); + //size_t fs=pa_frame_size(&u->sink->sample_spec); /* The sink is _put() before the sink input is, so let's * make sure we don't access it in that time. Also, the @@ -215,8 +219,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_sink_get_latency_within_thread(u->sink_input->sink) + /* Add the latency internal to our sink input on top */ - pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) + - pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); + // pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); //+ pa_bytes_to_usec(u->latency * fs, ss) //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); return 0; @@ -274,15 +278,6 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); -typedef union float_vector { - float f[v_size]; - v4sf v; -#ifdef __SSE2__ - __m128 m; -#endif -} float_vector_t; - //reference implementation static void dsp_logic( float * restrict dst,//used as a temp array too, needs to be fft_length! @@ -299,13 +294,13 @@ static void dsp_logic( memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data for(size_t j = 0;j < u->window_size; ++j){ - dst[j] = W[j] * src[j]; + dst[j] = u->X * W[j] * src[j]; } //Processing is done here! //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering - for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){ + for(size_t j = 0; j < FILTER_SIZE; ++j){ u->output_window[j][0] *= u->H[j]; u->output_window[j][1] *= u->H[j]; } @@ -335,6 +330,15 @@ static void dsp_logic( ); } +typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); +typedef union float_vector { + float f[v_size]; + v4sf v; +#ifdef __SSE2__ + __m128 m; +#endif +} float_vector_t; + ////regardless of sse enabled, the loops in here assume ////16 byte aligned addresses and memory allocations divisible by v_size //void dsp_logic( @@ -347,11 +351,12 @@ static void dsp_logic( // const float * restrict W,//The windowing function // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants -// + //float_vector_t x = {u->X, u->X, u->X, u->X}; // const size_t window_size = PA_ROUND_UP(u->window_size,v_size); -// const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2); +// const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2); // //const size_t R = PA_ROUND_UP(u->R, v_size); // const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size); +// overlap_size = PA_ROUND_UP(u->overlap_size, v_size); // // //assert(u->samples_gathered >= u->R); // //zero out the bit beyond the real overlap so we don't add garbage @@ -368,9 +373,9 @@ static void dsp_logic( // float_vector_t *w = (float_vector_t*) (W+j); // float_vector_t *s = (float_vector_t*) (src+j); //#if __SSE2__ -// d->m = _mm_mul_ps(w->m, s->m); +// d->m = _mm_mul_ps(x->m, _mm_mul_ps(w->m, s->m)); //#else -// d->v = w->v * s->v; +// d->v = x->v * w->v * s->v; //#endif // } // //Processing is done here! @@ -498,6 +503,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; size_t fs; struct timeval start, end; + unsigned a_i; pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -548,7 +554,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + a_i = pa_aupdate_read_begin(u->a_H); + u->X = u->Xs[a_i]; + u->H = u->Hs[a_i]; pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); @@ -727,18 +735,25 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } static void save_profile(struct userdata *u, char *name){ - float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); - const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + unsigned a_i; + const size_t profile_size = PROFILE_SIZE * sizeof(float); + float *H_n, *profile; + const float *H; pa_datum key, data; - for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ + profile = pa_xnew0(float, profile_size); + a_i = pa_aupdate_read_begin(u->a_H); + H_n = profile + 1; + H = u->Hs[a_i]; + profile[0] = u->Xs[a_i]; + for(size_t i = 0 ; i <= FILTER_SIZE; ++i){ //H_n[i] = H[i] * u->fft_size; H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); key.data=name; key.size = strlen(key.data); - data.data = H_n; - data.size = (u->fft_size / 2 + 1) * sizeof(float); + data.data = profile; + data.size = profile_size; pa_database_set(u->database, &key, &data, TRUE); pa_database_sync(u->database); } @@ -760,13 +775,17 @@ static void remove_profile(pa_core *c, char *name){ } static const char* load_profile(struct userdata *u, char *name){ - pa_datum key,value; + unsigned a_i; + pa_datum key, value; + const size_t profile_size = PROFILE_SIZE * sizeof(float); key.data = name; key.size = strlen(key.data); if(pa_database_get(u->database, &key, &value) != NULL){ - if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ - float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; - memcpy(H, value.data, value.size); + if(value.size == profile_size){ + float *H = (float *) value.data; + a_i = pa_aupdate_write_begin(u->a_H); + u->Xs[a_i] = H[0]; + memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float)); pa_aupdate_write_end(u->a_H); }else{ return "incompatible size"; @@ -831,6 +850,7 @@ int pa__init(pa_module*m) { pa_bool_t *use_default = NULL; size_t fs; float *H; + unsigned a_i; pa_assert(m); @@ -867,7 +887,7 @@ int pa__init(pa_module*m) { u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ - u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); + u->Hs[i] = alloc((FILTER_SIZE), sizeof(float)); } u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); @@ -880,7 +900,7 @@ int pa__init(pa_module*m) { u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } - u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); + u->output_window = alloc((FILTER_SIZE), sizeof(fftwf_complex)); u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); @@ -964,8 +984,10 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - H=u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + a_i = pa_aupdate_write_begin(u->a_H); + H = u->Hs[a_i]; + u->Xs[a_i] = 1.0f; + for(size_t i = 0; i < FILTER_SIZE; ++i){ H[i] = 1.0 / sqrtf(2.0f); } fix_filter(H, u->fft_size); @@ -1053,9 +1075,7 @@ void pa__done(pa_module*m) { #define MANAGER_IFACE EXTNAME ".Manager" #define EQUALIZER_IFACE EXTNAME ".Equalizer" static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); @@ -1064,14 +1084,12 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(struct userdata *u, double **H_); -static void set_filter(struct userdata *u, double **H_); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1144,16 +1162,19 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, EQUALIZER_HANDLER_FILTER, + EQUALIZER_HANDLER_PREAMP, EQUALIZER_HANDLER_MAX }; pa_dbus_arg_info filter_points_args[]={ {"xs", "au","in"}, {"ys", "ad","out"}, + {"preamp", "d","out"}, }; pa_dbus_arg_info seed_filter_args[]={ {"xs", "au","in"}, {"ys", "ad","in"}, + {"preamp", "d","in"}, }; pa_dbus_arg_info save_profile_args[]={ {"name", "s","in"}, @@ -1190,7 +1211,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, - [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}, + [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter} }; enum equalizer_signal_index{ @@ -1215,7 +1236,7 @@ static pa_dbus_interface_info equalizer_info={ .n_signals=EQUALIZER_SIGNAL_MAX }; -static void dbus_init(struct userdata *u){ +void dbus_init(struct userdata *u){ uint32_t dummy; DBusMessage *signal = NULL; pa_idxset *sink_list = NULL; @@ -1244,7 +1265,7 @@ static void dbus_init(struct userdata *u){ dbus_message_unref(signal); } -static void dbus_done(struct userdata *u){ +void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; @@ -1269,7 +1290,7 @@ static void dbus_done(struct userdata *u){ pa_dbus_protocol_unref(u->dbus_protocol); } -static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { DBusError error; pa_core *c = (pa_core *)_u; DBusMessage *signal = NULL; @@ -1296,7 +1317,7 @@ static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } @@ -1319,7 +1340,7 @@ static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ } } -static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ unsigned n; char **names = NULL; pa_assert(conn); @@ -1367,7 +1388,7 @@ static void get_profiles(pa_core *c, char ***names, unsigned *n){ pa_strlist_free(head); } -static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ char **names; unsigned n; pa_assert(conn); @@ -1382,7 +1403,7 @@ static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_ pa_xfree(names); } -static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_core *c; char **names = NULL; unsigned n; @@ -1418,14 +1439,14 @@ static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; DBusError error; DBusMessage *signal = NULL; float *ys; uint32_t *xs; - double *_ys; - unsigned x_npoints, y_npoints; + double *_ys, preamp; + unsigned x_npoints, y_npoints, a_i; float *H; pa_bool_t points_good = TRUE; pa_assert(conn); @@ -1437,13 +1458,14 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, if(!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, + DBUS_TYPE_DOUBLE, &preamp, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } for(size_t i = 0; i < x_npoints; ++i){ - if(xs[i] >= u->fft_size / 2 + 1){ + if(xs[i] >= FILTER_SIZE){ points_good = FALSE; break; } @@ -1453,8 +1475,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, dbus_error_free(&error); return; - }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); + }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE ){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE); dbus_error_free(&error); return; }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){ @@ -1467,9 +1489,10 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = (float) _ys[i]; } - - H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); + a_i = pa_aupdate_write_begin(u->a_H); + H = u->Hs[a_i]; + u->Xs[a_i] = preamp; + interpolate(H, FILTER_SIZE, xs, ys, x_npoints); fix_filter(H, u->fft_size); pa_aupdate_write_end(u->a_H); pa_xfree(ys); @@ -1484,14 +1507,16 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, dbus_message_unref(signal); } -static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; DBusError error; uint32_t *xs; - double *ys; - unsigned x_npoints; + double *ys, preamp; + unsigned x_npoints, a_i; float *H; pa_bool_t points_good=TRUE; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; pa_assert(conn); pa_assert(msg); @@ -1507,30 +1532,39 @@ static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage return; } for(size_t i = 0; i < x_npoints; ++i){ - if(xs[i] >= u->fft_size / 2 + 1){ + if(xs[i] >= FILTER_SIZE){ points_good=FALSE; break; } } - if(x_npoints > u->fft_size / 2 +1 || !points_good){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1); + if(x_npoints > FILTER_SIZE || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", FILTER_SIZE); dbus_error_free(&error); return; } ys = pa_xmalloc(x_npoints * sizeof(double)); - H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + a_i = pa_aupdate_read_begin(u->a_H); + H = u->Hs[a_i]; + preamp = u->Xs[a_i]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } pa_aupdate_read_end(u->a_H); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + + pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); pa_xfree(ys); } -static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; char *name; DBusMessage *signal = NULL; @@ -1555,7 +1589,7 @@ static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; char *name; DBusError error; @@ -1574,7 +1608,7 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg dbus_error_free(&error); return; } - err_msg = load_profile(u,name); + err_msg = load_profile(u, name); if(err_msg != NULL){ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); @@ -1587,23 +1621,23 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t n_coefs; pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - n_coefs = (uint32_t) (u->fft_size / 2 + 1); + n_coefs = (uint32_t) PROFILE_SIZE; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t rate; pa_assert_se(u = (struct userdata *) _u); @@ -1614,7 +1648,7 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t fft_size; pa_assert_se(u = (struct userdata *) _u); @@ -1625,71 +1659,47 @@ static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -static void get_filter(struct userdata *u, double **H_){ +static double * get_filter(struct userdata *u){ float *H; - *H_ = pa_xnew0(double, u->fft_size / 2 + 1); - H = u->Hs[pa_aupdate_read_begin(u->a_H)]; - for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - (*H_)[i] = H[i] * u->fft_size; + double *H_; + unsigned a_i; + H_ = pa_xnew0(double, PROFILE_SIZE); + a_i = pa_aupdate_read_begin(u->a_H); + H = u->Hs[a_i]; + H_[0] = u->Xs[a_i]; + for(size_t i = 0;i < FILTER_SIZE; ++i){ + H_[i + 1] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); + return H_; } -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter; - uint32_t rev, n_coefs, rate, fft_size; - double *H; - pa_assert_se(u = (struct userdata *) _u); - pa_assert(msg); - - rev = 1; - n_coefs = (uint32_t) (u->fft_size / 2 + 1); - rate = (uint32_t) u->sink->sample_spec.rate; - fft_size = (uint32_t) u->fft_size; - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - get_filter(u, &H); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); - pa_xfree(H); - - pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); -} - -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; unsigned n_coefs; double *H_; pa_assert_se(u = (struct userdata *) _u); - n_coefs = u->fft_size / 2 + 1; + n_coefs = PROFILE_SIZE; pa_assert(conn); pa_assert(msg); - get_filter(u, &H_); + H_ = get_filter(u); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } -static void set_filter(struct userdata *u, double **H_){ - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float) (*H_)[i]; +static void set_filter(struct userdata *u, double *H_){ + unsigned a_i= pa_aupdate_write_begin(u->a_H); + float *H = u->Hs[a_i]; + u->Xs[a_i] = H_[0]; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = (float) H_[i + 1]; } - fix_filter(H, u->fft_size); + fix_filter(H + 1, u->fft_size); pa_aupdate_write_end(u->a_H); } -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; double *H; unsigned _n_coefs; @@ -1701,13 +1711,11 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_ if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } - if(_n_coefs!=u->fft_size / 2 + 1){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); + if(_n_coefs != PROFILE_SIZE){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs); return; } - set_filter(u, &H); - //Stupid for IO reasons? Add a save signal to dbus instead - //save_state(u); + set_filter(u, H); pa_dbus_send_empty_reply(conn, msg); @@ -1715,3 +1723,34 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_ pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } + +void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + uint32_t rev, n_coefs, rate, fft_size; + double *H; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(msg); + + rev = 1; + n_coefs = (uint32_t) PROFILE_SIZE; + rate = (uint32_t) u->sink->sample_spec.rate; + fft_size = (uint32_t) u->fft_size; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + H = get_filter(u); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); + pa_xfree(H); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} -- cgit From 2f6fd32cc5726b47d332ad71c0f04f16614118ba Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 19 Aug 2009 00:30:07 -0700 Subject: module-equalizer-sink: fixed a bug w/ new zero-latency input scheme (that was an interesting/cool bug!) --- src/modules/module-equalizer-sink.c | 50 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 0d82e018..67a3d74e 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -104,8 +104,6 @@ struct userdata { size_t overlap_size;//window_size-R size_t samples_gathered; //message - float X; - float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; fftwf_complex *output_window; @@ -113,7 +111,7 @@ struct userdata { //size_t samplings; float Xs[2]; - float *Hs[2];//thread updatable copies + float *Hs[2];//thread updatable copies of the freq response filters (magintude based) pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; @@ -284,7 +282,8 @@ static void dsp_logic( float * restrict src,/*input data w/ overlap at start, *automatically cycled in routine */ - float * restrict overlap,//The size of the overlap + float * restrict overlap, + const float X,//multipliar const float * restrict H,//The freq. magnitude scalers filter const float * restrict W,//The windowing function fftwf_complex * restrict output_window,//The transformed window'd src @@ -293,16 +292,16 @@ static void dsp_logic( //zero padd the data memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data - for(size_t j = 0;j < u->window_size; ++j){ - dst[j] = u->X * W[j] * src[j]; + for(size_t j = 0; j < u->window_size; ++j){ + dst[j] = X * W[j] * src[j]; } //Processing is done here! //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering for(size_t j = 0; j < FILTER_SIZE; ++j){ - u->output_window[j][0] *= u->H[j]; - u->output_window[j][1] *= u->H[j]; + u->output_window[j][0] *= H[j]; + u->output_window[j][1] *= H[j]; } //inverse fft fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); @@ -314,9 +313,9 @@ static void dsp_logic( //} //overlap add and preserve overlap component from this window (linear phase) - for(size_t j = 0;j < u->overlap_size; ++j){ + for(size_t j = 0; j < u->overlap_size; ++j){ u->work_buffer[j] += overlap[j]; - overlap[j] = dst[u->R+j]; + overlap[j] = dst[u->R + j]; } ////debug: tests if basic buffering works ////shouldn't modify the signal AT ALL (beyond roundoff) @@ -325,8 +324,8 @@ static void dsp_logic( //} //preseve the needed input for the next window's overlap - memmove(src, src+u->R, - ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float) + memmove(src, src + u->R, + u->overlap_size * sizeof(float) ); } @@ -347,11 +346,12 @@ typedef union float_vector { // *automatically cycled in routine // */ // float * restrict overlap,//The size of the overlap +// const float X,//multipliar // const float * restrict H,//The freq. magnitude scalers filter // const float * restrict W,//The windowing function // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants - //float_vector_t x = {u->X, u->X, u->X, u->X}; + //float_vector_t x = {X, X, X, X}; // const size_t window_size = PA_ROUND_UP(u->window_size,v_size); // const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2); // //const size_t R = PA_ROUND_UP(u->R, v_size); @@ -430,25 +430,33 @@ typedef union float_vector { // //} // // //preseve the needed input for the next window's overlap -// memmove(src, src+u->R, -// ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float) +// memmove(src, src + u->R, +// u->overlap_size * sizeof(float) // ); //} static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); float *dst; + unsigned a_i; + float *H, X; pa_assert(u->samples_gathered >= u->R); tchunk->index = 0; tchunk->length = u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); + /* set the H filter */ + a_i = pa_aupdate_read_begin(u->a_H); + X = u->Xs[a_i]; + H = u->Hs[a_i]; + for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, u->input[c], u->overlap_accum[c], - u->H, + X, + H, u->W, u->output_window, u @@ -465,6 +473,8 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ } pa_memblock_release(tchunk->memblock); u->samples_gathered -= u->R; + + pa_aupdate_read_end(u->a_H); } static void initialize_buffer(struct userdata *u, pa_memchunk *in){ @@ -492,7 +502,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){ pa_assert_se( u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered += samples; pa_memblock_release(in->memblock); @@ -503,7 +513,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; size_t fs; struct timeval start, end; - unsigned a_i; pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -554,15 +563,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - a_i = pa_aupdate_read_begin(u->a_H); - u->X = u->Xs[a_i]; - u->H = u->Hs[a_i]; pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); - pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); @@ -1162,7 +1167,6 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, EQUALIZER_HANDLER_FILTER, - EQUALIZER_HANDLER_PREAMP, EQUALIZER_HANDLER_MAX }; -- cgit From b028e4e917441f138197a5c6a7b86b50b26bc35b Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 19 Aug 2009 23:22:33 -0700 Subject: module-equalizer-sink: per-channel filtering support + profiles, easier default configuration --- src/modules/module-equalizer-sink.c | 521 +++++++++++++++++++++++++----------- 1 file changed, 371 insertions(+), 150 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 67a3d74e..98b6b89b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -110,15 +110,16 @@ struct userdata { fftwf_plan forward_plan, inverse_plan; //size_t samplings; - float Xs[2]; - float *Hs[2];//thread updatable copies of the freq response filters (magintude based) - pa_aupdate *a_H; + float **Xs; + float ***Hs;//thread updatable copies of the freq response filters (magintude based) + pa_aupdate **a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; pa_bool_t first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; + pa_bool_t set_default; pa_database *database; }; @@ -129,6 +130,7 @@ static const char* const valid_modargs[] = { "master", "format", "rate", + "set_default", "channels", "channel_map", NULL @@ -138,8 +140,10 @@ static const char* const valid_modargs[] = { #define v_size 4 #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +#define EQ_STATE_DB "equalizer-state" #define FILTER_SIZE (u->fft_size / 2 + 1) -#define PROFILE_SIZE (FILTER_SIZE + 1) +#define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1) +#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -445,12 +449,11 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ tchunk->length = u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - /* set the H filter */ - a_i = pa_aupdate_read_begin(u->a_H); - X = u->Xs[a_i]; - H = u->Hs[a_i]; for(size_t c=0;c < u->channels; c++) { + a_i = pa_aupdate_read_begin(u->a_H[c]); + X = u->Xs[c][a_i]; + H = u->Hs[c][a_i]; dsp_logic( u->work_buffer, u->input[c], @@ -461,6 +464,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ u->output_window, u ); + pa_aupdate_read_end(u->a_H[c]); if(u->first_iteration){ /* The windowing function will make the audio ramped in, as a cheap fix we can * undo the windowing (for non-zero window values) @@ -473,8 +477,6 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ } pa_memblock_release(tchunk->memblock); u->samples_gathered -= u->R; - - pa_aupdate_read_end(u->a_H); } static void initialize_buffer(struct userdata *u, pa_memchunk *in){ @@ -538,6 +540,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); + //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -699,6 +702,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); pa_sink_attach_within_thread(u->sink); + if(u->set_default){ + pa_namereg_set_default_sink(u->module->core, u->sink); + } } /* Called from main context */ @@ -739,22 +745,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -static void save_profile(struct userdata *u, char *name){ +static void save_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; - const size_t profile_size = PROFILE_SIZE * sizeof(float); + const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); float *H_n, *profile; const float *H; pa_datum key, data; profile = pa_xnew0(float, profile_size); - a_i = pa_aupdate_read_begin(u->a_H); + a_i = pa_aupdate_read_begin(u->a_H[channel]); + profile[0] = u->Xs[a_i][channel]; + H = u->Hs[channel][a_i]; H_n = profile + 1; - H = u->Hs[a_i]; - profile[0] = u->Xs[a_i]; for(size_t i = 0 ; i <= FILTER_SIZE; ++i){ - //H_n[i] = H[i] * u->fft_size; - H_n[i] = H[i]; + H_n[i] = H[i] * u->fft_size; + //H_n[i] = H[i]; } - pa_aupdate_read_end(u->a_H); + pa_aupdate_read_end(u->a_H[channel]); key.data=name; key.size = strlen(key.data); data.data = profile; @@ -764,9 +770,38 @@ static void save_profile(struct userdata *u, char *name){ } static void save_state(struct userdata *u){ - char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); - save_profile(u, state_name); - pa_xfree(state_name); + unsigned a_i; + const size_t state_size = STATE_SIZE * sizeof(float); + float *H_n, *state; + float *H; + pa_datum key, data; + pa_database *database; + char *dbname; + char *state_name = u->name; + state = pa_xnew0(float, STATE_SIZE); + + for(size_t c = 0; c < u->channels; ++c){ + a_i = pa_aupdate_read_begin(u->a_H[c]); + state[c * CHANNEL_PROFILE_SIZE] = u->Xs[a_i][c]; + H = u->Hs[c][a_i]; + H_n = state + c * CHANNEL_PROFILE_SIZE + 1; + memcpy(H_n, H, FILTER_SIZE * sizeof(float)); + pa_aupdate_read_end(u->a_H[c]); + } + + key.data = state_name; + key.size = strlen(key.data); + data.data = state; + data.size = state_size; + //thread safety for 0.9.17? + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); + pa_assert_se(database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + + pa_database_set(database, &key, &data, TRUE); + pa_database_sync(database); + pa_database_close(database); + pa_xfree(state); } static void remove_profile(pa_core *c, char *name){ @@ -779,19 +814,20 @@ static void remove_profile(pa_core *c, char *name){ pa_database_sync(database); } -static const char* load_profile(struct userdata *u, char *name){ +static const char* load_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; pa_datum key, value; - const size_t profile_size = PROFILE_SIZE * sizeof(float); + const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); key.data = name; key.size = strlen(key.data); if(pa_database_get(u->database, &key, &value) != NULL){ if(value.size == profile_size){ - float *H = (float *) value.data; - a_i = pa_aupdate_write_begin(u->a_H); - u->Xs[a_i] = H[0]; - memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float)); - pa_aupdate_write_end(u->a_H); + float *profile = (float *) value.data; + a_i = pa_aupdate_write_begin(u->a_H[channel]); + u->Xs[channel][a_i] = profile[0]; + memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); + fix_filter(u->Hs[channel][a_i], u->fft_size); + pa_aupdate_write_end(u->a_H[channel]); }else{ return "incompatible size"; } @@ -800,13 +836,39 @@ static const char* load_profile(struct userdata *u, char *name){ return "profile doesn't exist"; } return NULL; - //fix_filter(u->H, u->fft_size); } static void load_state(struct userdata *u){ - char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); - load_profile(u,state_name); - pa_xfree(state_name); + unsigned a_i; + float *H; + pa_datum key, value; + pa_database *database; + char *dbname; + char *state_name = u->name; + + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); + database = pa_database_open(dbname, FALSE); + pa_xfree(dbname); + if(!database){ + return; + } + + key.data = state_name; + key.size = strlen(key.data); + + if(pa_database_get(database, &key, &value) != NULL){ + size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels); + float *state = (float *) value.data; + for(size_t c = 0; c < states; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = state + c * CHANNEL_PROFILE_SIZE + 1; + u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; + memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + pa_datum_free(&value); + } + pa_database_close(database); } /* Called from main context */ @@ -865,8 +927,12 @@ int pa__init(pa_module*m) { } if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { - pa_log("Master sink not found"); - goto fail; + pa_log("Master sink not found, trying default"); + master = pa_namereg_get_default_sink(m->core); + if(!master){ + pa_log("no default sink found!"); + goto fail; + } } ss = master->sample_spec; @@ -882,6 +948,9 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; + u->set_default = TRUE; + u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); + u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log_debug("fft size: %ld", u->fft_size); @@ -889,10 +958,16 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->a_H = pa_aupdate_new(); + u->a_H = pa_xnew0(pa_aupdate *, u->channels); u->latency = u->window_size - u->R; - for(size_t i = 0; i < 2; ++i){ - u->Hs[i] = alloc((FILTER_SIZE), sizeof(float)); + u->Xs = pa_xnew0(float *, u->channels); + u->Hs = pa_xnew0(float **, u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->Xs[c] = pa_xnew0(float, 2); + u->Hs[c] = pa_xnew0(float *, 2); + for(size_t i = 0; i < 2; ++i){ + u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float)); + } } u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); @@ -900,6 +975,7 @@ int pa__init(pa_module*m) { u->input = pa_xnew0(float *, u->channels); u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ + u->a_H[c] = pa_aupdate_new(); u->input[c] = alloc(u->window_size, sizeof(float)); memset(u->input[c], 0, (u->window_size)*sizeof(float)); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); @@ -989,14 +1065,16 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - a_i = pa_aupdate_write_begin(u->a_H); - H = u->Hs[a_i]; - u->Xs[a_i] = 1.0f; - for(size_t i = 0; i < FILTER_SIZE; ++i){ - H[i] = 1.0 / sqrtf(2.0f); + for(size_t c = 0; c< u->channels; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = u->Hs[c][a_i]; + u->Xs[c][a_i] = 1.0f; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = 1.0 / sqrtf(2.0f); + } + fix_filter(H, u->fft_size); + pa_aupdate_write_end(u->a_H[c]); } - fix_filter(H, u->fft_size); - pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1049,23 +1127,30 @@ void pa__done(pa_module*m) { if (u->sink) pa_sink_unref(u->sink); - pa_aupdate_free(u->a_H); pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); pa_xfree(u->output_window); for(size_t c=0; c < u->channels; ++c){ + pa_aupdate_free(u->a_H[c]); pa_xfree(u->overlap_accum[c]); pa_xfree(u->input[c]); } + pa_xfree(u->a_H); pa_xfree(u->overlap_accum); pa_xfree(u->input); pa_xfree(u->work_buffer); pa_xfree(u->W); - for(size_t i = 0; i < 2; ++i){ - pa_xfree(u->Hs[i]); + for(size_t c = 0; c < u->channels; ++c){ + pa_xfree(u->Xs[c]); + for(size_t i = 0; i < 2; ++i){ + pa_xfree(u->Hs[c][i]); + } + pa_xfree(u->Hs[c]); } + pa_xfree(u->Xs); + pa_xfree(u->Hs); pa_xfree(u->name); @@ -1088,11 +1173,12 @@ static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { @@ -1158,6 +1244,8 @@ enum equalizer_method_index { EQUALIZER_METHOD_SEED_FILTER, EQUALIZER_METHOD_SAVE_PROFILE, EQUALIZER_METHOD_LOAD_PROFILE, + EQUALIZER_METHOD_SET_FILTER, + EQUALIZER_METHOD_GET_FILTER, EQUALIZER_METHOD_MAX }; @@ -1166,25 +1254,41 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_SAMPLERATE, EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, - EQUALIZER_HANDLER_FILTER, + EQUALIZER_HANDLER_N_CHANNELS, EQUALIZER_HANDLER_MAX }; pa_dbus_arg_info filter_points_args[]={ + {"channel", "u","in"}, {"xs", "au","in"}, {"ys", "ad","out"}, - {"preamp", "d","out"}, + {"preamp", "d","out"} }; pa_dbus_arg_info seed_filter_args[]={ + {"channel", "u","in"}, {"xs", "au","in"}, {"ys", "ad","in"}, - {"preamp", "d","in"}, + {"preamp", "d","in"} +}; + +pa_dbus_arg_info set_filter_args[]={ + {"channel", "u","in"}, + {"ys", "ad","in"}, + {"preamp", "d","in"} +}; +pa_dbus_arg_info get_filter_args[]={ + {"channel", "u","in"}, + {"ys", "ad","out"}, + {"preamp", "d","out"} }; + pa_dbus_arg_info save_profile_args[]={ - {"name", "s","in"}, + {"channel", "u","in"}, + {"name", "s","in"} }; pa_dbus_arg_info load_profile_args[]={ - {"name", "s","in"}, + {"channel", "u","in"}, + {"name", "s","in"} }; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ @@ -1198,6 +1302,16 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=filter_points_args, .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_get_filter_points}, + [EQUALIZER_METHOD_SET_FILTER]{ + .method_name="SetFilter", + .arguments=set_filter_args, + .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_set_filter}, + [EQUALIZER_METHOD_GET_FILTER]{ + .method_name="GetFilter", + .arguments=get_filter_args, + .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_filter}, [EQUALIZER_METHOD_SAVE_PROFILE]{ .method_name="SaveProfile", .arguments=save_profile_args, @@ -1207,7 +1321,7 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .method_name="LoadProfile", .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_handle_load_profile}, + .receive_cb=equalizer_handle_load_profile} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1215,7 +1329,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, - [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter} + [EQUALIZER_HANDLER_N_CHANNELS]{.property_name="NChannels",.type="u",.get_cb=equalizer_get_n_channels,.set_cb=NULL}, }; enum equalizer_signal_index{ @@ -1249,15 +1363,15 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); sink_list = pa_shared_get(u->sink->core, SINKLIST); - u->database=pa_shared_get(u->sink->core, EQDB); - if(sink_list==NULL){ + u->database = pa_shared_get(u->sink->core, EQDB); + if(sink_list == NULL){ char *dbname; sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->sink->core, SINKLIST, sink_list); - pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); + pa_assert_se(dbname = pa_state_path("equalizer-presets", FALSE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); - pa_shared_set(u->sink->core,EQDB,u->database); + pa_shared_set(u->sink->core, EQDB, u->database); pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } @@ -1448,7 +1562,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * DBusError error; DBusMessage *signal = NULL; float *ys; - uint32_t *xs; + uint32_t *xs, channel, r_channel; double *_ys, preamp; unsigned x_npoints, y_npoints, a_i; float *H; @@ -1460,6 +1574,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, DBUS_TYPE_DOUBLE, &preamp, @@ -1468,6 +1583,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= FILTER_SIZE){ points_good = FALSE; @@ -1478,7 +1598,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; - }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE ){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE); dbus_error_free(&error); @@ -1493,14 +1612,25 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = (float) _ys[i]; } - a_i = pa_aupdate_write_begin(u->a_H); - H = u->Hs[a_i]; - u->Xs[a_i] = preamp; + r_channel = channel == u->channels ? 0 : channel; + a_i = pa_aupdate_write_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + u->Xs[r_channel][a_i] = preamp; interpolate(H, FILTER_SIZE, xs, ys, x_npoints); fix_filter(H, u->fft_size); - pa_aupdate_write_end(u->a_H); + if(channel == u->channels){ + for(size_t c = 1; c < u->channels; ++c){ + unsigned b_i = pa_aupdate_write_begin(u->a_H[c]); + float *H_p = u->Hs[c][b_i]; + u->Xs[c][b_i] = preamp; + memcpy(H_p, H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + } + pa_aupdate_write_end(u->a_H[r_channel]); pa_xfree(ys); + //Stupid for IO reasons? Add a save signal to dbus instead //save_state(u); @@ -1513,28 +1643,34 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; - DBusError error; - uint32_t *xs; + uint32_t *xs, channel, r_channel; double *ys, preamp; unsigned x_npoints, a_i; float *H; pa_bool_t points_good=TRUE; DBusMessage *reply = NULL; DBusMessageIter msg_iter; + DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= FILTER_SIZE){ points_good=FALSE; @@ -1548,14 +1684,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, return; } + r_channel = channel == u->channels ? 0 : channel; ys = pa_xmalloc(x_npoints * sizeof(double)); - a_i = pa_aupdate_read_begin(u->a_H); - H = u->Hs[a_i]; - preamp = u->Xs[a_i]; + a_i = pa_aupdate_read_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + preamp = u->Xs[r_channel][a_i]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } - pa_aupdate_read_end(u->a_H); + pa_aupdate_read_end(u->a_H[r_channel]); pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1568,9 +1705,128 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, pa_xfree(ys); } +static void get_filter(struct userdata *u, size_t channel, double **H_, double *preamp){ + float *H; + unsigned a_i; + size_t r_channel = channel == u->channels ? 0 : channel; + *H_ = pa_xnew0(double, FILTER_SIZE); + a_i = pa_aupdate_read_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + for(size_t i = 0;i < FILTER_SIZE; ++i){ + (*H_)[i] = H[i] * u->fft_size; + } + *preamp = u->Xs[r_channel][a_i]; + + pa_aupdate_read_end(u->a_H[r_channel]); +} + +void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + unsigned n_coefs; + uint32_t channel; + double *H_, preamp; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusError error; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + dbus_error_init(&error); + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + + n_coefs = CHANNEL_PROFILE_SIZE; + pa_assert(conn); + pa_assert(msg); + get_filter(u, channel, &H_, &preamp); + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + + pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, H_, n_coefs); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + pa_xfree(H_); +} + +static void set_filter(struct userdata *u, size_t channel, double *H_, double preamp){ + unsigned a_i; + size_t r_channel = channel == u->channels ? 0 : channel; + float *H; + //all channels + a_i = pa_aupdate_write_begin(u->a_H[r_channel]); + u->Xs[r_channel][a_i] = (float) preamp; + H = u->Hs[r_channel][a_i]; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = (float) H_[i]; + } + fix_filter(H, u->fft_size); + if(channel == u->channels){ + for(size_t c = 1; c < u->channels; ++c){ + unsigned b_i = pa_aupdate_write_begin(u->a_H[c]); + u->Xs[c][b_i] = u->Xs[r_channel][a_i]; + memcpy(u->Hs[c][b_i], u->Hs[r_channel][a_i], FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + } + pa_aupdate_write_end(u->a_H[r_channel]); +} + +void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + double *H, preamp; + uint32_t channel; + unsigned _n_coefs; + DBusMessage *signal = NULL; + DBusError error; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + dbus_error_init(&error); + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &H, &_n_coefs, + DBUS_TYPE_DOUBLE, &preamp, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + if(_n_coefs != FILTER_SIZE){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", FILTER_SIZE, _n_coefs); + return; + } + set_filter(u, channel, H, preamp); + + pa_dbus_send_empty_reply(conn, msg); + + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); +} + void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; char *name; + uint32_t channel, r_channel; DBusMessage *signal = NULL; DBusError error; pa_assert(conn); @@ -1579,13 +1835,20 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } - save_profile(u,name); + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + save_profile(u, r_channel, name); pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); @@ -1594,9 +1857,10 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void } void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; + struct userdata *u = (struct userdata *) _u; char *name; DBusError error; + uint32_t channel, r_channel; const char *err_msg = NULL; DBusMessage *signal = NULL; @@ -1606,18 +1870,31 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } - err_msg = load_profile(u, name); + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + + err_msg = load_profile(u, r_channel, name); if(err_msg != NULL){ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); return; } + if(channel == u->channels){ + for(uint32_t c = 1; c < u->channels; ++c){ + load_profile(u, c, name); + } + } pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); @@ -1630,6 +1907,17 @@ void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } +void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t channels; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + channels = (uint32_t) u->channels; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &channels); +} + void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t n_coefs; @@ -1637,7 +1925,7 @@ void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); - n_coefs = (uint32_t) PROFILE_SIZE; + n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } @@ -1663,84 +1951,19 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u) pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -static double * get_filter(struct userdata *u){ - float *H; - double *H_; - unsigned a_i; - H_ = pa_xnew0(double, PROFILE_SIZE); - a_i = pa_aupdate_read_begin(u->a_H); - H = u->Hs[a_i]; - H_[0] = u->Xs[a_i]; - for(size_t i = 0;i < FILTER_SIZE; ++i){ - H_[i + 1] = H[i] * u->fft_size; - } - pa_aupdate_read_end(u->a_H); - return H_; -} - -void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - unsigned n_coefs; - double *H_; - pa_assert_se(u = (struct userdata *) _u); - - n_coefs = PROFILE_SIZE; - pa_assert(conn); - pa_assert(msg); - H_ = get_filter(u); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); - pa_xfree(H_); -} - -static void set_filter(struct userdata *u, double *H_){ - unsigned a_i= pa_aupdate_write_begin(u->a_H); - float *H = u->Hs[a_i]; - u->Xs[a_i] = H_[0]; - for(size_t i = 0; i < FILTER_SIZE; ++i){ - H[i] = (float) H_[i + 1]; - } - fix_filter(H + 1, u->fft_size); - pa_aupdate_write_end(u->a_H); -} - -void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - double *H; - unsigned _n_coefs; - DBusMessage *signal = NULL; - pa_assert_se(u = (struct userdata *) _u); - pa_assert(conn); - pa_assert(msg); - - if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ - return; - } - if(_n_coefs != PROFILE_SIZE){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs); - return; - } - set_filter(u, H); - - pa_dbus_send_empty_reply(conn, msg); - - pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); - pa_dbus_protocol_send_signal(u->dbus_protocol, signal); - dbus_message_unref(signal); -} - void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - uint32_t rev, n_coefs, rate, fft_size; - double *H; + uint32_t rev, n_coefs, rate, fft_size, channels; pa_assert_se(u = (struct userdata *) _u); pa_assert(msg); rev = 1; - n_coefs = (uint32_t) PROFILE_SIZE; + n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE; rate = (uint32_t) u->sink->sample_spec.rate; fft_size = (uint32_t) u->fft_size; + channels = (uint32_t) u->channels; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1750,9 +1973,7 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - H = get_filter(u); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); - pa_xfree(H); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_CHANNELS].property_name, DBUS_TYPE_UINT32, &channels); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); -- cgit From f5ceed8151214a95ca2192ed1c681eb31cb0bc98 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 20 Aug 2009 18:24:19 -0700 Subject: module-equalizer-sink: added server side persistance of profile names --- src/modules/module-equalizer-sink.c | 221 +++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 98b6b89b..c531468d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -122,6 +122,7 @@ struct userdata { pa_bool_t set_default; pa_database *database; + char **base_profiles; }; static const char* const valid_modargs[] = { @@ -143,7 +144,7 @@ static const char* const valid_modargs[] = { #define EQ_STATE_DB "equalizer-state" #define FILTER_SIZE (u->fft_size / 2 + 1) #define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1) -#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) +#define FILTER_STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -199,7 +200,7 @@ static int is_monotonic(const uint32_t *xs,size_t length){ /* Called from I/O thread context */ -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { @@ -234,7 +235,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse /* Called from main context */ -static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); @@ -249,7 +250,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { } /* Called from I/O thread context */ -static void sink_request_rewind(pa_sink *s) { +static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -264,7 +265,7 @@ static void sink_request_rewind(pa_sink *s) { } /* Called from I/O thread context */ -static void sink_update_requested_latency(pa_sink *s) { +static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -280,6 +281,35 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } +/* Called from main context */ +static void sink_set_volume_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); +} + +/* Called from main context */ +static void sink_set_mute_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); +} + + //reference implementation static void dsp_logic( float * restrict dst,//used as a temp array too, needs to be fft_length! @@ -581,6 +611,26 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return 0; } +/* Called from main context */ +static void sink_input_volume_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_volume_changed(u->sink, &i->volume); +} + +/* Called from main context */ +static void sink_input_mute_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_mute_changed(u->sink, i->muted); +} + static void reset_filter(struct userdata *u){ u->samples_gathered = 0; for(size_t i = 0;i < u->channels; ++i){ @@ -745,6 +795,41 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } +static void pack(char **strs, size_t len, char **packed, size_t *length){ + size_t t_len = 0; + size_t headers = (1+len) * sizeof(uint16_t); + size_t offset = sizeof(uint16_t); + for(size_t i = 0; i < len; ++i){ + t_len += strlen(strs[i]); + } + *length = headers + t_len; + *packed = pa_xmalloc0(*length); + ((uint16_t *) *packed)[0] = (uint16_t) len; + for(size_t i = 0; i < len; ++i){ + uint16_t l = strlen(strs[i]); + *((uint16_t *)(*packed + offset)) = l; + offset += sizeof(uint16_t); + memcpy(*packed + offset, strs[i], l); + offset += l; + } +} +static void unpack(char *str, size_t length, char ***strs, size_t *len){ + size_t offset = sizeof(uint16_t); + *len = ((uint16_t *)str)[0]; + *strs = pa_xnew(char *, *len); + for(size_t i = 0; i < *len; ++i){ + size_t l = *((uint16_t *)(str+offset)); + size_t e = PA_MIN(offset + l, length) - offset; + offset = PA_MIN(offset + sizeof(uint16_t), length); + if(e > 0){ + (*strs)[i] = pa_xnew(char, e + 1); + memcpy((*strs)[i], strs + offset, e); + (*strs)[i][e] = '\0'; + }else{ + (*strs)[i]=NULL; + } + } +} static void save_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); @@ -767,18 +852,26 @@ static void save_profile(struct userdata *u, size_t channel, char *name){ data.size = profile_size; pa_database_set(u->database, &key, &data, TRUE); pa_database_sync(u->database); + if(u->base_profiles[channel]){ + pa_xfree(u->base_profiles[channel]); + } + u->base_profiles[channel] = pa_xstrdup(name); } static void save_state(struct userdata *u){ unsigned a_i; - const size_t state_size = STATE_SIZE * sizeof(float); + const size_t filter_state_size = FILTER_STATE_SIZE * sizeof(float); float *H_n, *state; float *H; pa_datum key, data; pa_database *database; char *dbname; char *state_name = u->name; - state = pa_xnew0(float, STATE_SIZE); + char *packed; + size_t packed_length; + + pack(u->base_profiles, u->channels, &packed, &packed_length); + state = (float *) pa_xmalloc0(filter_state_size + packed_length); for(size_t c = 0; c < u->channels; ++c){ a_i = pa_aupdate_read_begin(u->a_H[c]); @@ -788,11 +881,13 @@ static void save_state(struct userdata *u){ memcpy(H_n, H, FILTER_SIZE * sizeof(float)); pa_aupdate_read_end(u->a_H[c]); } + memcpy(((char *)state) + filter_state_size, packed, packed_length); + pa_xfree(packed); key.data = state_name; key.size = strlen(key.data); data.data = state; - data.size = state_size; + data.size = filter_state_size + packed_length; //thread safety for 0.9.17? pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); pa_assert_se(database = pa_database_open(dbname, TRUE)); @@ -828,6 +923,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){ memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); fix_filter(u->Hs[channel][a_i], u->fft_size); pa_aupdate_write_end(u->a_H[channel]); + if(u->base_profiles[channel]){ + pa_xfree(u->base_profiles[channel]); + } + u->base_profiles[channel] = pa_xstrdup(name); }else{ return "incompatible size"; } @@ -845,7 +944,6 @@ static void load_state(struct userdata *u){ pa_database *database; char *dbname; char *state_name = u->name; - pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); database = pa_database_open(dbname, FALSE); pa_xfree(dbname); @@ -857,14 +955,26 @@ static void load_state(struct userdata *u){ key.size = strlen(key.data); if(pa_database_get(database, &key, &value) != NULL){ - size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels); - float *state = (float *) value.data; - for(size_t c = 0; c < states; ++c){ - a_i = pa_aupdate_write_begin(u->a_H[c]); - H = state + c * CHANNEL_PROFILE_SIZE + 1; - u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; - memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); - pa_aupdate_write_end(u->a_H[c]); + if(value.size > FILTER_STATE_SIZE * sizeof(float) + sizeof(uint16_t)){ + float *state = (float *) value.data; + size_t n_profs; + char **names; + for(size_t c = 0; c < u->channels; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = state + c * CHANNEL_PROFILE_SIZE + 1; + u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; + memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); + n_profs = PA_MIN(n_profs, u->channels); + for(size_t c = 0; c < n_profs; ++c){ + if(u->base_profiles[c]){ + pa_xfree(u->base_profiles[c]); + } + u->base_profiles[c] = names[c]; + } + pa_xfree(names); } pa_datum_free(&value); } @@ -898,8 +1008,6 @@ static void * alloc(size_t x,size_t s){ size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); float *t; pa_assert(f >= x*s); - //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); - //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); t = fftwf_malloc(f); memset(t, 0, f); return t; @@ -914,7 +1022,6 @@ int pa__init(pa_module*m) { pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; - pa_bool_t *use_default = NULL; size_t fs; float *H; unsigned a_i; @@ -988,6 +1095,8 @@ int pa__init(pa_module*m) { hanning_window(u->W, u->window_size); u->first_iteration = TRUE; + u->base_profiles = pa_xnew0(char *, u->channels); + /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -1007,7 +1116,9 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); + u->sink = pa_sink_new(m->core, &sink_data, + PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| + (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -1015,10 +1126,12 @@ int pa__init(pa_module*m) { goto fail; } u->name=pa_xstrdup(u->sink->name); - u->sink->parent.process_msg = sink_process_msg; - u->sink->set_state = sink_set_state; - u->sink->update_requested_latency = sink_update_requested_latency; - u->sink->request_rewind = sink_request_rewind; + u->sink->parent.process_msg = sink_process_msg_cb; + u->sink->set_state = sink_set_state_cb; + u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->request_rewind = sink_request_rewind_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->set_mute = sink_set_mute_cb; u->sink->userdata = u; u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence); @@ -1053,6 +1166,9 @@ int pa__init(pa_module*m) { u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; + u->sink_input->volume_changed = sink_input_volume_changed_cb; + u->sink_input->mute_changed = sink_input_mute_changed_cb; + u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -1060,7 +1176,6 @@ int pa__init(pa_module*m) { pa_modargs_free(ma); - pa_xfree(use_default); dbus_init(u); @@ -1084,7 +1199,6 @@ fail: if (ma) pa_modargs_free(ma); - pa_xfree(use_default); pa__done(m); @@ -1112,6 +1226,13 @@ void pa__done(pa_module*m) { dbus_done(u); + for(size_t c = 0; c < u->channels; ++c){ + if(u->base_profiles[c]){ + pa_xfree(u->base_profiles[c]); + } + } + pa_xfree(u->base_profiles); + /* See comments in sink_input_kill_cb() above regarding * destruction order! */ @@ -1181,6 +1302,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1246,6 +1368,7 @@ enum equalizer_method_index { EQUALIZER_METHOD_LOAD_PROFILE, EQUALIZER_METHOD_SET_FILTER, EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_GET_PROFILE_NAME, EQUALIZER_METHOD_MAX }; @@ -1290,6 +1413,10 @@ pa_dbus_arg_info load_profile_args[]={ {"channel", "u","in"}, {"name", "s","in"} }; +pa_dbus_arg_info base_profile_name_args[]={ + {"channel", "u","in"}, + {"name", "s","out"} +}; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ [EQUALIZER_METHOD_SEED_FILTER]{ @@ -1321,7 +1448,12 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .method_name="LoadProfile", .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_handle_load_profile} + .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_GET_PROFILE_NAME]{ + .method_name="BaseProfile", + .arguments=base_profile_name_args, + .n_arguments=sizeof(base_profile_name_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_profile_name} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1631,9 +1763,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_xfree(ys); - //Stupid for IO reasons? Add a save signal to dbus instead - //save_state(u); - pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); @@ -1902,6 +2031,36 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_message_unref(signal); } +void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u = (struct userdata *) _u; + DBusError error; + uint32_t channel, r_channel; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + if(u->base_profiles[r_channel]){ + pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); + }else{ + pa_dbus_send_empty_reply(conn, msg); + } +} + void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); -- cgit From 263b683437b9a88722f80fd0abea9ca1998fbd36 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 20 Aug 2009 23:55:02 -0700 Subject: module-equalizer-sink: fix improper usage of pa_modargs_get_value_boolean for u->set_default --- src/modules/module-equalizer-sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index c531468d..9a79cb97 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -753,6 +753,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); pa_sink_attach_within_thread(u->sink); if(u->set_default){ + pa_log("Setting default sink to %s", u->sink->name); pa_namereg_set_default_sink(u->module->core, u->sink); } } @@ -1056,7 +1057,7 @@ int pa__init(pa_module*m) { m->userdata = u; u->set_default = TRUE; - u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); + pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); -- cgit From 3053badf0684e077fca8e8fddb43b4e9f2a5c30c Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 23 Aug 2009 15:49:27 -0700 Subject: module-equalizer-sink: resync with ladspa parent sink --- src/modules/module-equalizer-sink.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 9a79cb97..f634d5ea 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -998,9 +998,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - - pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); - pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + if (dest) { + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + } else + pa_sink_set_asyncmsgq(u->sink, NULL); } //ensure's memory allocated is a multiple of v_size -- cgit From bc869b5b28a0e0d4d53bc0a56174cda8212da1ca Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 27 Jun 2009 21:03:37 +0100 Subject: device-manager: Add a new module to keep track of the names and descriptions of various sinks. This will be used as the basis for a queryable system for past and present devices, initially for use in KDE. Currently all this module does is save lists of sinks/sources and their descriptions, so it needs to gain a protocol extension to make this queryable. As things stand it will save the device descriptions of all sinks and restore them if they differ from whats on record. --- src/Makefile.am | 12 +- src/modules/module-device-manager.c | 352 ++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 src/modules/module-device-manager.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6544e2aa..6e3d79b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -990,7 +990,8 @@ modlibexec_LTLIBRARIES += \ module-sine-source.la \ module-detect.la \ module-volume-restore.la \ - module-device-restore.la \ + module-device-manager.la \ + module-device-restore.la \ module-stream-restore.la \ module-card-restore.la \ module-default-device-restore.la \ @@ -1231,7 +1232,8 @@ SYMDEF_FILES = \ modules/jack/module-jack-sink-symdef.h \ modules/jack/module-jack-source-symdef.h \ modules/module-volume-restore-symdef.h \ - modules/module-device-restore-symdef.h \ + modules/module-device-manager-symdef.h \ + modules/module-device-restore-symdef.h \ modules/module-stream-restore-symdef.h \ modules/module-card-restore-symdef.h \ modules/module-default-device-restore-symdef.h \ @@ -1539,6 +1541,12 @@ module_cork_music_on_phone_la_LDFLAGS = $(MODULE_LDFLAGS) module_cork_music_on_phone_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS) +# Device description restore module +module_device_manager_la_SOURCES = modules/module-device-manager.c +module_device_manager_la_LDFLAGS = $(MODULE_LDFLAGS) +module_device_manager_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_device_manager_la_CFLAGS = $(AM_CFLAGS) + # Device volume/muted restore module module_device_restore_la_SOURCES = modules/module-device-restore.c module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c new file mode 100644 index 00000000..96d4a668 --- /dev/null +++ b/src/modules/module-device-manager.c @@ -0,0 +1,352 @@ +/*** + This file is part of PulseAudio. + + Copyright 2006-2008 Lennart Poettering + Copyright 2009 Colin Guthrie + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-device-manager-symdef.h" + +PA_MODULE_AUTHOR("Colin Guthrie"); +PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE("This module does not take any arguments"); + +#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) + +static const char* const valid_modargs[] = { + NULL +}; + +struct userdata { + pa_core *core; + pa_module *module; + pa_subscription *subscription; + pa_hook_slot + *sink_new_hook_slot, + *source_new_hook_slot; + pa_time_event *save_time_event; + pa_database *database; +}; + +#define ENTRY_VERSION 1 + +struct entry { + uint8_t version; + char description[PA_NAME_MAX]; +} PA_GCC_PACKED; + +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { + struct userdata *u = userdata; + + pa_assert(a); + pa_assert(e); + pa_assert(u); + + pa_assert(e == u->save_time_event); + u->core->mainloop->time_free(u->save_time_event); + u->save_time_event = NULL; + + pa_database_sync(u->database); + pa_log_info("Synced."); +} + +static struct entry* read_entry(struct userdata *u, const char *name) { + pa_datum key, data; + struct entry *e; + + pa_assert(u); + pa_assert(name); + + key.data = (char*) name; + key.size = strlen(name); + + pa_zero(data); + + if (!pa_database_get(u->database, &key, &data)) + goto fail; + + if (data.size != sizeof(struct entry)) { + pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry)); + goto fail; + } + + e = (struct entry*) data.data; + + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name); + goto fail; + } + + if (!memchr(e->description, 0, sizeof(e->description))) { + pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name); + goto fail; + } + + return e; + +fail: + + pa_datum_free(&data); + return NULL; +} + +static void trigger_save(struct userdata *u) { + if (u->save_time_event) + return; + + u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); +} + +static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { + if (strncmp(a->description, b->description, sizeof(a->description))) + return FALSE; + + return TRUE; +} + +static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct userdata *u = userdata; + struct entry entry, *old; + char *name; + pa_datum key, data; + + pa_assert(c); + pa_assert(u); + + if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) + return; + + pa_zero(entry); + entry.version = ENTRY_VERSION; + + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { + pa_sink *sink; + + if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) + return; + + name = pa_sprintf_malloc("sink:%s", sink->name); + + if ((old = read_entry(u, name))) + entry = *old; + + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + + } else { + pa_source *source; + + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); + + if (!(source = pa_idxset_get_by_index(c->sources, idx))) + return; + + name = pa_sprintf_malloc("source:%s", source->name); + + if ((old = read_entry(u, name))) + entry = *old; + + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + } + + if (old) { + + if (entries_equal(old, &entry)) { + pa_xfree(old); + pa_xfree(name); + return; + } + + pa_xfree(old); + } + + key.data = name; + key.size = strlen(name); + + data.data = &entry; + data.size = sizeof(entry); + + pa_log_info("Storing device description for %s.", name); + + pa_database_set(u->database, &key, &data, TRUE); + + pa_xfree(name); + + trigger_save(u); +} + +static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + name = pa_sprintf_malloc("sink:%s", new_data->name); + + if ((e = read_entry(u, name))) { + if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + pa_log_info("Restoring description for sink %s.", name); + pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + name = pa_sprintf_malloc("source:%s", new_data->name); + + if ((e = read_entry(u, name))) { + + if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + pa_log_info("Restoring description for sink %s.", name); + pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + char *fname; + pa_sink *sink; + pa_source *source; + uint32_t idx; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); + + u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u); + u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u); + + if (!(fname = pa_state_path("device-manager", TRUE))) + goto fail; + + if (!(u->database = pa_database_open(fname, TRUE))) { + pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno)); + pa_xfree(fname); + goto fail; + } + + pa_log_info("Sucessfully opened database file '%s'.", fname); + pa_xfree(fname); + + for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx)) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); + + for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx)) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); + + pa_modargs_free(ma); + return 0; + +fail: + pa__done(m); + + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata* u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->subscription) + pa_subscription_free(u->subscription); + + if (u->sink_new_hook_slot) + pa_hook_slot_free(u->sink_new_hook_slot); + if (u->source_new_hook_slot) + pa_hook_slot_free(u->source_new_hook_slot); + + if (u->save_time_event) + u->core->mainloop->time_free(u->save_time_event); + + if (u->database) + pa_database_close(u->database); + + pa_xfree(u); +} -- cgit From 37e82cec0ad13923a5db259a88bd00a2840112c6 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 27 Jun 2009 22:08:07 +0100 Subject: device-manager: Add an untested protocol extension. This is effectively copied from the stream restore extension. --- src/Makefile.am | 2 + src/modules/module-device-manager.c | 256 +++++++++++++++++++++++++- src/pulse/context.c | 5 + src/pulse/ext-device-manager.c | 358 ++++++++++++++++++++++++++++++++++++ src/pulse/ext-device-manager.h | 106 +++++++++++ src/pulse/internal.h | 6 + 6 files changed, 732 insertions(+), 1 deletion(-) create mode 100644 src/pulse/ext-device-manager.c create mode 100644 src/pulse/ext-device-manager.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6e3d79b5..b7ebac52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -689,6 +689,7 @@ pulseinclude_HEADERS = \ pulse/context.h \ pulse/def.h \ pulse/error.h \ + pulse/ext-device-manager.h \ pulse/ext-stream-restore.h \ pulse/gccmacro.h \ pulse/introspect.h \ @@ -739,6 +740,7 @@ libpulse_la_SOURCES = \ pulse/context.c pulse/context.h \ pulse/def.h \ pulse/error.c pulse/error.h \ + pulse/ext-device-manager.c pulse/ext-device-manager.h \ pulse/ext-stream-restore.c pulse/ext-stream-restore.h \ pulse/gccmacro.h \ pulse/internal.h \ diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 96d4a668..b41f71c7 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -47,6 +47,9 @@ #include #include #include +#include +#include +#include #include #include "module-device-manager-symdef.h" @@ -69,9 +72,13 @@ struct userdata { pa_subscription *subscription; pa_hook_slot *sink_new_hook_slot, - *source_new_hook_slot; + *source_new_hook_slot, + *connection_unlink_hook_slot; pa_time_event *save_time_event; pa_database *database; + + pa_native_protocol *protocol; + pa_idxset *subscribed; }; #define ENTRY_VERSION 1 @@ -81,6 +88,15 @@ struct entry { char description[PA_NAME_MAX]; } PA_GCC_PACKED; +enum { + SUBCOMMAND_TEST, + SUBCOMMAND_READ, + SUBCOMMAND_WRITE, + SUBCOMMAND_DELETE, + SUBCOMMAND_SUBSCRIBE, + SUBCOMMAND_EVENT +}; + static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; @@ -272,6 +288,230 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data return PA_HOOK_OK; } +static char *get_name(const char *key, const char *prefix) { + char *t; + + if (strncmp(key, prefix, sizeof(prefix))) + return NULL; + + t = pa_xstrdup(key + sizeof(prefix)); + return t; +} + +static void apply_entry(struct userdata *u, const char *name, struct entry *e) { + pa_sink *sink; + pa_source *source; + uint32_t idx; + + pa_assert(u); + pa_assert(name); + pa_assert(e); + + for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) { + char *n; + + if (!(n = get_name(name, "sink"))) + continue; + + if (!pa_streq(sink->name, n)) { + pa_xfree(n); + continue; + } + pa_xfree(n); + + pa_log_info("Restoring description for sink %s.", sink->name); + pa_proplist_sets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } + + for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) { + char *n; + + if (!(n = get_name(name, "source"))) + continue; + + if (!pa_streq(source->name, n)) { + pa_xfree(n); + continue; + } + pa_xfree(n); + + pa_log_info("Restoring description for source %s.", source->name); + pa_proplist_sets(source->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } +} + +#define EXT_VERSION 1 + +static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { + struct userdata *u; + uint32_t command; + pa_tagstruct *reply = NULL; + + pa_assert(p); + pa_assert(m); + pa_assert(c); + pa_assert(t); + + u = m->userdata; + + if (pa_tagstruct_getu32(t, &command) < 0) + goto fail; + + reply = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); + pa_tagstruct_putu32(reply, tag); + + switch (command) { + case SUBCOMMAND_TEST: { + if (!pa_tagstruct_eof(t)) + goto fail; + + pa_tagstruct_putu32(reply, EXT_VERSION); + break; + } + + case SUBCOMMAND_READ: { + pa_datum key; + pa_bool_t done; + + if (!pa_tagstruct_eof(t)) + goto fail; + + done = !pa_database_first(u->database, &key, NULL); + + while (!done) { + pa_datum next_key; + struct entry *e; + char *name; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + name = pa_xstrndup(key.data, key.size); + pa_datum_free(&key); + + if ((e = read_entry(u, name))) { + pa_tagstruct_puts(reply, name); + pa_tagstruct_puts(reply, e->description); + + pa_xfree(e); + } + + pa_xfree(name); + + key = next_key; + } + + break; + } + + case SUBCOMMAND_WRITE: { + uint32_t mode; + pa_bool_t apply_immediately = FALSE; + + if (pa_tagstruct_getu32(t, &mode) < 0 || + pa_tagstruct_get_boolean(t, &apply_immediately) < 0) + goto fail; + + if (mode != PA_UPDATE_MERGE && + mode != PA_UPDATE_REPLACE && + mode != PA_UPDATE_SET) + goto fail; + + if (mode == PA_UPDATE_SET) + pa_database_clear(u->database); + + while (!pa_tagstruct_eof(t)) { + const char *name, *description; + struct entry entry; + pa_datum key, data; + + pa_zero(entry); + entry.version = ENTRY_VERSION; + + if (pa_tagstruct_gets(t, &name) < 0 || + pa_tagstruct_gets(reply, &description) < 0) + goto fail; + + if (!name || !*name) + goto fail; + + pa_strlcpy(entry.description, description, sizeof(entry.description)); + + key.data = (char*) name; + key.size = strlen(name); + + data.data = &entry; + data.size = sizeof(entry); + + if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) + if (apply_immediately) + apply_entry(u, name, &entry); + } + + trigger_save(u); + + break; + } + + case SUBCOMMAND_DELETE: + + while (!pa_tagstruct_eof(t)) { + const char *name; + pa_datum key; + + if (pa_tagstruct_gets(t, &name) < 0) + goto fail; + + key.data = (char*) name; + key.size = strlen(name); + + pa_database_unset(u->database, &key); + } + + trigger_save(u); + + break; + + case SUBCOMMAND_SUBSCRIBE: { + + pa_bool_t enabled; + + if (pa_tagstruct_get_boolean(t, &enabled) < 0 || + !pa_tagstruct_eof(t)) + goto fail; + + if (enabled) + pa_idxset_put(u->subscribed, c, NULL); + else + pa_idxset_remove_by_data(u->subscribed, c, NULL); + + break; + } + + default: + goto fail; + } + + pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply); + return 0; + + fail: + + if (reply) + pa_tagstruct_free(reply); + + return -1; +} + +static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) { + pa_assert(p); + pa_assert(c); + pa_assert(u); + + pa_idxset_remove_by_data(u->subscribed, c, NULL); + return PA_HOOK_OK; +} + int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; @@ -290,6 +530,12 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; + u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + + u->protocol = pa_native_protocol_get(m->core); + pa_native_protocol_install_ext(u->protocol, m, extension_cb); + + u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u); u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); @@ -348,5 +594,13 @@ void pa__done(pa_module*m) { if (u->database) pa_database_close(u->database); + if (u->protocol) { + pa_native_protocol_remove_ext(u->protocol, m); + pa_native_protocol_unref(u->protocol); + } + + if (u->subscribed) + pa_idxset_free(u->subscribed, NULL, NULL); + pa_xfree(u); } diff --git a/src/pulse/context.c b/src/pulse/context.c index 23ae30ce..7468d0a9 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -128,6 +128,9 @@ static void reset_callbacks(pa_context *c) { c->event_callback = NULL; c->event_userdata = NULL; + c->ext_device_manager.callback = NULL; + c->ext_device_manager.userdata = NULL; + c->ext_stream_restore.callback = NULL; c->ext_stream_restore.userdata = NULL; } @@ -1434,6 +1437,8 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t if (!strcmp(name, "module-stream-restore")) pa_ext_stream_restore_command(c, tag, t); + else if (!strcmp(name, "module-device-manager")) + pa_ext_device_manager_command(c, tag, t); else pa_log(_("Received message for unknown extension '%s'"), name); diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c new file mode 100644 index 00000000..1c6eee5e --- /dev/null +++ b/src/pulse/ext-device-manager.c @@ -0,0 +1,358 @@ +/*** + This file is part of PulseAudio. + + Copyright 2008 Lennart Poettering + Copyright 2009 Colin Guthrie + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "internal.h" +#include "operation.h" +#include "fork-detect.h" + +#include "ext-device-manager.h" + +enum { + SUBCOMMAND_TEST, + SUBCOMMAND_READ, + SUBCOMMAND_WRITE, + SUBCOMMAND_DELETE, + SUBCOMMAND_SUBSCRIBE, + SUBCOMMAND_EVENT +}; + +static void ext_device_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + uint32_t version = PA_INVALID_INDEX; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) + goto finish; + + } else if (pa_tagstruct_getu32(t, &version) < 0 || + !pa_tagstruct_eof(t)) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_ext_device_manager_test_cb_t cb = (pa_ext_device_manager_test_cb_t) o->callback; + cb(o->context, version, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation *pa_ext_device_manager_test( + pa_context *c, + pa_ext_device_manager_test_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_TEST); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_operation *o = userdata; + int eol = 1; + + pa_assert(pd); + pa_assert(o); + pa_assert(PA_REFCNT_VALUE(o) >= 1); + + if (!o->context) + goto finish; + + if (command != PA_COMMAND_REPLY) { + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) + goto finish; + + eol = -1; + } else { + + while (!pa_tagstruct_eof(t)) { + pa_ext_device_manager_info i; + + memset(&i, 0, sizeof(i)); + + if (pa_tagstruct_gets(t, &i.name) < 0 || + pa_tagstruct_gets(t, &i.description) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->callback) { + pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback; + cb(o->context, &i, 0, o->userdata); + } + } + } + + if (o->callback) { + pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback; + cb(o->context, NULL, eol, o->userdata); + } + +finish: + pa_operation_done(o); + pa_operation_unref(o); +} + +pa_operation *pa_ext_device_manager_read( + pa_context *c, + pa_ext_device_manager_read_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_READ); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation *pa_ext_device_manager_write( + pa_context *c, + pa_update_mode_t mode, + const pa_ext_device_manager_info data[], + unsigned n, + int apply_immediately, + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET); + pa_assert(data); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_WRITE); + + pa_tagstruct_putu32(t, mode); + pa_tagstruct_put_boolean(t, apply_immediately); + + for (; n > 0; n--, data++) { + if (!data->name || !*data->name) + goto fail; + + pa_tagstruct_puts(t, data->name); + pa_tagstruct_puts(t, data->description); + } + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; +} + +pa_operation *pa_ext_device_manager_delete( + pa_context *c, + const char *const s[], + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; + const char *const *k; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(s); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_DELETE); + + for (k = s; *k; k++) { + if (!*k || !**k) + goto fail; + + pa_tagstruct_puts(t, *k); + } + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; +} + +pa_operation *pa_ext_device_manager_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o; + pa_tagstruct *t; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE); + pa_tagstruct_put_boolean(t, enable); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +void pa_ext_device_manager_set_subscribe_cb( + pa_context *c, + pa_ext_device_manager_subscribe_cb_t cb, + void *userdata) { + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + if (pa_detect_fork()) + return; + + c->ext_device_manager.callback = cb; + c->ext_device_manager.userdata = userdata; +} + +void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) { + uint32_t subcommand; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + pa_assert(t); + + if (pa_tagstruct_getu32(t, &subcommand) < 0 || + !pa_tagstruct_eof(t)) { + + pa_context_fail(c, PA_ERR_PROTOCOL); + return; + } + + if (subcommand != SUBCOMMAND_EVENT) { + pa_context_fail(c, PA_ERR_PROTOCOL); + return; + } + + if (c->ext_device_manager.callback) + c->ext_device_manager.callback(c, c->ext_device_manager.userdata); +} diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h new file mode 100644 index 00000000..eed0c50c --- /dev/null +++ b/src/pulse/ext-device-manager.h @@ -0,0 +1,106 @@ +#ifndef foopulseextdevicemanagerhfoo +#define foopulseextdevicemanagerhfoo + +/*** + This file is part of PulseAudio. + + Copyright 2008 Lennart Poettering + Copyright 2009 Colin Guthrie + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio 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 Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include +#include + +/** \file + * + * Routines for controlling module-stream-restore + */ + +PA_C_DECL_BEGIN + +/** Stores information about one device in the device database that is + * maintained by module-device-manager. \since 0.9.17 */ +typedef struct pa_ext_device_manager_info { + const char *name; /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */ + const char *description; /**< The description of the device when it was last seen, if applicable and saved */ +} pa_ext_device_manager_info; + +/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.17 */ +typedef void (*pa_ext_device_manager_test_cb_t)( + pa_context *c, + uint32_t version, + void *userdata); + +/** Test if this extension module is available in the server. \since 0.9.17 */ +pa_operation *pa_ext_device_manager_test( + pa_context *c, + pa_ext_device_manager_test_cb_t cb, + void *userdata); + +/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.17 */ +typedef void (*pa_ext_device_manager_read_cb_t)( + pa_context *c, + const pa_ext_device_manager_info *info, + int eol, + void *userdata); + +/** Read all entries from the device database. \since 0.9.17 */ +pa_operation *pa_ext_device_manager_read( + pa_context *c, + pa_ext_device_manager_read_cb_t cb, + void *userdata); + +/** Store entries in the device database. \since 0.9.17 */ +pa_operation *pa_ext_device_manager_write( + pa_context *c, + pa_update_mode_t mode, + const pa_ext_device_manager_info data[], + unsigned n, + int apply_immediately, + pa_context_success_cb_t cb, + void *userdata); + +/** Delete entries from the device database. \since 0.9.17 */ +pa_operation *pa_ext_device_manager_delete( + pa_context *c, + const char *const s[], + pa_context_success_cb_t cb, + void *userdata); + +/** Subscribe to changes in the device database. \since 0.9.17 */ +pa_operation *pa_ext_device_manager_subscribe( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata); + +/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.17 */ +typedef void (*pa_ext_device_manager_subscribe_cb_t)( + pa_context *c, + void *userdata); + +/** Set the subscription callback that is called when + * pa_ext_device_manager_subscribe() was called. \since 0.9.17 */ +void pa_ext_device_manager_set_subscribe_cb( + pa_context *c, + pa_ext_device_manager_subscribe_cb_t cb, + void *userdata); + +PA_C_DECL_END + +#endif diff --git a/src/pulse/internal.h b/src/pulse/internal.h index e069c9e9..b371bfc2 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -101,6 +102,10 @@ struct pa_context { uint32_t client_index; /* Extension specific data */ + struct { + pa_ext_device_manager_subscribe_cb_t callback; + void *userdata; + } ext_device_manager; struct { pa_ext_stream_restore_subscribe_cb_t callback; void *userdata; @@ -283,6 +288,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta #define PA_FAIL_RETURN_NULL(context, error) \ PA_FAIL_RETURN_ANY(context, error, NULL) +void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t); void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t); pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m); -- cgit From 93c3c655e436862e2340a9d8a90d6b8a58c9a6e1 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 27 Jun 2009 22:09:00 +0100 Subject: device-manager: Fix indentation --- src/Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index b7ebac52..0a041eb3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -992,8 +992,8 @@ modlibexec_LTLIBRARIES += \ module-sine-source.la \ module-detect.la \ module-volume-restore.la \ - module-device-manager.la \ - module-device-restore.la \ + module-device-manager.la \ + module-device-restore.la \ module-stream-restore.la \ module-card-restore.la \ module-default-device-restore.la \ @@ -1234,8 +1234,8 @@ SYMDEF_FILES = \ modules/jack/module-jack-sink-symdef.h \ modules/jack/module-jack-source-symdef.h \ modules/module-volume-restore-symdef.h \ - modules/module-device-manager-symdef.h \ - modules/module-device-restore-symdef.h \ + modules/module-device-manager-symdef.h \ + modules/module-device-restore-symdef.h \ modules/module-stream-restore-symdef.h \ modules/module-card-restore-symdef.h \ modules/module-default-device-restore-symdef.h \ -- cgit From 0b3b037e222e076e506bbcbdbeae4cd1dad96c40 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 28 Jun 2009 13:08:17 +0100 Subject: device-manager: Export device-manager extension functions --- src/map-file | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/map-file b/src/map-file index 95b2803a..cec688fb 100644 --- a/src/map-file +++ b/src/map-file @@ -144,6 +144,12 @@ pa_cvolume_set_fade; pa_cvolume_set_position; pa_cvolume_snprint; pa_cvolume_valid; +pa_ext_device_manager_delete; +pa_ext_device_manager_read; +pa_ext_device_manager_set_subscribe_cb; +pa_ext_device_manager_subscribe; +pa_ext_device_manager_test; +pa_ext_device_manager_write; pa_ext_stream_restore_delete; pa_ext_stream_restore_read; pa_ext_stream_restore_set_subscribe_cb; -- cgit From 70accbbd61ae1205a009a4bfa6ae7514bc0bd940 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 28 Jun 2009 13:21:43 +0100 Subject: device-manager: Link native protocol library. --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 0a041eb3..8ed74823 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1546,7 +1546,7 @@ module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS) # Device description restore module module_device_manager_la_SOURCES = modules/module-device-manager.c module_device_manager_la_LDFLAGS = $(MODULE_LDFLAGS) -module_device_manager_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_device_manager_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_device_manager_la_CFLAGS = $(AM_CFLAGS) # Device volume/muted restore module -- cgit From 40e97eb698e0211045818c73d03d55e985f329d5 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 28 Jun 2009 14:40:00 +0100 Subject: device-manager: Fix tagstruct description extraction (copy+paste blunder) --- src/modules/module-device-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index b41f71c7..68ed9519 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -429,7 +429,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio entry.version = ENTRY_VERSION; if (pa_tagstruct_gets(t, &name) < 0 || - pa_tagstruct_gets(reply, &description) < 0) + pa_tagstruct_gets(t, &description) < 0) goto fail; if (!name || !*name) -- cgit From 64979385e09ba0a411669f9feeea56c93bf14d38 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 28 Jun 2009 15:33:38 +0100 Subject: device-restore: Fix the application of an entry to allow changing the name of devices. This fixes a few bugs in the copy+pasted implementation of apply_entry()/get_name(). --- src/modules/module-device-manager.c | 70 ++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 68ed9519..3ebdd485 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -289,55 +289,47 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data } static char *get_name(const char *key, const char *prefix) { - char *t; + char *t; - if (strncmp(key, prefix, sizeof(prefix))) - return NULL; + if (strncmp(key, prefix, strlen(prefix))) + return NULL; - t = pa_xstrdup(key + sizeof(prefix)); - return t; + t = pa_xstrdup(key + strlen(prefix)); + return t; } static void apply_entry(struct userdata *u, const char *name, struct entry *e) { - pa_sink *sink; - pa_source *source; - uint32_t idx; - - pa_assert(u); - pa_assert(name); - pa_assert(e); - - for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) { + pa_sink *sink; + pa_source *source; + uint32_t idx; char *n; - if (!(n = get_name(name, "sink"))) - continue; - - if (!pa_streq(sink->name, n)) { - pa_xfree(n); - continue; - } - pa_xfree(n); - - pa_log_info("Restoring description for sink %s.", sink->name); - pa_proplist_sets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); - } - - for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) { - char *n; + pa_assert(u); + pa_assert(name); + pa_assert(e); - if (!(n = get_name(name, "source"))) - continue; + if ((n = get_name(name, "sink:"))) { + for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) { + if (!pa_streq(sink->name, n)) { + continue; + } - if (!pa_streq(source->name, n)) { - pa_xfree(n); - continue; + pa_log_info("Setting description for sink %s.", sink->name); + pa_sink_set_description(sink, e->description); + } + pa_xfree(n); + } + else if ((n = get_name(name, "source:"))) { + for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) { + if (!pa_streq(source->name, n)) { + continue; + } + + pa_log_info("Setting description for source %s.", source->name); + pa_source_set_description(source, e->description); + } + pa_xfree(n); } - pa_xfree(n); - - pa_log_info("Restoring description for source %s.", source->name); - pa_proplist_sets(source->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); - } } #define EXT_VERSION 1 -- cgit From 42b30e1aa2a134ccd90486b3dc73d1b13a7636a6 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 29 Jun 2009 19:55:34 +0100 Subject: stream-restore: Preventative initialistion to NULL There is not technically a bug here due to the early return and the knowledge that one of the if blocks will definitely be run, but this makes sure we don't call free on uninitialised data or do anything else suitibly daft. Also helps when you copy the code and change it slightly and don't realise you've left things open... --- src/modules/module-stream-restore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 9b6f9143..b7b36be4 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -281,8 +281,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; - struct entry entry, *old; - char *name; + struct entry entry, *old = NULL; + char *name = NULL; pa_datum key, data; pa_assert(c); -- cgit From aa5d56ba752490abb9a8a18081196d9de2f6976b Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 29 Jun 2009 20:10:04 +0100 Subject: device-manager: Only store and save details for non-monitor sources --- src/modules/module-device-manager.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 3ebdd485..77b6f2fe 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -168,8 +168,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; - struct entry entry, *old; - char *name; + struct entry entry, *old = NULL; + char *name = NULL; pa_datum key, data; pa_assert(c); @@ -205,6 +205,9 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (!(source = pa_idxset_get_by_index(c->sources, idx))) return; + if (source->monitor_of) + return; + name = pa_sprintf_malloc("source:%s", source->name); if ((old = read_entry(u, name))) @@ -251,7 +254,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new if ((e = read_entry(u, name))) { if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { - pa_log_info("Restoring description for sink %s.", name); + pa_log_info("Restoring description for sink %s.", new_data->name); pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); } @@ -276,7 +279,8 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data if ((e = read_entry(u, name))) { if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { - pa_log_info("Restoring description for sink %s.", name); + /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ + pa_log_info("Restoring description for sink %s.", new_data->name); pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); } @@ -325,6 +329,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { continue; } + if (source->monitor_of) { + pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name); + continue; + } + pa_log_info("Setting description for source %s.", source->name); pa_source_set_description(source, e->description); } -- cgit From 464e1a89868b7c68927d7c95b4ff7d8fe3dbb0f0 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 5 Jul 2009 19:40:06 +0100 Subject: device-manager: Fix copy+paste leftover --- src/pulse/ext-device-manager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index eed0c50c..422691fe 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -28,7 +28,7 @@ /** \file * - * Routines for controlling module-stream-restore + * Routines for controlling module-device-manager */ PA_C_DECL_BEGIN -- cgit From 9357bdf4e7c069e29d2fa403a01a892d80d2d89f Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 19 Sep 2009 15:32:13 +0100 Subject: device-manager: Update docs version -> 0.9.19 (predicted) --- src/pulse/ext-device-manager.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index 422691fe..33bcbfa8 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -34,38 +34,38 @@ PA_C_DECL_BEGIN /** Stores information about one device in the device database that is - * maintained by module-device-manager. \since 0.9.17 */ + * maintained by module-device-manager. \since 0.9.19 */ typedef struct pa_ext_device_manager_info { const char *name; /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */ const char *description; /**< The description of the device when it was last seen, if applicable and saved */ } pa_ext_device_manager_info; -/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.17 */ +/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.19 */ typedef void (*pa_ext_device_manager_test_cb_t)( pa_context *c, uint32_t version, void *userdata); -/** Test if this extension module is available in the server. \since 0.9.17 */ +/** Test if this extension module is available in the server. \since 0.9.19 */ pa_operation *pa_ext_device_manager_test( pa_context *c, pa_ext_device_manager_test_cb_t cb, void *userdata); -/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.17 */ +/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.19 */ typedef void (*pa_ext_device_manager_read_cb_t)( pa_context *c, const pa_ext_device_manager_info *info, int eol, void *userdata); -/** Read all entries from the device database. \since 0.9.17 */ +/** Read all entries from the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_read( pa_context *c, pa_ext_device_manager_read_cb_t cb, void *userdata); -/** Store entries in the device database. \since 0.9.17 */ +/** Store entries in the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_write( pa_context *c, pa_update_mode_t mode, @@ -75,27 +75,27 @@ pa_operation *pa_ext_device_manager_write( pa_context_success_cb_t cb, void *userdata); -/** Delete entries from the device database. \since 0.9.17 */ +/** Delete entries from the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_delete( pa_context *c, const char *const s[], pa_context_success_cb_t cb, void *userdata); -/** Subscribe to changes in the device database. \since 0.9.17 */ +/** Subscribe to changes in the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_subscribe( pa_context *c, int enable, pa_context_success_cb_t cb, void *userdata); -/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.17 */ +/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.19 */ typedef void (*pa_ext_device_manager_subscribe_cb_t)( pa_context *c, void *userdata); /** Set the subscription callback that is called when - * pa_ext_device_manager_subscribe() was called. \since 0.9.17 */ + * pa_ext_device_manager_subscribe() was called. \since 0.9.19 */ void pa_ext_device_manager_set_subscribe_cb( pa_context *c, pa_ext_device_manager_subscribe_cb_t cb, -- cgit From 103897a1e33fe83f6ba0b7d521ccc2e36da43881 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 19 Sep 2009 16:13:25 +0100 Subject: device-manager: Provide a way for clients to enable/disable role-based device-priority routing. The routing logic itself does not yet exist, but the command currently will unload/load module-stream-restore as approriate. (module-stream-restore would conflict with the role-based priority-routing). --- src/modules/module-device-manager.c | 56 +++++++++++++++++++++++++++++++++++++ src/pulse/ext-device-manager.c | 32 +++++++++++++++++++++ src/pulse/ext-device-manager.h | 7 +++++ 3 files changed, 95 insertions(+) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 77b6f2fe..5685dbb8 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -79,6 +79,10 @@ struct userdata { pa_native_protocol *protocol; pa_idxset *subscribed; + + pa_bool_t role_device_priority_routing; + pa_bool_t stream_restore_used; + pa_bool_t checked_stream_restore; }; #define ENTRY_VERSION 1 @@ -93,6 +97,7 @@ enum { SUBCOMMAND_READ, SUBCOMMAND_WRITE, SUBCOMMAND_DELETE, + SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -473,6 +478,57 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; + case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: + + while (!pa_tagstruct_eof(t)) { + pa_bool_t enable; + uint32_t sridx = PA_INVALID_INDEX; + uint32_t idx; + pa_module *module; + + if (pa_tagstruct_get_boolean(t, &enable) < 0) + goto fail; + + /* If this is the first run, check for stream restore module */ + if (!u->checked_stream_restore) { + u->checked_stream_restore = TRUE; + + for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { + if (strcmp(module->name, "module-stream-restore") == 0) { + pa_log_debug("Detected module-stream-restore is currently in use"); + u->stream_restore_used = TRUE; + sridx = module->index; + } + } + } + + u->role_device_priority_routing = enable; + if (enable) { + if (u->stream_restore_used) { + if (PA_INVALID_INDEX == sridx) { + /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */ + for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { + if (strcmp(module->name, "module-stream-restore") == 0) { + sridx = module->index; + } + } + } + if (PA_INVALID_INDEX != sridx) { + pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing"); + pa_module_unload_request_by_index(u->core, sridx, TRUE); + } + } + } else if (u->stream_restore_used) { + /* We want to reload module-stream-restore */ + if (!pa_module_load(u->core, "module-stream-restore", "")) + pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing"); + } + } + + trigger_save(u); + + break; + case SUBCOMMAND_SUBSCRIBE: { pa_bool_t enabled; diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index 1c6eee5e..0603a89a 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -41,6 +41,7 @@ enum { SUBCOMMAND_READ, SUBCOMMAND_WRITE, SUBCOMMAND_DELETE, + SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -289,6 +290,37 @@ fail: return NULL; } +pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING); + pa_tagstruct_put_boolean(t, !!enable); + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + pa_operation *pa_ext_device_manager_subscribe( pa_context *c, int enable, diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index 33bcbfa8..29d56ba4 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -82,6 +82,13 @@ pa_operation *pa_ext_device_manager_delete( pa_context_success_cb_t cb, void *userdata); +/** Subscribe to changes in the device database. \since 0.9.19 */ +pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( + pa_context *c, + int enable, + pa_context_success_cb_t cb, + void *userdata); + /** Subscribe to changes in the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_subscribe( pa_context *c, -- cgit From 95f28393ab413c797e2f16d2caf1f8caf0283b71 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 19 Sep 2009 16:46:18 +0100 Subject: device-manager: Fix copy+paste code that looped over the tagstruct when not necessary --- src/modules/module-device-manager.c | 77 ++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 5685dbb8..b3c407ca 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -478,56 +478,53 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; - case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: - - while (!pa_tagstruct_eof(t)) { - pa_bool_t enable; - uint32_t sridx = PA_INVALID_INDEX; - uint32_t idx; - pa_module *module; - - if (pa_tagstruct_get_boolean(t, &enable) < 0) - goto fail; - - /* If this is the first run, check for stream restore module */ - if (!u->checked_stream_restore) { - u->checked_stream_restore = TRUE; - - for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { - if (strcmp(module->name, "module-stream-restore") == 0) { - pa_log_debug("Detected module-stream-restore is currently in use"); - u->stream_restore_used = TRUE; - sridx = module->index; - } + case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: { + + pa_bool_t enable; + uint32_t sridx = PA_INVALID_INDEX; + uint32_t idx; + pa_module *module; + + if (pa_tagstruct_get_boolean(t, &enable) < 0) + goto fail; + + /* If this is the first run, check for stream restore module */ + if (!u->checked_stream_restore) { + u->checked_stream_restore = TRUE; + + for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { + if (strcmp(module->name, "module-stream-restore") == 0) { + pa_log_debug("Detected module-stream-restore is currently in use"); + u->stream_restore_used = TRUE; + sridx = module->index; } } + } - u->role_device_priority_routing = enable; - if (enable) { - if (u->stream_restore_used) { - if (PA_INVALID_INDEX == sridx) { - /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */ - for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { - if (strcmp(module->name, "module-stream-restore") == 0) { - sridx = module->index; - } + u->role_device_priority_routing = enable; + if (enable) { + if (u->stream_restore_used) { + if (PA_INVALID_INDEX == sridx) { + /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */ + for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { + if (strcmp(module->name, "module-stream-restore") == 0) { + sridx = module->index; } } - if (PA_INVALID_INDEX != sridx) { - pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing"); - pa_module_unload_request_by_index(u->core, sridx, TRUE); - } } - } else if (u->stream_restore_used) { - /* We want to reload module-stream-restore */ - if (!pa_module_load(u->core, "module-stream-restore", "")) - pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing"); + if (PA_INVALID_INDEX != sridx) { + pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing"); + pa_module_unload_request_by_index(u->core, sridx, TRUE); + } } + } else if (u->stream_restore_used) { + /* We want to reload module-stream-restore */ + if (!pa_module_load(u->core, "module-stream-restore", "")) + pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing"); } - trigger_save(u); - break; + } case SUBCOMMAND_SUBSCRIBE: { -- cgit From aebe4787f293cc6810c54db751bee7df3a5d1ea2 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 19 Sep 2009 17:48:10 +0100 Subject: device-manager: Provide a method for prefering/defering a device. This allows clients to edit the priroity order. What is not yet in place is the initialisation of that priority list when new devices are detected or the cleaning (remove holes) when devices are removed. In order to keep the storage transparent I will likely remove the write functionality and replace it with a simple rename method. I also still need to expose the priority itself when reading the data. --- src/modules/module-device-manager.c | 148 ++++++++++++++++++++++++++++++++++++ src/pulse/ext-device-manager.c | 74 ++++++++++++++++++ src/pulse/ext-device-manager.h | 16 ++++ 3 files changed, 238 insertions(+) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index b3c407ca..740b98fa 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -87,9 +87,23 @@ struct userdata { #define ENTRY_VERSION 1 +#define NUM_ROLES 9 +enum { + ROLE_NONE, + ROLE_VIDEO, + ROLE_MUSIC, + ROLE_GAME, + ROLE_EVENT, + ROLE_PHONE, + ROLE_ANIMATION, + ROLE_PRODUCTION, + ROLE_A11Y, +}; + struct entry { uint8_t version; char description[PA_NAME_MAX]; + uint32_t priority[NUM_ROLES]; } PA_GCC_PACKED; enum { @@ -98,6 +112,8 @@ enum { SUBCOMMAND_WRITE, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, + SUBCOMMAND_PREFER_DEVICE, + SUBCOMMAND_DEFER_DEVICE, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -346,6 +362,31 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { } } + +static uint32_t get_role_index(const char* role) { + pa_assert(role); + + if (strcmp(role, "") == 0) + return ROLE_NONE; + if (strcmp(role, "video") == 0) + return ROLE_VIDEO; + if (strcmp(role, "music") == 0) + return ROLE_MUSIC; + if (strcmp(role, "game") == 0) + return ROLE_GAME; + if (strcmp(role, "event") == 0) + return ROLE_EVENT; + if (strcmp(role, "phone") == 0) + return ROLE_PHONE; + if (strcmp(role, "animation") == 0) + return ROLE_ANIMATION; + if (strcmp(role, "production") == 0) + return ROLE_PRODUCTION; + if (strcmp(role, "a11y") == 0) + return ROLE_A11Y; + return PA_INVALID_INDEX; +} + #define EXT_VERSION 1 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { @@ -526,6 +567,113 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; } + case SUBCOMMAND_PREFER_DEVICE: + case SUBCOMMAND_DEFER_DEVICE: { + + const char *role, *device; + struct entry *e; + uint32_t role_index; + + if (pa_tagstruct_gets(t, &role) < 0 || + pa_tagstruct_gets(t, &device) < 0) + goto fail; + + if (!role || !device || !*device) + goto fail; + + role_index = get_role_index(role); + if (PA_INVALID_INDEX == role_index) + goto fail; + + if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { + pa_datum key; + pa_datum data; + pa_bool_t done; + char* prefix; + uint32_t priority; + pa_bool_t haschanged = FALSE; + + if (strncmp(device, "sink:", 5) == 0) + prefix = pa_xstrdup("sink:"); + else + prefix = pa_xstrdup("source:"); + + priority = e->priority[role_index]; + + /* Now we need to load up all the other entries of this type and shuffle the priroities around */ + + done = !pa_database_first(u->database, &key, NULL); + + while (!done && !haschanged) { + pa_datum next_key; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + /* Only read devices with the right prefix */ + if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { + char *name; + struct entry *e2; + + name = pa_xstrndup(key.data, key.size); + pa_datum_free(&key); + + if ((e2 = read_entry(u, name))) { + if (SUBCOMMAND_PREFER_DEVICE == command) { + /* PREFER */ + if (e2->priority[role_index] == (priority - 1)) { + e2->priority[role_index]++; + haschanged = TRUE; + } + } else { + /* DEFER */ + if (e2->priority[role_index] == (priority + 1)) { + e2->priority[role_index]--; + haschanged = TRUE; + } + } + + if (haschanged) { + data.data = e2; + data.size = sizeof(*e2); + + if (pa_database_set(u->database, &key, &data, FALSE)) + pa_log_warn("Could not save device"); + } + pa_xfree(e2); + } + + pa_xfree(name); + } + + key = next_key; + } + + /* Now write out our actual entry */ + if (haschanged) { + if (SUBCOMMAND_PREFER_DEVICE == command) + e->priority[role_index]--; + else + e->priority[role_index]++; + + key.data = (char *) device; + key.size = strlen(device); + + data.data = e; + data.size = sizeof(*e); + + if (pa_database_set(u->database, &key, &data, FALSE)) + pa_log_warn("Could not save device"); + + trigger_save(u); + } + + pa_xfree(e); + + pa_xfree(prefix); + } + break; + } + case SUBCOMMAND_SUBSCRIBE: { pa_bool_t enabled; diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index 0603a89a..a634e212 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -42,6 +42,8 @@ enum { SUBCOMMAND_WRITE, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, + SUBCOMMAND_PREFER_DEVICE, + SUBCOMMAND_DEFER_DEVICE, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -321,6 +323,78 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( return o; } +pa_operation *pa_ext_device_manager_prefer_device( + pa_context *c, + const char* role, + const char* device, + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + pa_assert(role); + pa_assert(device); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_PREFER_DEVICE); + pa_tagstruct_puts(t, role); + pa_tagstruct_puts(t, device); + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation *pa_ext_device_manager_defer_device( + pa_context *c, + const char* role, + const char* device, + pa_context_success_cb_t cb, + void *userdata) { + + uint32_t tag; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); + + pa_assert(role); + pa_assert(device); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); + pa_tagstruct_putu32(t, PA_INVALID_INDEX); + pa_tagstruct_puts(t, "module-device-manager"); + pa_tagstruct_putu32(t, SUBCOMMAND_DEFER_DEVICE); + pa_tagstruct_puts(t, role); + pa_tagstruct_puts(t, device); + + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + pa_operation *pa_ext_device_manager_subscribe( pa_context *c, int enable, diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index 29d56ba4..d6de1320 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -89,6 +89,22 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( pa_context_success_cb_t cb, void *userdata); +/** Subscribe to changes in the device database. \since 0.9.19 */ +pa_operation *pa_ext_device_manager_prefer_device( + pa_context *c, + const char* role, + const char* device, + pa_context_success_cb_t cb, + void *userdata); + +/** Subscribe to changes in the device database. \since 0.9.19 */ +pa_operation *pa_ext_device_manager_defer_device( + pa_context *c, + const char* role, + const char* device, + pa_context_success_cb_t cb, + void *userdata); + /** Subscribe to changes in the device database. \since 0.9.19 */ pa_operation *pa_ext_device_manager_subscribe( pa_context *c, -- cgit From f8ec8f382ff1e9a05296b5a656195d44e8d05b44 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 12:44:02 +0100 Subject: device-manager: Change the write function to a rename function. The structure itself will contain various bits of info so exposing this fully to the client is a bad idea. By keeping to a rename operation we keep what we do store abstracted from the clients. Also fix some doxy comments. --- src/modules/module-device-manager.c | 63 +++++++++++++++++-------------------- src/pulse/ext-device-manager.c | 40 ++++++----------------- src/pulse/ext-device-manager.h | 16 +++++----- 3 files changed, 44 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 740b98fa..0a0c39dc 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -109,7 +109,7 @@ struct entry { enum { SUBCOMMAND_TEST, SUBCOMMAND_READ, - SUBCOMMAND_WRITE, + SUBCOMMAND_RENAME, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_PREFER_DEVICE, @@ -451,51 +451,41 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; } - case SUBCOMMAND_WRITE: { - uint32_t mode; - pa_bool_t apply_immediately = FALSE; + case SUBCOMMAND_RENAME: { - if (pa_tagstruct_getu32(t, &mode) < 0 || - pa_tagstruct_get_boolean(t, &apply_immediately) < 0) - goto fail; - - if (mode != PA_UPDATE_MERGE && - mode != PA_UPDATE_REPLACE && - mode != PA_UPDATE_SET) - goto fail; - - if (mode == PA_UPDATE_SET) - pa_database_clear(u->database); - - while (!pa_tagstruct_eof(t)) { - const char *name, *description; - struct entry entry; - pa_datum key, data; - - pa_zero(entry); - entry.version = ENTRY_VERSION; + struct entry *e; + const char *device, *description; - if (pa_tagstruct_gets(t, &name) < 0 || + if (pa_tagstruct_gets(t, &device) < 0 || pa_tagstruct_gets(t, &description) < 0) goto fail; - if (!name || !*name) + if (!device || !*device || !description || !*description) goto fail; - pa_strlcpy(entry.description, description, sizeof(entry.description)); + if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { + pa_datum key, data; - key.data = (char*) name; - key.size = strlen(name); + pa_strlcpy(e->description, description, sizeof(e->description)); - data.data = &entry; - data.size = sizeof(entry); + key.data = (char *) device; + key.size = strlen(device); - if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) - if (apply_immediately) - apply_entry(u, name, &entry); - } + data.data = e; + data.size = sizeof(*e); - trigger_save(u); + if (pa_database_set(u->database, &key, &data, FALSE) == 0) { + apply_entry(u, device, e); + + trigger_save(u); + } + else + pa_log_warn("Could not save device"); + + pa_xfree(e); + } + else + pa_log_warn("Could not rename device %s, no entry in database", device); break; } @@ -671,6 +661,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_xfree(prefix); } + else + pa_log_warn("Could not reorder device %s, no entry in database", device); + break; } diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index a634e212..bc6301ca 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -39,7 +39,7 @@ enum { SUBCOMMAND_TEST, SUBCOMMAND_READ, - SUBCOMMAND_WRITE, + SUBCOMMAND_RENAME, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, SUBCOMMAND_PREFER_DEVICE, @@ -183,12 +183,10 @@ pa_operation *pa_ext_device_manager_read( return o; } -pa_operation *pa_ext_device_manager_write( +pa_operation *pa_ext_device_manager_set_device_description( pa_context *c, - pa_update_mode_t mode, - const pa_ext_device_manager_info data[], - unsigned n, - int apply_immediately, + const char* device, + const char* description, pa_context_success_cb_t cb, void *userdata) { @@ -198,8 +196,8 @@ pa_operation *pa_ext_device_manager_write( pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET); - pa_assert(data); + pa_assert(device); + pa_assert(description); PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); @@ -210,35 +208,15 @@ pa_operation *pa_ext_device_manager_write( t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); pa_tagstruct_putu32(t, PA_INVALID_INDEX); pa_tagstruct_puts(t, "module-device-manager"); - pa_tagstruct_putu32(t, SUBCOMMAND_WRITE); - - pa_tagstruct_putu32(t, mode); - pa_tagstruct_put_boolean(t, apply_immediately); - - for (; n > 0; n--, data++) { - if (!data->name || !*data->name) - goto fail; + pa_tagstruct_putu32(t, SUBCOMMAND_RENAME); - pa_tagstruct_puts(t, data->name); - pa_tagstruct_puts(t, data->description); - } + pa_tagstruct_puts(t, device); + pa_tagstruct_puts(t, description); pa_pstream_send_tagstruct(c->pstream, t); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; - -fail: - if (o) { - pa_operation_cancel(o); - pa_operation_unref(o); - } - - if (t) - pa_tagstruct_free(t); - - pa_context_set_error(c, PA_ERR_INVALID); - return NULL; } pa_operation *pa_ext_device_manager_delete( diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index d6de1320..686c8d22 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -65,13 +65,11 @@ pa_operation *pa_ext_device_manager_read( pa_ext_device_manager_read_cb_t cb, void *userdata); -/** Store entries in the device database. \since 0.9.19 */ -pa_operation *pa_ext_device_manager_write( +/** Sets the description for a device. \since 0.9.19 */ +pa_operation *pa_ext_device_manager_set_device_description( pa_context *c, - pa_update_mode_t mode, - const pa_ext_device_manager_info data[], - unsigned n, - int apply_immediately, + const char* device, + const char* description, pa_context_success_cb_t cb, void *userdata); @@ -82,14 +80,14 @@ pa_operation *pa_ext_device_manager_delete( pa_context_success_cb_t cb, void *userdata); -/** Subscribe to changes in the device database. \since 0.9.19 */ +/** Enable the role-based device-priority routing mode. \since 0.9.19 */ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( pa_context *c, int enable, pa_context_success_cb_t cb, void *userdata); -/** Subscribe to changes in the device database. \since 0.9.19 */ +/** Prefer a given device in the priority list. \since 0.9.19 */ pa_operation *pa_ext_device_manager_prefer_device( pa_context *c, const char* role, @@ -97,7 +95,7 @@ pa_operation *pa_ext_device_manager_prefer_device( pa_context_success_cb_t cb, void *userdata); -/** Subscribe to changes in the device database. \since 0.9.19 */ +/** Defer a given device in the priority list. \since 0.9.19 */ pa_operation *pa_ext_device_manager_defer_device( pa_context *c, const char* role, -- cgit From a64f0f719ff4154cbd3d8f78dbb41c8e816eb672 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 13:57:10 +0100 Subject: device-manager: Let subscribed clients know when something changes. --- src/modules/module-device-manager.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 0a0c39dc..59aedd67 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -174,6 +174,22 @@ fail: } static void trigger_save(struct userdata *u) { + pa_native_connection *c; + uint32_t idx; + + for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) { + pa_tagstruct *t; + + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION); + pa_tagstruct_putu32(t, 0); + pa_tagstruct_putu32(t, u->module->index); + pa_tagstruct_puts(t, u->module->name); + pa_tagstruct_putu32(t, SUBCOMMAND_EVENT); + + pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t); + } + if (u->save_time_event) return; -- cgit From 180250096765ccbabfd4194b186c2d00110df70d Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 14:36:20 +0100 Subject: device-manager: When a new device is encountered, initialise the priority list to an appropriate value --- src/modules/module-device-manager.c | 60 ++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 59aedd67..f759e340 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -203,6 +203,60 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { return TRUE; } +static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) { + struct entry *old; + + pa_assert(u); + pa_assert(entry); + pa_assert(name); + pa_assert(prefix); + + if ((old = read_entry(u, name))) + *entry = *old; + else { + /* This is a new device, so make sure we write it's priority list correctly */ + uint32_t max_priority[NUM_ROLES]; + pa_datum key; + pa_bool_t done; + + pa_zero(max_priority); + done = !pa_database_first(u->database, &key, NULL); + + /* Find all existing devices with the same prefix so we calculate the current max priority for each role */ + while (!done) { + pa_datum next_key; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { + char *name2; + struct entry *e; + + name2 = pa_xstrndup(key.data, key.size); + + if ((e = read_entry(u, name2))) { + for (uint32_t i = 0; i < NUM_ROLES; ++i) { + max_priority[i] = PA_MAX(max_priority[i], e->priority[i]); + } + + pa_xfree(e); + } + + pa_xfree(name2); + } + pa_datum_free(&key); + key = next_key; + } + + /* Actually initialise our entry now we've calculated it */ + for (uint32_t i = 0; i < NUM_ROLES; ++i) { + entry->priority[i] = max_priority[i] + 1; + } + } + + return old; +} + static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; struct entry entry, *old = NULL; @@ -229,8 +283,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 name = pa_sprintf_malloc("sink:%s", sink->name); - if ((old = read_entry(u, name))) - entry = *old; + old = load_or_initialize_entry(u, &entry, name, "sink:"); pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); @@ -247,8 +300,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 name = pa_sprintf_malloc("source:%s", source->name); - if ((old = read_entry(u, name))) - entry = *old; + old = load_or_initialize_entry(u, &entry, name, "source:"); pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); } -- cgit From e589f38e227a53e1ed57491528c1290ddf8c1da7 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 14:39:41 +0100 Subject: device-manager: Fix the freeing of the datum on prefer/defer. Also fix a log typo --- src/modules/module-device-manager.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index f759e340..75059f74 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -366,10 +366,9 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data name = pa_sprintf_malloc("source:%s", new_data->name); if ((e = read_entry(u, name))) { - if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ - pa_log_info("Restoring description for sink %s.", new_data->name); + pa_log_info("Restoring description for source %s.", new_data->name); pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); } @@ -644,8 +643,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio goto fail; if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { - pa_datum key; - pa_datum data; + pa_datum key, data; pa_bool_t done; char* prefix; uint32_t priority; @@ -673,7 +671,6 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio struct entry *e2; name = pa_xstrndup(key.data, key.size); - pa_datum_free(&key); if ((e2 = read_entry(u, name))) { if (SUBCOMMAND_PREFER_DEVICE == command) { @@ -697,12 +694,14 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (pa_database_set(u->database, &key, &data, FALSE)) pa_log_warn("Could not save device"); } + pa_xfree(e2); } pa_xfree(name); } + pa_datum_free(&key); key = next_key; } -- cgit From faae33d808480a34f02bea6e66ba0da0523694cf Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 14:43:53 +0100 Subject: device-manager: debug and comments --- src/modules/module-device-manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 75059f74..e029c1d8 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -322,7 +322,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 data.data = &entry; data.size = sizeof(entry); - pa_log_info("Storing device description for %s.", name); + pa_log_info("Storing device %s.", name); pa_database_set(u->database, &key, &data, TRUE); @@ -569,6 +569,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio key.data = (char*) name; key.size = strlen(name); + /** @todo: Reindex the priorities */ pa_database_unset(u->database, &key); } -- cgit From ed8af7c8fd7bf845a243518357b6b73667d209c0 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 17:29:38 +0100 Subject: device-manager: Rough framework (slots etc.) for handling routing. This is incomplete, it just adds the slots in question and assigns noops to them. Some minor cleanup of types. Due to the priority of the hooks, it seems we can actually coexist with module-stream restore so the code to detect and unload it will be removed shortly. --- src/modules/module-device-manager.c | 206 +++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index e029c1d8..4c4e3f0b 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -58,11 +58,15 @@ PA_MODULE_AUTHOR("Colin Guthrie"); PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_USAGE("This module does not take any arguments"); +PA_MODULE_USAGE( + "on_hotplug= " + "on_rescue="); #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) static const char* const valid_modargs[] = { + "on_hotplug", + "on_rescue", NULL }; @@ -73,6 +77,12 @@ struct userdata { pa_hook_slot *sink_new_hook_slot, *source_new_hook_slot, + *sink_input_new_hook_slot, + *source_output_new_hook_slot, + *sink_put_hook_slot, + *source_put_hook_slot, + *sink_unlink_hook_slot, + *source_unlink_hook_slot, *connection_unlink_hook_slot; pa_time_event *save_time_event; pa_database *database; @@ -80,6 +90,8 @@ struct userdata { pa_native_protocol *protocol; pa_idxset *subscribed; + pa_bool_t on_hotplug; + pa_bool_t on_rescue; pa_bool_t role_device_priority_routing; pa_bool_t stream_restore_used; pa_bool_t checked_stream_restore; @@ -100,10 +112,12 @@ enum { ROLE_A11Y, }; +typedef uint32_t role_indexes_t[NUM_ROLES]; + struct entry { uint8_t version; char description[PA_NAME_MAX]; - uint32_t priority[NUM_ROLES]; + role_indexes_t priority; } PA_GCC_PACKED; enum { @@ -215,7 +229,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct *entry = *old; else { /* This is a new device, so make sure we write it's priority list correctly */ - uint32_t max_priority[NUM_ROLES]; + role_indexes_t max_priority; pa_datum key; pa_bool_t done; @@ -390,6 +404,138 @@ static char *get_name(const char *key, const char *prefix) { return t; } +static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + /*if (!(name = get_name(new_data->proplist, "sink-input"))) + return PA_HOOK_OK; + + if (new_data->sink) + pa_log_debug("Not restoring device for stream %s, because already set.", name); + else if ((e = read_entry(u, name))) { + + pa_xfree(e); + } + + pa_xfree(name);*/ + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + if (new_data->direct_on_input) + return PA_HOOK_OK; + + /*if (!(name = get_name(new_data->proplist, "source-output"))) + return PA_HOOK_OK; + + if (new_data->source) + pa_log_debug("Not restoring device for stream %s, because already set", name); + else if ((e = read_entry(u, name))) { + + pa_xfree(e); + } + + pa_xfree(name);*/ + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_hotplug); + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + /** @todo Ensure redo the routing based on the priorities */ + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_hotplug); + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + /** @todo Ensure redo the routing based on the priorities */ + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { + pa_sink_input *si; + uint32_t idx; + + pa_assert(c); + pa_assert(sink); + pa_assert(u); + pa_assert(u->on_rescue); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + /** @todo Ensure redo the routing based on the priorities */ + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { + pa_source_output *so; + uint32_t idx; + + pa_assert(c); + pa_assert(source); + pa_assert(u); + pa_assert(u->on_rescue); + + /* There's no point in doing anything if the core is shut down anyway */ + if (c->state == PA_CORE_SHUTDOWN) + return PA_HOOK_OK; + + if (!u->role_device_priority_routing) + return PA_HOOK_OK; + + /** @todo Ensure redo the routing based on the priorities */ + + return PA_HOOK_OK; +} + + static void apply_entry(struct userdata *u, const char *name, struct entry *e) { pa_sink *sink; pa_source *source; @@ -781,7 +927,10 @@ int pa__init(pa_module*m) { char *fname; pa_sink *sink; pa_source *source; + pa_sink_input *si; + pa_source_output *so; uint32_t idx; + pa_bool_t on_hotplug = TRUE, on_rescue = TRUE; pa_assert(m); @@ -790,9 +939,17 @@ int pa__init(pa_module*m) { goto fail; } + if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || + pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { + pa_log("on_hotplug= and on_rescue= expect boolean arguments"); + goto fail; + } + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; + u->on_hotplug = on_hotplug; + u->on_rescue = on_rescue; u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); u->protocol = pa_native_protocol_get(m->core); @@ -802,9 +959,27 @@ int pa__init(pa_module*m) { u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); + /* Used to handle device description management */ u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u); u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u); + /* The following slots are used to deal with routing */ + /* A little bit later than module-stream-restore, module-intended-roles */ + u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u); + u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u); + + if (on_hotplug) { + /* A little bit later than module-stream-restore, module-intended-roles */ + u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u); + u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u); + } + + if (on_rescue) { + /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */ + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u); + } + if (!(fname = pa_state_path("device-manager", TRUE))) goto fail; @@ -817,12 +992,18 @@ int pa__init(pa_module*m) { pa_log_info("Sucessfully opened database file '%s'.", fname); pa_xfree(fname); - for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx)) + PA_IDXSET_FOREACH(sink, m->core->sinks, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); - for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx)) + PA_IDXSET_FOREACH(source, m->core->sources, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); + PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); + + PA_IDXSET_FOREACH(so, m->core->source_outputs, idx) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u); + pa_modargs_free(ma); return 0; @@ -851,6 +1032,21 @@ void pa__done(pa_module*m) { if (u->source_new_hook_slot) pa_hook_slot_free(u->source_new_hook_slot); + if (u->sink_input_new_hook_slot) + pa_hook_slot_free(u->sink_input_new_hook_slot); + if (u->source_output_new_hook_slot) + pa_hook_slot_free(u->source_output_new_hook_slot); + + if (u->sink_put_hook_slot) + pa_hook_slot_free(u->sink_put_hook_slot); + if (u->source_put_hook_slot) + pa_hook_slot_free(u->source_put_hook_slot); + + if (u->sink_unlink_hook_slot) + pa_hook_slot_free(u->sink_unlink_hook_slot); + if (u->source_unlink_hook_slot) + pa_hook_slot_free(u->source_unlink_hook_slot); + if (u->save_time_event) u->core->mainloop->time_free(u->save_time_event); -- cgit From ca68105c8f9920fa18016b24e26ba9365d8f94b6 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 17:33:18 +0100 Subject: device-manager: Remove unneeded logic for checking for and (un)loading module-stream-restore. We can co-exist --- src/modules/module-device-manager.c | 35 ----------------------------------- 1 file changed, 35 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 4c4e3f0b..38b4c025 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -93,8 +93,6 @@ struct userdata { pa_bool_t on_hotplug; pa_bool_t on_rescue; pa_bool_t role_device_priority_routing; - pa_bool_t stream_restore_used; - pa_bool_t checked_stream_restore; }; #define ENTRY_VERSION 1 @@ -733,40 +731,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (pa_tagstruct_get_boolean(t, &enable) < 0) goto fail; - /* If this is the first run, check for stream restore module */ - if (!u->checked_stream_restore) { - u->checked_stream_restore = TRUE; - - for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { - if (strcmp(module->name, "module-stream-restore") == 0) { - pa_log_debug("Detected module-stream-restore is currently in use"); - u->stream_restore_used = TRUE; - sridx = module->index; - } - } - } - u->role_device_priority_routing = enable; - if (enable) { - if (u->stream_restore_used) { - if (PA_INVALID_INDEX == sridx) { - /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */ - for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) { - if (strcmp(module->name, "module-stream-restore") == 0) { - sridx = module->index; - } - } - } - if (PA_INVALID_INDEX != sridx) { - pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing"); - pa_module_unload_request_by_index(u->core, sridx, TRUE); - } - } - } else if (u->stream_restore_used) { - /* We want to reload module-stream-restore */ - if (!pa_module_load(u->core, "module-stream-restore", "")) - pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing"); - } break; } -- cgit From 678d8e963d7a9ce4ecd27f38ba49112b6b7663d4 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 17:34:17 +0100 Subject: device-manager: Add a function to get a list of the highest priority device indexes for each role. --- src/modules/module-device-manager.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 38b4c025..f4d00b55 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -402,6 +402,87 @@ static char *get_name(const char *key, const char *prefix) { return t; } +static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) { + role_indexes_t *indexes, highest_priority_available; + pa_datum key; + pa_bool_t done; + + pa_assert(u); + pa_assert(prefix); + + indexes = pa_xnew(role_indexes_t, 1); + for (uint32_t i = 0; i < NUM_ROLES; ++i) { + *indexes[i] = PA_INVALID_INDEX; + } + pa_zero(highest_priority_available); + + done = !pa_database_first(u->database, &key, NULL); + + /* Find all existing devices with the same prefix so we find the highest priority device for each role */ + while (!done) { + pa_datum next_key; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { + char *name; + struct entry *e; + + name = pa_xstrndup(key.data, key.size); + + if ((e = read_entry(u, name))) { + for (uint32_t i = 0; i < NUM_ROLES; ++i) { + if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) { + /* We've found a device with a higher priority than that we've currently got, + so see if it is currently available or not and update our list */ + uint32_t idx; + pa_bool_t found = FALSE; + char *device_name = get_name(name, prefix); + + if (strcmp(prefix, "sink:") == 0) { + pa_sink *sink; + + PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { + if (strcmp(sink->name, device_name) == 0) { + found = TRUE; + idx = sink->index; /* Is this needed? */ + break; + } + } + } else { + pa_source *source; + + PA_IDXSET_FOREACH(source, u->core->sources, idx) { + if (strcmp(source->name, device_name) == 0) { + found = TRUE; + idx = source->index; /* Is this needed? */ + break; + } + } + } + if (found) { + highest_priority_available[i] = e->priority[i]; + *indexes[i] = idx; + } + + pa_xfree(device_name); + } + } + + pa_xfree(e); + } + + pa_xfree(name); + } + + pa_datum_free(&key); + key = next_key; + } + + return indexes; +} + + static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { char *name; struct entry *e; -- cgit From 74c1c27eaac2e9d40902442f4b3671e12499494b Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 18:08:40 +0100 Subject: device-manager: Add routing functions that are triggered when sinks/soruces are added/removed. --- src/modules/module-device-manager.c | 197 +++++++++++++++++++++++++----------- 1 file changed, 136 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index f4d00b55..87588ae5 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -402,6 +402,30 @@ static char *get_name(const char *key, const char *prefix) { return t; } +static uint32_t get_role_index(const char* role) { + pa_assert(role); + + if (strcmp(role, "") == 0) + return ROLE_NONE; + if (strcmp(role, "video") == 0) + return ROLE_VIDEO; + if (strcmp(role, "music") == 0) + return ROLE_MUSIC; + if (strcmp(role, "game") == 0) + return ROLE_GAME; + if (strcmp(role, "event") == 0) + return ROLE_EVENT; + if (strcmp(role, "phone") == 0) + return ROLE_PHONE; + if (strcmp(role, "animation") == 0) + return ROLE_ANIMATION; + if (strcmp(role, "production") == 0) + return ROLE_PRODUCTION; + if (strcmp(role, "a11y") == 0) + return ROLE_A11Y; + return PA_INVALID_INDEX; +} + static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) { role_indexes_t *indexes, highest_priority_available; pa_datum key; @@ -484,9 +508,6 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { - char *name; - struct entry *e; - pa_assert(c); pa_assert(new_data); pa_assert(u); @@ -510,9 +531,6 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n } static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { - char *name; - struct entry *e; - pa_assert(c); pa_assert(new_data); pa_assert(u); @@ -538,80 +556,161 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; } -static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { +static pa_hook_result_t reroute_sinks(struct userdata *u) { pa_sink_input *si; + role_indexes_t *indexes; uint32_t idx; - pa_assert(c); - pa_assert(sink); pa_assert(u); - pa_assert(u->on_hotplug); if (!u->role_device_priority_routing) return PA_HOOK_OK; - /** @todo Ensure redo the routing based on the priorities */ + pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:")); + + PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { + const char *role; + uint32_t role_index, device_index; + pa_sink *sink; + + if (si->save_sink) + continue; + + /* Skip this if it is already in the process of being moved + * anyway */ + if (!si->sink) + continue; + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) + continue; + + if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX == role_index) + continue; + + device_index = *indexes[role_index]; + if (PA_INVALID_INDEX == device_index) + continue; + + if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index))) + continue; + + if (si->sink != sink) + pa_sink_input_move_to(si, sink, TRUE); + } + + pa_xfree(indexes); return PA_HOOK_OK; } -static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { +static pa_hook_result_t reroute_sources(struct userdata *u) { pa_source_output *so; + role_indexes_t *indexes; uint32_t idx; - pa_assert(c); - pa_assert(source); pa_assert(u); - pa_assert(u->on_hotplug); if (!u->role_device_priority_routing) return PA_HOOK_OK; - /** @todo Ensure redo the routing based on the priorities */ + pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:")); + + PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) { + const char *role; + uint32_t role_index, device_index; + pa_source *source; + + if (so->save_source) + continue; + + if (so->direct_on_input) + continue; + + /* Skip this if it is already in the process of being moved + * anyway */ + if (!so->source) + continue; + + /* It might happen that a stream and a source are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) + continue; + + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX == role_index) + continue; + + device_index = *indexes[role_index]; + if (PA_INVALID_INDEX == device_index) + continue; + + if (!(source = pa_idxset_get_by_index(u->core->sources, device_index))) + continue; + + if (so->source != source) + pa_source_output_move_to(so, source, TRUE); + } + + pa_xfree(indexes); return PA_HOOK_OK; } -static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { - pa_sink_input *si; - uint32_t idx; +static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) { + pa_assert(c); + pa_assert(u); + pa_assert(u->core == c); + pa_assert(u->on_hotplug); + + return reroute_sinks(u); +} + +static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { + pa_assert(c); + pa_assert(u); + pa_assert(u->core == c); + pa_assert(u->on_hotplug); + return reroute_sources(u); +} + +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) { pa_assert(c); - pa_assert(sink); pa_assert(u); + pa_assert(u->core == c); pa_assert(u->on_rescue); /* There's no point in doing anything if the core is shut down anyway */ if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - if (!u->role_device_priority_routing) - return PA_HOOK_OK; - - /** @todo Ensure redo the routing based on the priorities */ - - return PA_HOOK_OK; + return reroute_sinks(u); } -static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { - pa_source_output *so; - uint32_t idx; - +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { pa_assert(c); - pa_assert(source); pa_assert(u); + pa_assert(u->core == c); pa_assert(u->on_rescue); /* There's no point in doing anything if the core is shut down anyway */ if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - if (!u->role_device_priority_routing) - return PA_HOOK_OK; - - /** @todo Ensure redo the routing based on the priorities */ - - return PA_HOOK_OK; + return reroute_sinks(u); } @@ -655,30 +754,6 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { } -static uint32_t get_role_index(const char* role) { - pa_assert(role); - - if (strcmp(role, "") == 0) - return ROLE_NONE; - if (strcmp(role, "video") == 0) - return ROLE_VIDEO; - if (strcmp(role, "music") == 0) - return ROLE_MUSIC; - if (strcmp(role, "game") == 0) - return ROLE_GAME; - if (strcmp(role, "event") == 0) - return ROLE_EVENT; - if (strcmp(role, "phone") == 0) - return ROLE_PHONE; - if (strcmp(role, "animation") == 0) - return ROLE_ANIMATION; - if (strcmp(role, "production") == 0) - return ROLE_PRODUCTION; - if (strcmp(role, "a11y") == 0) - return ROLE_A11Y; - return PA_INVALID_INDEX; -} - #define EXT_VERSION 1 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { -- cgit From 1d04c353ea61f47961c2f25aff20b75c602e8c93 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 18:23:52 +0100 Subject: device-manager: Set the most appropriate sink/source when new streams are created --- src/modules/module-device-manager.c | 66 ++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 87588ae5..5c3c3958 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -515,17 +515,34 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n if (!u->role_device_priority_routing) return PA_HOOK_OK; - /*if (!(name = get_name(new_data->proplist, "sink-input"))) - return PA_HOOK_OK; - if (new_data->sink) - pa_log_debug("Not restoring device for stream %s, because already set.", name); - else if ((e = read_entry(u, name))) { + pa_log_debug("Not restoring device for stream, because already set."); + else { + const char *role; + uint32_t role_index; - pa_xfree(e); - } + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX != role_index) { + role_indexes_t *indexes; + uint32_t device_index; + + pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:")); + + device_index = *indexes[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_sink *sink; - pa_xfree(name);*/ + if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { + new_data->sink = sink; + new_data->save_sink = TRUE; + } + } + } + } return PA_HOOK_OK; } @@ -541,17 +558,34 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (new_data->direct_on_input) return PA_HOOK_OK; - /*if (!(name = get_name(new_data->proplist, "source-output"))) - return PA_HOOK_OK; - if (new_data->source) - pa_log_debug("Not restoring device for stream %s, because already set", name); - else if ((e = read_entry(u, name))) { + pa_log_debug("Not restoring device for stream, because already set"); + else { + const char *role; + uint32_t role_index; - pa_xfree(e); - } + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX != role_index) { + role_indexes_t *indexes; + uint32_t device_index; + + pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:")); + + device_index = *indexes[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_source *source; - pa_xfree(name);*/ + if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { + new_data->source = source; + new_data->save_source = TRUE; + } + } + } + } return PA_HOOK_OK; } -- cgit From 4fb9dafaf8cf844ecf981d243d35a2d57b780275 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 18:24:51 +0100 Subject: device-manager: Remove unused variables --- src/modules/module-device-manager.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 5c3c3958..ec981bba 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -914,9 +914,6 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: { pa_bool_t enable; - uint32_t sridx = PA_INVALID_INDEX; - uint32_t idx; - pa_module *module; if (pa_tagstruct_get_boolean(t, &enable) < 0) goto fail; -- cgit From e47f385b09b2bd6e4c5dd2ba4c1b93a213ec1b5c Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 18:31:10 +0100 Subject: device-manager: Allow the routing component to be turned on via a module argument as well as via protocol extn. --- src/modules/module-device-manager.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index ec981bba..fba4ebe6 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -59,12 +59,14 @@ PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE( + "do_routing= " "on_hotplug= " "on_rescue="); #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) static const char* const valid_modargs[] = { + "do_routing", "on_hotplug", "on_rescue", NULL @@ -92,7 +94,7 @@ struct userdata { pa_bool_t on_hotplug; pa_bool_t on_rescue; - pa_bool_t role_device_priority_routing; + pa_bool_t do_routing; }; #define ENTRY_VERSION 1 @@ -512,7 +514,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n pa_assert(new_data); pa_assert(u); - if (!u->role_device_priority_routing) + if (!u->do_routing) return PA_HOOK_OK; if (new_data->sink) @@ -552,7 +554,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou pa_assert(new_data); pa_assert(u); - if (!u->role_device_priority_routing) + if (!u->do_routing) return PA_HOOK_OK; if (new_data->direct_on_input) @@ -597,7 +599,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) { pa_assert(u); - if (!u->role_device_priority_routing) + if (!u->do_routing) return PA_HOOK_OK; pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:")); @@ -610,8 +612,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) { if (si->save_sink) continue; - /* Skip this if it is already in the process of being moved - * anyway */ + /* Skip this if it is already in the process of being moved anyway */ if (!si->sink) continue; @@ -652,7 +653,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) { pa_assert(u); - if (!u->role_device_priority_routing) + if (!u->do_routing) return PA_HOOK_OK; pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:")); @@ -668,8 +669,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) { if (so->direct_on_input) continue; - /* Skip this if it is already in the process of being moved - * anyway */ + /* Skip this if it is already in the process of being moved anyway */ if (!so->source) continue; @@ -918,7 +918,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (pa_tagstruct_get_boolean(t, &enable) < 0) goto fail; - u->role_device_priority_routing = enable; + u->do_routing = enable; break; } @@ -1082,7 +1082,7 @@ int pa__init(pa_module*m) { pa_sink_input *si; pa_source_output *so; uint32_t idx; - pa_bool_t on_hotplug = TRUE, on_rescue = TRUE; + pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE; pa_assert(m); @@ -1091,7 +1091,8 @@ int pa__init(pa_module*m) { goto fail; } - if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || + if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 || + pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { pa_log("on_hotplug= and on_rescue= expect boolean arguments"); goto fail; @@ -1100,6 +1101,7 @@ int pa__init(pa_module*m) { m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; + u->do_routing = do_routing; u->on_hotplug = on_hotplug; u->on_rescue = on_rescue; u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -- cgit From 9e447978eb9ed246762a07e52466384580834566 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 18:39:50 +0100 Subject: device-manager: Some efficiency and safety tweaks --- src/modules/module-device-manager.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index fba4ebe6..5abcc75e 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -431,7 +431,7 @@ static uint32_t get_role_index(const char* role) { static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) { role_indexes_t *indexes, highest_priority_available; pa_datum key; - pa_bool_t done; + pa_bool_t done, sink_mode; pa_assert(u); pa_assert(prefix); @@ -442,6 +442,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c } pa_zero(highest_priority_available); + sink_mode = (strcmp(prefix, "sink:") == 0); + done = !pa_database_first(u->database, &key, NULL); /* Find all existing devices with the same prefix so we find the highest priority device for each role */ @@ -465,7 +467,7 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c pa_bool_t found = FALSE; char *device_name = get_name(name, prefix); - if (strcmp(prefix, "sink:") == 0) { + if (sink_mode) { pa_sink *sink; PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { @@ -944,15 +946,18 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { pa_datum key, data; pa_bool_t done; - char* prefix; + char* prefix = NULL; uint32_t priority; pa_bool_t haschanged = FALSE; if (strncmp(device, "sink:", 5) == 0) prefix = pa_xstrdup("sink:"); - else + else if (strncmp(device, "source:", 7) == 0) prefix = pa_xstrdup("source:"); + if (!prefix) + goto fail; + priority = e->priority[role_index]; /* Now we need to load up all the other entries of this type and shuffle the priroities around */ -- cgit From 1e2d236b9984dbc25080b5a97add86043a524ea4 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 19:18:22 +0100 Subject: device-manager: Update exports --- src/map-file | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/map-file b/src/map-file index cec688fb..d7b341df 100644 --- a/src/map-file +++ b/src/map-file @@ -144,12 +144,15 @@ pa_cvolume_set_fade; pa_cvolume_set_position; pa_cvolume_snprint; pa_cvolume_valid; +pa_ext_device_manager_defer_device; pa_ext_device_manager_delete; +pa_ext_device_manager_enable_role_device_priority_routing; +pa_ext_device_manager_prefer_device; pa_ext_device_manager_read; +pa_ext_device_manager_set_device_description; pa_ext_device_manager_set_subscribe_cb; pa_ext_device_manager_subscribe; pa_ext_device_manager_test; -pa_ext_device_manager_write; pa_ext_stream_restore_delete; pa_ext_stream_restore_read; pa_ext_stream_restore_set_subscribe_cb; -- cgit From ce0b2bdc0718cfaec58d9809bd97a123a9fe07a4 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 19:30:31 +0100 Subject: device-manager: Fix the database write mode --- src/modules/module-device-manager.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 5abcc75e..3f4418ae 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -877,7 +877,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = e; data.size = sizeof(*e); - if (pa_database_set(u->database, &key, &data, FALSE) == 0) { + if (pa_database_set(u->database, &key, &data, TRUE) == 0) { apply_entry(u, device, e); trigger_save(u); @@ -995,7 +995,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = e2; data.size = sizeof(*e2); - if (pa_database_set(u->database, &key, &data, FALSE)) + if (pa_database_set(u->database, &key, &data, TRUE)) pa_log_warn("Could not save device"); } @@ -1022,7 +1022,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = e; data.size = sizeof(*e); - if (pa_database_set(u->database, &key, &data, FALSE)) + if (pa_database_set(u->database, &key, &data, TRUE)) pa_log_warn("Could not save device"); trigger_save(u); -- cgit From 0016b5e2655ec8e5a415d02bf3ccb97c641a60bb Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 20:34:52 +0100 Subject: device-manager: Keep a cache of the highest priority devices for each role. Rather than querying our database on every new stream, we keep a cache and only update it when a sink/source is added/removed. --- src/modules/module-device-manager.c | 99 +++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 3f4418ae..3f3f4a1f 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -72,6 +72,21 @@ static const char* const valid_modargs[] = { NULL }; +#define NUM_ROLES 9 +enum { + ROLE_NONE, + ROLE_VIDEO, + ROLE_MUSIC, + ROLE_GAME, + ROLE_EVENT, + ROLE_PHONE, + ROLE_ANIMATION, + ROLE_PRODUCTION, + ROLE_A11Y, +}; + +typedef uint32_t role_indexes_t[NUM_ROLES]; + struct userdata { pa_core *core; pa_module *module; @@ -95,24 +110,12 @@ struct userdata { pa_bool_t on_hotplug; pa_bool_t on_rescue; pa_bool_t do_routing; -}; -#define ENTRY_VERSION 1 - -#define NUM_ROLES 9 -enum { - ROLE_NONE, - ROLE_VIDEO, - ROLE_MUSIC, - ROLE_GAME, - ROLE_EVENT, - ROLE_PHONE, - ROLE_ANIMATION, - ROLE_PRODUCTION, - ROLE_A11Y, + role_indexes_t preferred_sinks; + role_indexes_t preferred_sources; }; -typedef uint32_t role_indexes_t[NUM_ROLES]; +#define ENTRY_VERSION 1 struct entry { uint8_t version; @@ -211,6 +214,7 @@ static void trigger_save(struct userdata *u) { } static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { + /** @todo: Compare the priority lists too */ if (strncmp(a->description, b->description, sizeof(a->description))) return FALSE; @@ -428,7 +432,7 @@ static uint32_t get_role_index(const char* role) { return PA_INVALID_INDEX; } -static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) { +static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) { role_indexes_t *indexes, highest_priority_available; pa_datum key; pa_bool_t done, sink_mode; @@ -436,14 +440,18 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c pa_assert(u); pa_assert(prefix); - indexes = pa_xnew(role_indexes_t, 1); + sink_mode = (strcmp(prefix, "sink:") == 0); + + if (sink_mode) + indexes = &u->preferred_sinks; + else + indexes = &u->preferred_sources; + for (uint32_t i = 0; i < NUM_ROLES; ++i) { *indexes[i] = PA_INVALID_INDEX; } pa_zero(highest_priority_available); - sink_mode = (strcmp(prefix, "sink:") == 0); - done = !pa_database_first(u->database, &key, NULL); /* Find all existing devices with the same prefix so we find the highest priority device for each role */ @@ -471,6 +479,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c pa_sink *sink; PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { + if ((pa_sink*) ignore_device == sink) + continue; if (strcmp(sink->name, device_name) == 0) { found = TRUE; idx = sink->index; /* Is this needed? */ @@ -481,6 +491,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c pa_source *source; PA_IDXSET_FOREACH(source, u->core->sources, idx) { + if ((pa_source*) ignore_device == source) + continue; if (strcmp(source->name, device_name) == 0) { found = TRUE; idx = source->index; /* Is this needed? */ @@ -506,8 +518,6 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c pa_datum_free(&key); key = next_key; } - - return indexes; } @@ -531,12 +541,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n role_index = get_role_index(role); if (PA_INVALID_INDEX != role_index) { - role_indexes_t *indexes; uint32_t device_index; - pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:")); - - device_index = *indexes[role_index]; + device_index = u->preferred_sinks[role_index]; if (PA_INVALID_INDEX != device_index) { pa_sink *sink; @@ -574,12 +581,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou role_index = get_role_index(role); if (PA_INVALID_INDEX != role_index) { - role_indexes_t *indexes; uint32_t device_index; - pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:")); - - device_index = *indexes[role_index]; + device_index = u->preferred_sources[role_index]; if (PA_INVALID_INDEX != device_index) { pa_source *source; @@ -594,9 +598,8 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; } -static pa_hook_result_t reroute_sinks(struct userdata *u) { +static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink) { pa_sink_input *si; - role_indexes_t *indexes; uint32_t idx; pa_assert(u); @@ -604,7 +607,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) { if (!u->do_routing) return PA_HOOK_OK; - pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:")); + update_highest_priority_device_indexes(u, "sink:", ignore_sink); PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { const char *role; @@ -632,7 +635,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) { if (PA_INVALID_INDEX == role_index) continue; - device_index = *indexes[role_index]; + device_index = u->preferred_sinks[role_index]; if (PA_INVALID_INDEX == device_index) continue; @@ -643,14 +646,11 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) { pa_sink_input_move_to(si, sink, TRUE); } - pa_xfree(indexes); - return PA_HOOK_OK; } -static pa_hook_result_t reroute_sources(struct userdata *u) { +static pa_hook_result_t reroute_sources(struct userdata *u, pa_source* ignore_source) { pa_source_output *so; - role_indexes_t *indexes; uint32_t idx; pa_assert(u); @@ -658,7 +658,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) { if (!u->do_routing) return PA_HOOK_OK; - pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:")); + update_highest_priority_device_indexes(u, "source:", ignore_source); PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) { const char *role; @@ -689,7 +689,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) { if (PA_INVALID_INDEX == role_index) continue; - device_index = *indexes[role_index]; + device_index = u->preferred_sources[role_index]; if (PA_INVALID_INDEX == device_index) continue; @@ -700,8 +700,6 @@ static pa_hook_result_t reroute_sources(struct userdata *u) { pa_source_output_move_to(so, source, TRUE); } - pa_xfree(indexes); - return PA_HOOK_OK; } @@ -711,7 +709,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink pa_assert(u->core == c); pa_assert(u->on_hotplug); - return reroute_sinks(u); + return reroute_sinks(u, NULL); } static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { @@ -720,11 +718,12 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so pa_assert(u->core == c); pa_assert(u->on_hotplug); - return reroute_sources(u); + return reroute_sources(u, NULL); } -static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) { +static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { pa_assert(c); + pa_assert(sink); pa_assert(u); pa_assert(u->core == c); pa_assert(u->on_rescue); @@ -733,11 +732,12 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_s if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - return reroute_sinks(u); + return reroute_sinks(u, sink); } -static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { +static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { pa_assert(c); + pa_assert(source); pa_assert(u); pa_assert(u->core == c); pa_assert(u->on_rescue); @@ -746,7 +746,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - return reroute_sinks(u); + return reroute_sources(u, source); } @@ -1151,12 +1151,17 @@ int pa__init(pa_module*m) { pa_log_info("Sucessfully opened database file '%s'.", fname); pa_xfree(fname); + /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */ PA_IDXSET_FOREACH(sink, m->core->sinks, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); PA_IDXSET_FOREACH(source, m->core->sources, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); + /* Update our caches (all available devices will be present in our database now */ + update_highest_priority_device_indexes(u, "sink:", NULL); + update_highest_priority_device_indexes(u, "source:", NULL); + PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); -- cgit From 25f75342d4e3e2885d751b9625a8eda4c0c1380c Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 20:48:58 +0100 Subject: device-manager: Reroute the streams on startup and update our cache on enable. --- src/modules/module-device-manager.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 3f3f4a1f..745961c3 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -920,7 +920,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (pa_tagstruct_get_boolean(t, &enable) < 0) goto fail; - u->do_routing = enable; + if ((u->do_routing = enable)) { + /* Update our caches */ + update_highest_priority_device_indexes(u, "sink:", NULL); + update_highest_priority_device_indexes(u, "source:", NULL); + } break; } @@ -1158,15 +1162,9 @@ int pa__init(pa_module*m) { PA_IDXSET_FOREACH(source, m->core->sources, idx) subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); - /* Update our caches (all available devices will be present in our database now */ - update_highest_priority_device_indexes(u, "sink:", NULL); - update_highest_priority_device_indexes(u, "source:", NULL); - - PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx) - subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u); - - PA_IDXSET_FOREACH(so, m->core->source_outputs, idx) - subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u); + /* Perform the routing (if it's enabled) which will update our priority list cache too */ + reroute_sinks(u, NULL); + reroute_sources(u, NULL); pa_modargs_free(ma); return 0; -- cgit From 8d0787c1d5a0552e52ad91c2a6f0630f96216a69 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 20:50:23 +0100 Subject: device-manager: More sensible names for internal functions --- src/modules/module-device-manager.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 745961c3..c2bc0417 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -598,7 +598,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; } -static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink) { +static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) { pa_sink_input *si; uint32_t idx; @@ -649,7 +649,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink) return PA_HOOK_OK; } -static pa_hook_result_t reroute_sources(struct userdata *u, pa_source* ignore_source) { +static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) { pa_source_output *so; uint32_t idx; @@ -709,7 +709,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink pa_assert(u->core == c); pa_assert(u->on_hotplug); - return reroute_sinks(u, NULL); + return route_sink_inputs(u, NULL); } static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { @@ -718,7 +718,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so pa_assert(u->core == c); pa_assert(u->on_hotplug); - return reroute_sources(u, NULL); + return route_source_outputs(u, NULL); } static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { @@ -732,7 +732,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - return reroute_sinks(u, sink); + return route_sink_inputs(u, sink); } static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { @@ -746,7 +746,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; - return reroute_sources(u, source); + return route_source_outputs(u, source); } @@ -1088,8 +1088,6 @@ int pa__init(pa_module*m) { char *fname; pa_sink *sink; pa_source *source; - pa_sink_input *si; - pa_source_output *so; uint32_t idx; pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE; @@ -1163,8 +1161,8 @@ int pa__init(pa_module*m) { subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); /* Perform the routing (if it's enabled) which will update our priority list cache too */ - reroute_sinks(u, NULL); - reroute_sources(u, NULL); + route_sink_inputs(u, NULL); + route_source_outputs(u, NULL); pa_modargs_free(ma); return 0; -- cgit From d3460e349893f6dc75eda1f03ee7690b0911dcc2 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 20:57:34 +0100 Subject: device-manager: Refactor the routing method to allow the routing of a single stream --- src/modules/module-device-manager.c | 145 ++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index c2bc0417..d672197c 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -598,6 +598,47 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; } + +static void route_sink_input(struct userdata *u, pa_sink_input *si) { + const char *role; + uint32_t role_index, device_index; + pa_sink *sink; + + pa_assert(u); + pa_assert(u->do_routing); + + if (si->save_sink) + return; + + /* Skip this if it is already in the process of being moved anyway */ + if (!si->sink) + return; + + /* It might happen that a stream and a sink are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) + return; + + if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX == role_index) + return; + + device_index = u->preferred_sinks[role_index]; + if (PA_INVALID_INDEX == device_index) + return; + + if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index))) + return; + + if (si->sink != sink) + pa_sink_input_move_to(si, sink, TRUE); +} + static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) { pa_sink_input *si; uint32_t idx; @@ -610,43 +651,53 @@ static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_si update_highest_priority_device_indexes(u, "sink:", ignore_sink); PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { - const char *role; - uint32_t role_index, device_index; - pa_sink *sink; + route_sink_input(u, si); + } - if (si->save_sink) - continue; + return PA_HOOK_OK; +} - /* Skip this if it is already in the process of being moved anyway */ - if (!si->sink) - continue; +static void route_source_output(struct userdata *u, pa_source_output *so) { + const char *role; + uint32_t role_index, device_index; + pa_source *source; - /* It might happen that a stream and a sink are set up at the - same time, in which case we want to make sure we don't - interfere with that */ - if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si))) - continue; + pa_assert(u); + pa_assert(u->do_routing); - if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); + if (so->save_source) + return; - if (PA_INVALID_INDEX == role_index) - continue; + if (so->direct_on_input) + return; - device_index = u->preferred_sinks[role_index]; - if (PA_INVALID_INDEX == device_index) - continue; + /* Skip this if it is already in the process of being moved anyway */ + if (!so->source) + return; - if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index))) - continue; + /* It might happen that a stream and a source are set up at the + same time, in which case we want to make sure we don't + interfere with that */ + if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) + return; - if (si->sink != sink) - pa_sink_input_move_to(si, sink, TRUE); - } + if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); - return PA_HOOK_OK; + if (PA_INVALID_INDEX == role_index) + return; + + device_index = u->preferred_sources[role_index]; + if (PA_INVALID_INDEX == device_index) + return; + + if (!(source = pa_idxset_get_by_index(u->core->sources, device_index))) + return; + + if (so->source != source) + pa_source_output_move_to(so, source, TRUE); } static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) { @@ -661,43 +712,7 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno update_highest_priority_device_indexes(u, "source:", ignore_source); PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) { - const char *role; - uint32_t role_index, device_index; - pa_source *source; - - if (so->save_source) - continue; - - if (so->direct_on_input) - continue; - - /* Skip this if it is already in the process of being moved anyway */ - if (!so->source) - continue; - - /* It might happen that a stream and a source are set up at the - same time, in which case we want to make sure we don't - interfere with that */ - if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so))) - continue; - - if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); - - if (PA_INVALID_INDEX == role_index) - continue; - - device_index = u->preferred_sources[role_index]; - if (PA_INVALID_INDEX == device_index) - continue; - - if (!(source = pa_idxset_get_by_index(u->core->sources, device_index))) - continue; - - if (so->source != source) - pa_source_output_move_to(so, source, TRUE); + route_source_output(u, so); } return PA_HOOK_OK; -- cgit From 1d43230006e639170fbdbfa8797f2ba783ae5ca2 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 20 Sep 2009 21:19:41 +0100 Subject: device-manager: Reroute streams when they change allowing the media.role to be updated mid-stream. We do not handle the _EVENT_NEW subscription here as the PA_CORE_HOOK_SINK_INPUT_NEW/PA_CORE_HOOK_SOURCE_OUTPUT_NEW hook should handle the initial routing. --- src/modules/module-device-manager.c | 455 +++++++++++++++++++----------------- 1 file changed, 243 insertions(+), 212 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index d672197c..72b473c2 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -221,6 +221,16 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { return TRUE; } +static char *get_name(const char *key, const char *prefix) { + char *t; + + if (strncmp(key, prefix, strlen(prefix))) + return NULL; + + t = pa_xstrdup(key + strlen(prefix)); + return t; +} + static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) { struct entry *old; @@ -275,139 +285,6 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct return old; } -static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { - struct userdata *u = userdata; - struct entry entry, *old = NULL; - char *name = NULL; - pa_datum key, data; - - pa_assert(c); - pa_assert(u); - - if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && - t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && - t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && - t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) - return; - - pa_zero(entry); - entry.version = ENTRY_VERSION; - - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { - pa_sink *sink; - - if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) - return; - - name = pa_sprintf_malloc("sink:%s", sink->name); - - old = load_or_initialize_entry(u, &entry, name, "sink:"); - - pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); - - } else { - pa_source *source; - - pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); - - if (!(source = pa_idxset_get_by_index(c->sources, idx))) - return; - - if (source->monitor_of) - return; - - name = pa_sprintf_malloc("source:%s", source->name); - - old = load_or_initialize_entry(u, &entry, name, "source:"); - - pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); - } - - if (old) { - - if (entries_equal(old, &entry)) { - pa_xfree(old); - pa_xfree(name); - return; - } - - pa_xfree(old); - } - - key.data = name; - key.size = strlen(name); - - data.data = &entry; - data.size = sizeof(entry); - - pa_log_info("Storing device %s.", name); - - pa_database_set(u->database, &key, &data, TRUE); - - pa_xfree(name); - - trigger_save(u); -} - -static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { - char *name; - struct entry *e; - - pa_assert(c); - pa_assert(new_data); - pa_assert(u); - - name = pa_sprintf_malloc("sink:%s", new_data->name); - - if ((e = read_entry(u, name))) { - if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { - pa_log_info("Restoring description for sink %s.", new_data->name); - pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); - } - - pa_xfree(e); - } - - pa_xfree(name); - - return PA_HOOK_OK; -} - -static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { - char *name; - struct entry *e; - - pa_assert(c); - pa_assert(new_data); - pa_assert(u); - - name = pa_sprintf_malloc("source:%s", new_data->name); - - if ((e = read_entry(u, name))) { - if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { - /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ - pa_log_info("Restoring description for source %s.", new_data->name); - pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); - } - - pa_xfree(e); - } - - pa_xfree(name); - - return PA_HOOK_OK; -} - -static char *get_name(const char *key, const char *prefix) { - char *t; - - if (strncmp(key, prefix, strlen(prefix))) - return NULL; - - t = pa_xstrdup(key + strlen(prefix)); - return t; -} - static uint32_t get_role_index(const char* role) { pa_assert(role); @@ -521,84 +398,6 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha } -static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { - pa_assert(c); - pa_assert(new_data); - pa_assert(u); - - if (!u->do_routing) - return PA_HOOK_OK; - - if (new_data->sink) - pa_log_debug("Not restoring device for stream, because already set."); - else { - const char *role; - uint32_t role_index; - - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); - - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; - - device_index = u->preferred_sinks[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_sink *sink; - - if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { - new_data->sink = sink; - new_data->save_sink = TRUE; - } - } - } - } - - return PA_HOOK_OK; -} - -static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { - pa_assert(c); - pa_assert(new_data); - pa_assert(u); - - if (!u->do_routing) - return PA_HOOK_OK; - - if (new_data->direct_on_input) - return PA_HOOK_OK; - - if (new_data->source) - pa_log_debug("Not restoring device for stream, because already set"); - else { - const char *role; - uint32_t role_index; - - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); - - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; - - device_index = u->preferred_sources[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_source *source; - - if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { - new_data->source = source; - new_data->save_source = TRUE; - } - } - } - } - - return PA_HOOK_OK; -} - - static void route_sink_input(struct userdata *u, pa_sink_input *si) { const char *role; uint32_t role_index, device_index; @@ -718,6 +517,238 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno return PA_HOOK_OK; } +static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct userdata *u = userdata; + struct entry entry, *old = NULL; + char *name = NULL; + pa_datum key, data; + + pa_assert(c); + pa_assert(u); + + if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) && + + /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/ + t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) && + /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/ + t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) + return; + + pa_zero(entry); + entry.version = ENTRY_VERSION; + + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { + pa_sink_input *si; + + if (!u->do_routing) + return; + if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) + return; + + /* The role may change mid-stream, so we reroute */ + route_sink_input(u, si); + + return; + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) { + pa_source_output *so; + + if (!u->do_routing) + return; + if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) + return; + + /* The role may change mid-stream, so we reroute */ + route_source_output(u, so); + + return; + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { + pa_sink *sink; + + if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) + return; + + name = pa_sprintf_malloc("sink:%s", sink->name); + + old = load_or_initialize_entry(u, &entry, name, "sink:"); + + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { + pa_source *source; + + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); + + if (!(source = pa_idxset_get_by_index(c->sources, idx))) + return; + + if (source->monitor_of) + return; + + name = pa_sprintf_malloc("source:%s", source->name); + + old = load_or_initialize_entry(u, &entry, name, "source:"); + + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + } + + pa_assert(name); + + if (old) { + + if (entries_equal(old, &entry)) { + pa_xfree(old); + pa_xfree(name); + return; + } + + pa_xfree(old); + } + + key.data = name; + key.size = strlen(name); + + data.data = &entry; + data.size = sizeof(entry); + + pa_log_info("Storing device %s.", name); + + pa_database_set(u->database, &key, &data, TRUE); + + pa_xfree(name); + + trigger_save(u); +} + +static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + name = pa_sprintf_malloc("sink:%s", new_data->name); + + if ((e = read_entry(u, name))) { + if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + pa_log_info("Restoring description for sink %s.", new_data->name); + pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + name = pa_sprintf_malloc("source:%s", new_data->name); + + if ((e = read_entry(u, name))) { + if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ + pa_log_info("Restoring description for source %s.", new_data->name); + pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); + } + + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!u->do_routing) + return PA_HOOK_OK; + + if (new_data->sink) + pa_log_debug("Not restoring device for stream, because already set."); + else { + const char *role; + uint32_t role_index; + + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; + + device_index = u->preferred_sinks[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_sink *sink; + + if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { + new_data->sink = sink; + new_data->save_sink = TRUE; + } + } + } + } + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { + pa_assert(c); + pa_assert(new_data); + pa_assert(u); + + if (!u->do_routing) + return PA_HOOK_OK; + + if (new_data->direct_on_input) + return PA_HOOK_OK; + + if (new_data->source) + pa_log_debug("Not restoring device for stream, because already set"); + else { + const char *role; + uint32_t role_index; + + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index(""); + else + role_index = get_role_index(role); + + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; + + device_index = u->preferred_sources[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_source *source; + + if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { + new_data->source = source; + new_data->save_source = TRUE; + } + } + } + } + + return PA_HOOK_OK; +} + + static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) { pa_assert(c); pa_assert(u); @@ -1133,7 +1164,7 @@ int pa__init(pa_module*m) { u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u); - u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u); /* Used to handle device description management */ u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u); -- cgit From 4dedba73a6183fbc9eb51d0577a6dfd402f1e135 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Mon, 21 Sep 2009 18:50:00 +0100 Subject: device-manager: Add a function to dump the database which we do whenever we save it (and on startup) --- src/modules/module-device-manager.c | 122 +++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 72b473c2..baff5603 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -55,7 +55,7 @@ #include "module-device-manager-symdef.h" PA_MODULE_AUTHOR("Colin Guthrie"); -PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present"); +PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_USAGE( @@ -64,6 +64,7 @@ PA_MODULE_USAGE( "on_rescue="); #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) +#define DUMP_DATABASE static const char* const valid_modargs[] = { "do_routing", @@ -135,20 +136,6 @@ enum { SUBCOMMAND_EVENT }; -static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { - struct userdata *u = userdata; - - pa_assert(a); - pa_assert(e); - pa_assert(u); - - pa_assert(e == u->save_time_event); - u->core->mainloop->time_free(u->save_time_event); - u->save_time_event = NULL; - - pa_database_sync(u->database); - pa_log_info("Synced."); -} static struct entry* read_entry(struct userdata *u, const char *name) { pa_datum key, data; @@ -190,6 +177,107 @@ fail: return NULL; } +#ifdef DUMP_DATABASE +static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) { + pa_assert(u); + pa_assert(human); + + if (sink_mode) { + pa_sink *s; + if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index]))) + pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name); + else + pa_log_debug(" %s No sink specified", human); + } else { + pa_source *s; + if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index]))) + pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name); + else + pa_log_debug(" %s No source specified", human); + } +} + +static void dump_database(struct userdata *u) { + pa_datum key; + pa_bool_t done; + + pa_assert(u); + + done = !pa_database_first(u->database, &key, NULL); + + pa_log_debug("Dumping database"); + while (!done) { + char *name; + struct entry *e; + pa_datum next_key; + + done = !pa_database_next(u->database, &key, &next_key, NULL); + + name = pa_xstrndup(key.data, key.size); + + if ((e = read_entry(u, name))) { + pa_log_debug(" Got entry: %s", name); + pa_log_debug(" Description: %s", e->description); + pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u", + e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]); + pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u", + e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]); + pa_xfree(e); + } + + pa_xfree(name); + + pa_datum_free(&key); + key = next_key; + } + + pa_log_debug(" Highest priority devices per-role:"); + + pa_log_debug(" Sinks:"); + dump_database_helper(u, ROLE_NONE, "None: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Video: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Music: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Game: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Event: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Phone: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Anim: ", TRUE); + dump_database_helper(u, ROLE_NONE, "Prodtn:", TRUE); + dump_database_helper(u, ROLE_NONE, "Ally: ", TRUE); + + pa_log_debug(" Sources:"); + dump_database_helper(u, ROLE_NONE, "None: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Video: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Music: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Game: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Event: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Phone: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Anim: ", FALSE); + dump_database_helper(u, ROLE_NONE, "Prodtn:", FALSE); + dump_database_helper(u, ROLE_NONE, "Ally: ", FALSE); + + pa_log_debug("Completed database dump"); +} +#endif + +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { + struct userdata *u = userdata; + + pa_assert(a); + pa_assert(e); + pa_assert(u); + + pa_assert(e == u->save_time_event); + u->core->mainloop->time_free(u->save_time_event); + u->save_time_event = NULL; + + pa_database_sync(u->database); + pa_log_info("Synced."); + +#ifdef DUMP_DATABASE + dump_database(u); +#endif +} + static void trigger_save(struct userdata *u) { pa_native_connection *c; uint32_t idx; @@ -1210,6 +1298,10 @@ int pa__init(pa_module*m) { route_sink_inputs(u, NULL); route_source_outputs(u, NULL); +#ifdef DUMP_DATABASE + dump_database(u); +#endif + pa_modargs_free(ma); return 0; -- cgit From 8b2cc4def30327a72d95365c671d2adcae1a77a8 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 26 Sep 2009 14:36:36 +0100 Subject: device-manager: Expose the priority lists in the protocol extension. Also leave space for 'icon' and 'available' details too, althought currently this info is dummy. --- src/modules/module-device-manager.c | 84 +++++++++++++++++++++---------------- src/pulse/ext-device-manager.c | 31 +++++++++++++- src/pulse/ext-device-manager.h | 9 ++++ 3 files changed, 86 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index baff5603..8a3a6f89 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -88,6 +88,18 @@ enum { typedef uint32_t role_indexes_t[NUM_ROLES]; +static const char* role_names[NUM_ROLES] = { + "none", + "video", + "music", + "game", + "event", + "phone", + "animation", + "production", + "a11y", +}; + struct userdata { pa_core *core; pa_module *module; @@ -234,26 +246,28 @@ static void dump_database(struct userdata *u) { pa_log_debug(" Highest priority devices per-role:"); pa_log_debug(" Sinks:"); - dump_database_helper(u, ROLE_NONE, "None: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Video: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Music: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Game: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Event: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Phone: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Anim: ", TRUE); - dump_database_helper(u, ROLE_NONE, "Prodtn:", TRUE); - dump_database_helper(u, ROLE_NONE, "Ally: ", TRUE); + for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { + char name[13]; + uint32_t len = PA_MAX(12u, strlen(role_names[role])); + for (int i = 0; i < 12; ++i) name[i] = ' '; + strncpy(name, role_names[role], len); + name[len] = ':'; + name[0] -= 32; + name[12] = '\0'; + dump_database_helper(u, role, name, TRUE); + } pa_log_debug(" Sources:"); - dump_database_helper(u, ROLE_NONE, "None: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Video: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Music: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Game: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Event: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Phone: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Anim: ", FALSE); - dump_database_helper(u, ROLE_NONE, "Prodtn:", FALSE); - dump_database_helper(u, ROLE_NONE, "Ally: ", FALSE); + for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { + char name[13]; + uint32_t len = PA_MAX(12u, strlen(role_names[role])); + for (int i = 0; i < 12; ++i) name[i] = ' '; + strncpy(name, role_names[role], len); + name[len] = ':'; + name[0] -= 32; + name[12] = '\0'; + dump_database_helper(u, role, name, FALSE); + } pa_log_debug("Completed database dump"); } @@ -378,22 +392,10 @@ static uint32_t get_role_index(const char* role) { if (strcmp(role, "") == 0) return ROLE_NONE; - if (strcmp(role, "video") == 0) - return ROLE_VIDEO; - if (strcmp(role, "music") == 0) - return ROLE_MUSIC; - if (strcmp(role, "game") == 0) - return ROLE_GAME; - if (strcmp(role, "event") == 0) - return ROLE_EVENT; - if (strcmp(role, "phone") == 0) - return ROLE_PHONE; - if (strcmp(role, "animation") == 0) - return ROLE_ANIMATION; - if (strcmp(role, "production") == 0) - return ROLE_PRODUCTION; - if (strcmp(role, "a11y") == 0) - return ROLE_A11Y; + for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) + if (strcmp(role, role_names[i]) == 0) + return i; + return PA_INVALID_INDEX; } @@ -974,10 +976,18 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_datum_free(&key); if ((e = read_entry(u, name))) { - pa_tagstruct_puts(reply, name); - pa_tagstruct_puts(reply, e->description); + pa_tagstruct_puts(reply, name); + pa_tagstruct_puts(reply, e->description); + pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */ + pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */ + pa_tagstruct_putu32(reply, NUM_ROLES); + + for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) { + pa_tagstruct_puts(reply, role_names[i]); + pa_tagstruct_putu32(reply, e->priority[i]); + } - pa_xfree(e); + pa_xfree(e); } pa_xfree(name); diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index bc6301ca..01e4594b 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -128,20 +129,48 @@ static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint3 while (!pa_tagstruct_eof(t)) { pa_ext_device_manager_info i; + pa_bool_t available; memset(&i, 0, sizeof(i)); + available = FALSE; if (pa_tagstruct_gets(t, &i.name) < 0 || - pa_tagstruct_gets(t, &i.description) < 0) { + pa_tagstruct_gets(t, &i.description) < 0 || + pa_tagstruct_gets(t, &i.icon) < 0 || + pa_tagstruct_get_boolean(t, &available) < 0 || + pa_tagstruct_getu32(t, &i.n_role_priorities) < 0) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } + i.available = (uint8_t)available; + + if (i.n_role_priorities > 0) { + uint32_t j; + i.role_priorities = pa_xnew0(pa_ext_device_manager_role_priority_info, i.n_role_priorities+1); + + for (j = 0; j < i.n_role_priorities; j++) { + + if (pa_tagstruct_gets(t, &i.role_priorities[j].role) < 0 || + pa_tagstruct_getu32(t, &i.role_priorities[j].priority) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + pa_xfree(i.role_priorities); + goto finish; + } + } + + /* Terminate with an extra NULL entry, just to make sure */ + i.role_priorities[j].role = NULL; + i.role_priorities[j].priority = 0; + } if (o->callback) { pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); } + + pa_xfree(i.role_priorities); } } diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index 686c8d22..bd52331c 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -33,11 +33,20 @@ PA_C_DECL_BEGIN +typedef struct pa_ext_device_manager_role_priority_info { + const char *role; + uint32_t priority; +} pa_ext_device_manager_role_priority_info; + /** Stores information about one device in the device database that is * maintained by module-device-manager. \since 0.9.19 */ typedef struct pa_ext_device_manager_info { const char *name; /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */ const char *description; /**< The description of the device when it was last seen, if applicable and saved */ + const char *icon; /**< The icon given to the device */ + uint8_t available; /**< Is the device currently available? */ + uint32_t n_role_priorities; /**< How many role priorities do we have? */ + pa_ext_device_manager_role_priority_info *role_priorities; /**< An array of role priority structures or NULL */ } pa_ext_device_manager_info; /** Callback prototype for pa_ext_device_manager_test(). \since 0.9.19 */ -- cgit From bbf67019df9a16fa27594054c0df222acd408b12 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 27 Sep 2009 03:11:44 +0100 Subject: device-manager: Save icon and report current availability over protocol. This also ensures we let clients know whenver a sink changes in some capacity. Also correct some debug code. --- src/modules/module-device-manager.c | 70 ++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 8a3a6f89..0764ad02 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -133,6 +133,7 @@ struct userdata { struct entry { uint8_t version; char description[PA_NAME_MAX]; + char icon[PA_NAME_MAX]; role_indexes_t priority; } PA_GCC_PACKED; @@ -248,24 +249,20 @@ static void dump_database(struct userdata *u) { pa_log_debug(" Sinks:"); for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { char name[13]; - uint32_t len = PA_MAX(12u, strlen(role_names[role])); - for (int i = 0; i < 12; ++i) name[i] = ' '; + uint32_t len = PA_MIN(12u, strlen(role_names[role])); strncpy(name, role_names[role], len); - name[len] = ':'; - name[0] -= 32; - name[12] = '\0'; + for (int i = len+1; i < 12; ++i) name[i] = ' '; + name[len] = ':'; name[0] -= 32; name[12] = '\0'; dump_database_helper(u, role, name, TRUE); } pa_log_debug(" Sources:"); for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { char name[13]; - uint32_t len = PA_MAX(12u, strlen(role_names[role])); - for (int i = 0; i < 12; ++i) name[i] = ' '; + uint32_t len = PA_MIN(12u, strlen(role_names[role])); strncpy(name, role_names[role], len); - name[len] = ':'; - name[0] -= 32; - name[12] = '\0'; + for (int i = len+1; i < 12; ++i) name[i] = ' '; + name[len] = ':'; name[0] -= 32; name[12] = '\0'; dump_database_helper(u, role, name, FALSE); } @@ -292,10 +289,13 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct #endif } -static void trigger_save(struct userdata *u) { +static void notify_subscribers(struct userdata *u) { + pa_native_connection *c; uint32_t idx; + pa_assert(u); + for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) { pa_tagstruct *t; @@ -308,6 +308,13 @@ static void trigger_save(struct userdata *u) { pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t); } +} + +static void trigger_save(struct userdata *u) { + + pa_assert(u); + + notify_subscribers(u); if (u->save_time_event) return; @@ -390,8 +397,6 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct static uint32_t get_role_index(const char* role) { pa_assert(role); - if (strcmp(role, "") == 0) - return ROLE_NONE; for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) if (strcmp(role, role_names[i]) == 0) return i; @@ -510,7 +515,7 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) { return; if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); + role_index = get_role_index("none"); else role_index = get_role_index(role); @@ -571,7 +576,7 @@ static void route_source_output(struct userdata *u, pa_source_output *so) { return; if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); + role_index = get_role_index("none"); else role_index = get_role_index(role); @@ -665,6 +670,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 old = load_or_initialize_entry(u, &entry, name, "sink:"); pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon)); } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { pa_source *source; @@ -682,6 +688,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 old = load_or_initialize_entry(u, &entry, name, "source:"); pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon)); } pa_assert(name); @@ -691,6 +698,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 if (entries_equal(old, &entry)) { pa_xfree(old); pa_xfree(name); + + /* Even if the entries are equal, the availability or otherwise + of the sink/source may have changed so we notify clients all the same */ + notify_subscribers(u); + return; } @@ -976,10 +988,34 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_datum_free(&key); if ((e = read_entry(u, name))) { + uint32_t idx; + char *devname; + pa_bool_t available = FALSE; + + if ((devname = get_name(name, "sink:"))) { + pa_sink* s; + PA_IDXSET_FOREACH(s, u->core->sinks, idx) { + if (strcmp(s->name, devname) == 0) { + available = TRUE; + break; + } + } + pa_xfree(devname); + } else if ((devname = get_name(name, "source:"))) { + pa_source* s; + PA_IDXSET_FOREACH(s, u->core->sources, idx) { + if (strcmp(s->name, devname) == 0) { + available = TRUE; + break; + } + } + pa_xfree(devname); + } + pa_tagstruct_puts(reply, name); pa_tagstruct_puts(reply, e->description); - pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */ - pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */ + pa_tagstruct_puts(reply, e->icon); + pa_tagstruct_put_boolean(reply, available); pa_tagstruct_putu32(reply, NUM_ROLES); for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) { -- cgit From 8977abdc840989975d79d041ffcaf48804d7a52b Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 27 Sep 2009 16:55:31 +0100 Subject: device-manager: Don't notify clients on every subscription (it happens all the time). Also compare the entries fully before saving. --- src/modules/module-device-manager.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 0764ad02..89fb4609 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -323,10 +323,18 @@ static void trigger_save(struct userdata *u) { } static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { - /** @todo: Compare the priority lists too */ - if (strncmp(a->description, b->description, sizeof(a->description))) + + pa_assert(a); + pa_assert(b); + + if (strncmp(a->description, b->description, sizeof(a->description)) + || strncmp(a->icon, b->icon, sizeof(a->icon))) return FALSE; + for (int i=0; i < NUM_ROLES; ++i) + if (a->priority[i] != b->priority[i]) + return FALSE; + return TRUE; } @@ -699,10 +707,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 pa_xfree(old); pa_xfree(name); - /* Even if the entries are equal, the availability or otherwise - of the sink/source may have changed so we notify clients all the same */ - notify_subscribers(u); - return; } @@ -857,6 +861,8 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink pa_assert(u->core == c); pa_assert(u->on_hotplug); + notify_subscribers(u); + return route_sink_inputs(u, NULL); } @@ -866,6 +872,8 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so pa_assert(u->core == c); pa_assert(u->on_hotplug); + notify_subscribers(u); + return route_source_outputs(u, NULL); } @@ -880,6 +888,8 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; + notify_subscribers(u); + return route_sink_inputs(u, sink); } @@ -894,6 +904,8 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc if (c->state == PA_CORE_SHUTDOWN) return PA_HOOK_OK; + notify_subscribers(u); + return route_source_outputs(u, source); } -- cgit From f9b2d6500b75445b66c83ad1d6700e042f2f8d2a Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 01:27:02 +0100 Subject: device-manager: Change the prefer/defer options to a single 'reorder' command. We put in the devices from the wire into a hashmap and then add all like type device in the database and then order them based on priority (with the ones specified on the wire always being in that order at the top of the list. --- src/map-file | 3 +- src/modules/module-device-manager.c | 222 +++++++++++++++++++++++------------- src/pulse/ext-device-manager.c | 53 ++------- src/pulse/ext-device-manager.h | 12 +- 4 files changed, 154 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/map-file b/src/map-file index d7b341df..3fc934c3 100644 --- a/src/map-file +++ b/src/map-file @@ -144,11 +144,10 @@ pa_cvolume_set_fade; pa_cvolume_set_position; pa_cvolume_snprint; pa_cvolume_valid; -pa_ext_device_manager_defer_device; pa_ext_device_manager_delete; pa_ext_device_manager_enable_role_device_priority_routing; -pa_ext_device_manager_prefer_device; pa_ext_device_manager_read; +pa_ext_device_manager_reorder_devices_for_role; pa_ext_device_manager_set_device_description; pa_ext_device_manager_set_subscribe_cb; pa_ext_device_manager_subscribe; diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 89fb4609..407d76dd 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -143,8 +143,7 @@ enum { SUBCOMMAND_RENAME, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, - SUBCOMMAND_PREFER_DEVICE, - SUBCOMMAND_DEFER_DEVICE, + SUBCOMMAND_REORDER, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -1121,115 +1120,174 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio break; } - case SUBCOMMAND_PREFER_DEVICE: - case SUBCOMMAND_DEFER_DEVICE: { + case SUBCOMMAND_REORDER: { - const char *role, *device; + const char *role; struct entry *e; - uint32_t role_index; + uint32_t role_index, n_devices; + pa_datum key, data; + pa_bool_t done, sink_mode = TRUE; + struct device_t { uint32_t prio; char *device; }; + struct device_t *device; + struct device_t **devices; + uint32_t i, idx, offset; + pa_hashmap *h; + pa_bool_t first; if (pa_tagstruct_gets(t, &role) < 0 || - pa_tagstruct_gets(t, &device) < 0) - goto fail; - - if (!role || !device || !*device) - goto fail; - - role_index = get_role_index(role); - if (PA_INVALID_INDEX == role_index) + pa_tagstruct_getu32(t, &n_devices) < 0 || + n_devices < 1) goto fail; - if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { - pa_datum key, data; - pa_bool_t done; - char* prefix = NULL; - uint32_t priority; - pa_bool_t haschanged = FALSE; - - if (strncmp(device, "sink:", 5) == 0) - prefix = pa_xstrdup("sink:"); - else if (strncmp(device, "source:", 7) == 0) - prefix = pa_xstrdup("source:"); + if (PA_INVALID_INDEX == (role_index = get_role_index(role))) + goto fail; + + /* Cycle through the devices given and make sure they exist */ + h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + first = TRUE; + idx = 0; + for (i = 0; i < n_devices; ++i) { + const char *s; + if (pa_tagstruct_gets(t, &s) < 0) { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - if (!prefix) + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Protocol error on reorder"); goto fail; + } - priority = e->priority[role_index]; + /* Ensure this is a valid entry */ + if (!(e = read_entry(u, s))) { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - /* Now we need to load up all the other entries of this type and shuffle the priroities around */ + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Client specified an unknown device in it's reorder list."); + goto fail; + } + pa_xfree(e); - done = !pa_database_first(u->database, &key, NULL); + if (first) { + first = FALSE; + sink_mode = (0 == strncmp("sink:", s, 5)); + } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) + || (!sink_mode && 0 != strncmp("source:", s, 7))) + { + while ((device = pa_hashmap_steal_first(h))) { + pa_xfree(device->device); + pa_xfree(device); + } - while (!done && !haschanged) { - pa_datum next_key; + pa_hashmap_free(h, NULL, NULL); + pa_log_error("Attempted to reorder mixed devices (sinks and sources)"); + goto fail; + } - done = !pa_database_next(u->database, &key, &next_key, NULL); + /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */ + device = pa_xnew(struct device_t, 1); + device->device = pa_xstrdup(s); + if (pa_hashmap_put(h, device->device, device) == 0) { + device->prio = idx; + idx++; + } else { + pa_xfree(device->device); + pa_xfree(device); + } + } - /* Only read devices with the right prefix */ - if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { - char *name; - struct entry *e2; + /* Now cycle through our list and add all the devices. + This has the effect of addign in any in our DB, + not specified in the device list (and thus will be + tacked on at the end) */ + offset = idx; + done = !pa_database_first(u->database, &key, NULL); - name = pa_xstrndup(key.data, key.size); + while (!done && idx < 256) { + pa_datum next_key; - if ((e2 = read_entry(u, name))) { - if (SUBCOMMAND_PREFER_DEVICE == command) { - /* PREFER */ - if (e2->priority[role_index] == (priority - 1)) { - e2->priority[role_index]++; - haschanged = TRUE; - } - } else { - /* DEFER */ - if (e2->priority[role_index] == (priority + 1)) { - e2->priority[role_index]--; - haschanged = TRUE; - } - } + done = !pa_database_next(u->database, &key, &next_key, NULL); - if (haschanged) { - data.data = e2; - data.size = sizeof(*e2); + device = pa_xnew(struct device_t, 1); + device->device = pa_xstrndup(key.data, key.size); + if ((sink_mode && 0 == strncmp("sink:", device->device, 5)) + || (!sink_mode && 0 == strncmp("source:", device->device, 7))) { + + /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */ + if (pa_hashmap_put(h, device->device, device) == 0 + && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) { + /* We add offset on to the existing priorirty so that when we order, the + existing entries are always lower priority than the new ones. */ + device->prio = (offset + e->priority[role_index]); + pa_xfree(e); + } + else { + pa_xfree(device->device); + pa_xfree(device); + } + } else { + pa_xfree(device->device); + pa_xfree(device); + } - if (pa_database_set(u->database, &key, &data, TRUE)) - pa_log_warn("Could not save device"); - } + pa_datum_free(&key); - pa_xfree(e2); - } + key = next_key; + } - pa_xfree(name); + /* Now we put all the entries in a simple list for sorting it. */ + n_devices = pa_hashmap_size(h); + devices = pa_xnew(struct device_t *, n_devices); + idx = 0; + while ((device = pa_hashmap_steal_first(h))) { + devices[idx++] = device; + } + pa_hashmap_free(h, NULL, NULL); + + /* Simple bubble sort */ + for (i = 0; i < n_devices; ++i) { + for (uint32_t j = i; j < n_devices; ++j) { + if (devices[i]->prio > devices[j]->prio) { + struct device_t *tmp; + tmp = devices[i]; + devices[i] = devices[j]; + devices[j] = tmp; } - - pa_datum_free(&key); - key = next_key; } + } - /* Now write out our actual entry */ - if (haschanged) { - if (SUBCOMMAND_PREFER_DEVICE == command) - e->priority[role_index]--; - else - e->priority[role_index]++; + /* Go through in order and write the new entry and cleanup our own list */ + i = 0; idx = 1; + first = TRUE; + for (i = 0; i < n_devices; ++i) { + if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) { + if (e->priority[role_index] != idx) { + e->priority[role_index] = idx; - key.data = (char *) device; - key.size = strlen(device); + key.data = (char *) devices[i]->device; + key.size = strlen(devices[i]->device); - data.data = e; - data.size = sizeof(*e); + data.data = e; + data.size = sizeof(*e); - if (pa_database_set(u->database, &key, &data, TRUE)) - pa_log_warn("Could not save device"); + if (pa_database_set(u->database, &key, &data, TRUE) == 0) { + first = FALSE; + idx++; + } + } - trigger_save(u); + pa_xfree(e); } - - pa_xfree(e); - - pa_xfree(prefix); + pa_xfree(devices[i]->device); + pa_xfree(devices[i]); } - else - pa_log_warn("Could not reorder device %s, no entry in database", device); + + if (!first) + trigger_save(u); break; } diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c index 01e4594b..138ed838 100644 --- a/src/pulse/ext-device-manager.c +++ b/src/pulse/ext-device-manager.c @@ -43,8 +43,7 @@ enum { SUBCOMMAND_RENAME, SUBCOMMAND_DELETE, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, - SUBCOMMAND_PREFER_DEVICE, - SUBCOMMAND_DEFER_DEVICE, + SUBCOMMAND_REORDER, SUBCOMMAND_SUBSCRIBE, SUBCOMMAND_EVENT }; @@ -330,14 +329,14 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( return o; } -pa_operation *pa_ext_device_manager_prefer_device( +pa_operation *pa_ext_device_manager_reorder_devices_for_role( pa_context *c, const char* role, - const char* device, + const char** devices, pa_context_success_cb_t cb, void *userdata) { - uint32_t tag; + uint32_t tag, i; pa_operation *o = NULL; pa_tagstruct *t = NULL; @@ -349,52 +348,22 @@ pa_operation *pa_ext_device_manager_prefer_device( PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); pa_assert(role); - pa_assert(device); + pa_assert(devices); o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); pa_tagstruct_putu32(t, PA_INVALID_INDEX); pa_tagstruct_puts(t, "module-device-manager"); - pa_tagstruct_putu32(t, SUBCOMMAND_PREFER_DEVICE); + pa_tagstruct_putu32(t, SUBCOMMAND_REORDER); pa_tagstruct_puts(t, role); - pa_tagstruct_puts(t, device); - - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; -} - -pa_operation *pa_ext_device_manager_defer_device( - pa_context *c, - const char* role, - const char* device, - pa_context_success_cb_t cb, - void *userdata) { - - uint32_t tag; - pa_operation *o = NULL; - pa_tagstruct *t = NULL; - - pa_assert(c); - pa_assert(PA_REFCNT_VALUE(c) >= 1); - - PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED); - pa_assert(role); - pa_assert(device); + i = 0; while (devices[i]) i++; + pa_tagstruct_putu32(t, i); - o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); - - t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag); - pa_tagstruct_putu32(t, PA_INVALID_INDEX); - pa_tagstruct_puts(t, "module-device-manager"); - pa_tagstruct_putu32(t, SUBCOMMAND_DEFER_DEVICE); - pa_tagstruct_puts(t, role); - pa_tagstruct_puts(t, device); + i = 0; + while (devices[i]) + pa_tagstruct_puts(t, devices[i++]); pa_pstream_send_tagstruct(c->pstream, t); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h index bd52331c..13538f0c 100644 --- a/src/pulse/ext-device-manager.h +++ b/src/pulse/ext-device-manager.h @@ -97,18 +97,10 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing( void *userdata); /** Prefer a given device in the priority list. \since 0.9.19 */ -pa_operation *pa_ext_device_manager_prefer_device( +pa_operation *pa_ext_device_manager_reorder_devices_for_role( pa_context *c, const char* role, - const char* device, - pa_context_success_cb_t cb, - void *userdata); - -/** Defer a given device in the priority list. \since 0.9.19 */ -pa_operation *pa_ext_device_manager_defer_device( - pa_context *c, - const char* role, - const char* device, + const char** devices, pa_context_success_cb_t cb, void *userdata); -- cgit From 7633bb8a290f1748d8953ce7558b80aa77084140 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 09:07:42 +0100 Subject: device-manager: Add extra debug messages --- src/modules/module-device-manager.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 407d76dd..00389b64 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -1132,6 +1132,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio struct device_t **devices; uint32_t i, idx, offset; pa_hashmap *h; + void *state; pa_bool_t first; if (pa_tagstruct_gets(t, &role) < 0 || @@ -1200,6 +1201,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio } } + pa_log_debug("Hashmap contents (received from client)"); + PA_HASHMAP_FOREACH(device, h, state) { + pa_log_debug(" - %s (%d)", device->device, device->prio); + } + /* Now cycle through our list and add all the devices. This has the effect of addign in any in our DB, not specified in the device list (and thus will be @@ -1239,6 +1245,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio key = next_key; } + pa_log_debug("Hashmap contents (combined with database)"); + PA_HASHMAP_FOREACH(device, h, state) { + pa_log_debug(" - %s (%d)", device->device, device->prio); + } + /* Now we put all the entries in a simple list for sorting it. */ n_devices = pa_hashmap_size(h); devices = pa_xnew(struct device_t *, n_devices); @@ -1260,8 +1271,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio } } + pa_log_debug("Sorted device list"); + for (i = 0; i < n_devices; ++i) { + pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio); + } + /* Go through in order and write the new entry and cleanup our own list */ - i = 0; idx = 1; + idx = 1; first = TRUE; for (i = 0; i < n_devices; ++i) { if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) { @@ -1274,7 +1290,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = e; data.size = sizeof(*e); + pa_log_debug("Attempting to write record: %d. %s", e->priority[role_index], e->description); if (pa_database_set(u->database, &key, &data, TRUE) == 0) { + pa_log_debug("..... write successfull"); first = FALSE; idx++; } -- cgit From b8a6436d4be0c78405b21dbf6ee192a527713388 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 20:13:38 +0100 Subject: device-manager: Fix the writing of the database when priority doesn't change. --- src/modules/module-device-manager.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 00389b64..6b815bd3 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -1281,7 +1281,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio first = TRUE; for (i = 0; i < n_devices; ++i) { if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) { - if (e->priority[role_index] != idx) { + if (e->priority[role_index] == idx) + idx++; + else { e->priority[role_index] = idx; key.data = (char *) devices[i]->device; -- cgit From 3a20cf0b9f69e5f9b1012cc46b0918dd5f8173dd Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 21:30:24 +0100 Subject: device-manager: Misc fixes. * Fix a s/sink/source/ copy paste issue when dumping the database. * Only show priority list when routing is enabled (as the list is not updated if not) * Fix a memory access issue when finding the highest priority sinks/sources * key name->device name efficiency fix. * Silence noisy debug on reorder - it seems to work :) * Reroute after reordering. * Initialise preferred lists to PA_INVALID_INDEX --- src/modules/module-device-manager.c | 84 +++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 6b815bd3..5b5b6ca6 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -202,7 +202,7 @@ static void dump_database_helper(struct userdata *u, uint32_t role_index, const pa_log_debug(" %s No sink specified", human); } else { pa_source *s; - if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index]))) + if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index]))) pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name); else pa_log_debug(" %s No source specified", human); @@ -243,26 +243,28 @@ static void dump_database(struct userdata *u) { key = next_key; } - pa_log_debug(" Highest priority devices per-role:"); - - pa_log_debug(" Sinks:"); - for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { - char name[13]; - uint32_t len = PA_MIN(12u, strlen(role_names[role])); - strncpy(name, role_names[role], len); - for (int i = len+1; i < 12; ++i) name[i] = ' '; - name[len] = ':'; name[0] -= 32; name[12] = '\0'; - dump_database_helper(u, role, name, TRUE); - } + if (u->do_routing) { + pa_log_debug(" Highest priority devices per-role:"); + + pa_log_debug(" Sinks:"); + for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { + char name[13]; + uint32_t len = PA_MIN(12u, strlen(role_names[role])); + strncpy(name, role_names[role], len); + for (int i = len+1; i < 12; ++i) name[i] = ' '; + name[len] = ':'; name[0] -= 32; name[12] = '\0'; + dump_database_helper(u, role, name, TRUE); + } - pa_log_debug(" Sources:"); - for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { - char name[13]; - uint32_t len = PA_MIN(12u, strlen(role_names[role])); - strncpy(name, role_names[role], len); - for (int i = len+1; i < 12; ++i) name[i] = ' '; - name[len] = ':'; name[0] -= 32; name[12] = '\0'; - dump_database_helper(u, role, name, FALSE); + pa_log_debug(" Sources:"); + for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { + char name[13]; + uint32_t len = PA_MIN(12u, strlen(role_names[role])); + strncpy(name, role_names[role], len); + for (int i = len+1; i < 12; ++i) name[i] = ' '; + name[len] = ':'; name[0] -= 32; name[12] = '\0'; + dump_database_helper(u, role, name, FALSE); + } } pa_log_debug("Completed database dump"); @@ -427,7 +429,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha indexes = &u->preferred_sources; for (uint32_t i = 0; i < NUM_ROLES; ++i) { - *indexes[i] = PA_INVALID_INDEX; + (*indexes)[i] = PA_INVALID_INDEX; } pa_zero(highest_priority_available); @@ -440,19 +442,19 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha done = !pa_database_next(u->database, &key, &next_key, NULL); if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { - char *name; + char *name, *device_name; struct entry *e; name = pa_xstrndup(key.data, key.size); + device_name = get_name(name, prefix); - if ((e = read_entry(u, name))) { + if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) { for (uint32_t i = 0; i < NUM_ROLES; ++i) { - if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) { + if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) { /* We've found a device with a higher priority than that we've currently got, so see if it is currently available or not and update our list */ uint32_t idx; pa_bool_t found = FALSE; - char *device_name = get_name(name, prefix); if (sink_mode) { pa_sink *sink; @@ -481,10 +483,9 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha } if (found) { highest_priority_available[i] = e->priority[i]; - *indexes[i] = idx; + (*indexes)[i] = idx; } - pa_xfree(device_name); } } @@ -492,6 +493,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha } pa_xfree(name); + pa_xfree(device_name); } pa_datum_free(&key); @@ -1132,7 +1134,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio struct device_t **devices; uint32_t i, idx, offset; pa_hashmap *h; - void *state; + /*void *state;*/ pa_bool_t first; if (pa_tagstruct_gets(t, &role) < 0 || @@ -1201,10 +1203,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio } } - pa_log_debug("Hashmap contents (received from client)"); + /*pa_log_debug("Hashmap contents (received from client)"); PA_HASHMAP_FOREACH(device, h, state) { pa_log_debug(" - %s (%d)", device->device, device->prio); - } + }*/ /* Now cycle through our list and add all the devices. This has the effect of addign in any in our DB, @@ -1245,10 +1247,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio key = next_key; } - pa_log_debug("Hashmap contents (combined with database)"); + /*pa_log_debug("Hashmap contents (combined with database)"); PA_HASHMAP_FOREACH(device, h, state) { pa_log_debug(" - %s (%d)", device->device, device->prio); - } + }*/ /* Now we put all the entries in a simple list for sorting it. */ n_devices = pa_hashmap_size(h); @@ -1271,10 +1273,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio } } - pa_log_debug("Sorted device list"); + /*pa_log_debug("Sorted device list"); for (i = 0; i < n_devices; ++i) { pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio); - } + }*/ /* Go through in order and write the new entry and cleanup our own list */ idx = 1; @@ -1292,9 +1294,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio data.data = e; data.size = sizeof(*e); - pa_log_debug("Attempting to write record: %d. %s", e->priority[role_index], e->description); if (pa_database_set(u->database, &key, &data, TRUE) == 0) { - pa_log_debug("..... write successfull"); first = FALSE; idx++; } @@ -1306,9 +1306,15 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_xfree(devices[i]); } - if (!first) + if (!first) { trigger_save(u); + if (sink_mode) + route_sink_inputs(u, NULL); + else + route_source_outputs(u, NULL); + } + break; } @@ -1431,6 +1437,10 @@ int pa__init(pa_module*m) { subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); /* Perform the routing (if it's enabled) which will update our priority list cache too */ + for (uint32_t i = 0; i < NUM_ROLES; ++i) { + u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX; + } + route_sink_inputs(u, NULL); route_source_outputs(u, NULL); -- cgit From 20eedb24163884612c0fe81846ccf2983f336b7c Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 22:12:16 +0100 Subject: device-manager: Misc fixes to co-exist with other stream management/routing modules. * Do not read or set the save_sink/save_source flags. This seems to be for module-stream-restore only... * Even if a sink is already set by an earlier module, still move it to the sink we dictate. --- src/modules/module-device-manager.c | 70 ++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 5b5b6ca6..86ea95d0 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -510,9 +510,6 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) { pa_assert(u); pa_assert(u->do_routing); - if (si->save_sink) - return; - /* Skip this if it is already in the process of being moved anyway */ if (!si->sink) return; @@ -568,9 +565,6 @@ static void route_source_output(struct userdata *u, pa_source_output *so) { pa_assert(u); pa_assert(u->do_routing); - if (so->save_source) - return; - if (so->direct_on_input) return; @@ -779,6 +773,9 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data } static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { + const char *role; + uint32_t role_index; + pa_assert(c); pa_assert(new_data); pa_assert(u); @@ -787,27 +784,22 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n return PA_HOOK_OK; if (new_data->sink) - pa_log_debug("Not restoring device for stream, because already set."); - else { - const char *role; - uint32_t role_index; + pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way..."); - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index("none"); + else + role_index = get_role_index(role); - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; - device_index = u->preferred_sinks[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_sink *sink; + device_index = u->preferred_sinks[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_sink *sink; - if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { - new_data->sink = sink; - new_data->save_sink = TRUE; - } + if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { + new_data->sink = sink; } } } @@ -816,6 +808,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n } static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { + const char *role; + uint32_t role_index; + pa_assert(c); pa_assert(new_data); pa_assert(u); @@ -827,27 +822,22 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; if (new_data->source) - pa_log_debug("Not restoring device for stream, because already set"); - else { - const char *role; - uint32_t role_index; + pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way..."); - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index(""); - else - role_index = get_role_index(role); + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index("none"); + else + role_index = get_role_index(role); - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; - device_index = u->preferred_sources[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_source *source; + device_index = u->preferred_sources[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_source *source; - if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { - new_data->source = source; - new_data->save_source = TRUE; - } + if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { + new_data->source = source; } } } -- cgit From 42e28ce31c583ae9d431d2f1dd033d82cc63eb36 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Thu, 1 Oct 2009 22:33:22 +0100 Subject: device-manager: Add some scripts that are only run under KDE to load/initialise module-device-manager with routing turned on. --- src/Makefile.am | 14 +++++++++++--- src/daemon/pulseaudio-kde.desktop.in | 11 +++++++++++ src/daemon/start-pulseaudio-kde.in | 30 ++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/daemon/pulseaudio-kde.desktop.in create mode 100755 src/daemon/start-pulseaudio-kde.in (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 8ed74823..3f874f79 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -117,9 +117,11 @@ EXTRA_DIST = \ depmod.py \ daemon/esdcompat.in \ daemon/start-pulseaudio-x11.in \ + daemon/start-pulseaudio-kde.in \ utils/padsp \ modules/module-defs.h.m4 \ daemon/pulseaudio.desktop.in \ + daemon/pulseaudio-kde.desktop.in \ map-file \ daemon/pulseaudio-system.conf \ modules/alsa/mixer/profile-sets/default.conf \ @@ -153,7 +155,8 @@ dbuspolicy_DATA = \ if HAVE_X11 xdgautostart_in_files = \ - daemon/pulseaudio.desktop.in + daemon/pulseaudio.desktop.in \ + daemon/pulseaudio-kde.desktop.in endif xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop) @INTLTOOL_DESKTOP_RULE@ @@ -215,7 +218,7 @@ if HAVE_AVAHI bin_PROGRAMS += pabrowse endif -bin_SCRIPTS = esdcompat start-pulseaudio-x11 +bin_SCRIPTS = esdcompat start-pulseaudio-x11 start-pulseaudio-kde pacat_SOURCES = utils/pacat.c pacat_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS) @@ -1711,7 +1714,7 @@ module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) # Some minor stuff # ################################### -CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 daemon/pulseaudio.desktop +CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 start-pulseaudio-kde daemon/pulseaudio.desktop daemon/pulseaudio-kde.desktop esdcompat: daemon/esdcompat.in Makefile sed -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ @@ -1724,6 +1727,11 @@ start-pulseaudio-x11: daemon/start-pulseaudio-x11.in Makefile -e 's,@PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@ chmod +x start-pulseaudio-x11 +start-pulseaudio-kde: daemon/start-pulseaudio-kde.in Makefile + sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' \ + -e 's,@PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@ + chmod +x start-pulseaudio-kde + client.conf: pulse/client.conf.in Makefile sed -e 's,@PA_BINARY\@,$(PA_BINARY),g' < $< > $@ diff --git a/src/daemon/pulseaudio-kde.desktop.in b/src/daemon/pulseaudio-kde.desktop.in new file mode 100644 index 00000000..06846421 --- /dev/null +++ b/src/daemon/pulseaudio-kde.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +Encoding=UTF-8 +_Name=PulseAudio Sound System KDE Routing Policy +_Comment=Start the PulseAudio Sound System with KDE Routing Policy +Exec=start-pulseaudio-kde +Terminal=false +Type=Application +Categories= +GenericName= +OnlyShowIn=KDE; diff --git a/src/daemon/start-pulseaudio-kde.in b/src/daemon/start-pulseaudio-kde.in new file mode 100755 index 00000000..a79a50b5 --- /dev/null +++ b/src/daemon/start-pulseaudio-kde.in @@ -0,0 +1,30 @@ +#!/bin/sh + +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. + +set -e + +[ -z "$PULSE_SERVER" ] + +@PA_BINARY@ --start "$@" + +if [ x"$DISPLAY" != x ] ; then + + @PACTL_BINARY@ load-module module-module-device-manager "do_routing=1" > /dev/null + +fi -- cgit From 50db81c8603e1274b1bfe8836d134bf8ad6fb0a1 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 2 Oct 2009 00:49:50 +0100 Subject: device-manager: Fix typo in module loading script. --- src/daemon/start-pulseaudio-kde.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/daemon/start-pulseaudio-kde.in b/src/daemon/start-pulseaudio-kde.in index a79a50b5..c319e7dd 100755 --- a/src/daemon/start-pulseaudio-kde.in +++ b/src/daemon/start-pulseaudio-kde.in @@ -25,6 +25,6 @@ set -e if [ x"$DISPLAY" != x ] ; then - @PACTL_BINARY@ load-module module-module-device-manager "do_routing=1" > /dev/null + @PACTL_BINARY@ load-module module-device-manager "do_routing=1" > /dev/null fi -- cgit From a434f4c6af1ba421f8c689fa8bf9e040268205b2 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 26 Aug 2009 01:15:49 -0700 Subject: module-equalizer-sink: resyncing with head and fix invalid writes * pa_log->debug for default equalizer notification * partially fixed infinite rewind bug * set max_request to window_size first iteration * swap order inside ROUND_UP calls * resync pa_sink_input_new changes * change pa_sample_clamp parameters to be correct to fix invalid writes * reenable proper reset logic + proper request size --- src/modules/module-equalizer-sink.c | 213 ++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index f634d5ea..f556be78 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -99,10 +99,10 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ - size_t latency;//Really just R but made into it's own variable //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; + size_t input_buffer_max; //message float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -198,6 +198,34 @@ static int is_monotonic(const uint32_t *xs,size_t length){ return 1; } +//ensure's memory allocated is a multiple of v_size +//and aligned +static void * alloc(size_t x,size_t s){ + size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); + float *t; + pa_assert(f >= x*s); + t = fftwf_malloc(f); + memset(t, 0, f); + return t; +} + +static void alloc_input_buffers(struct userdata *u, size_t min_buffer_length){ + if(min_buffer_length <= u->input_buffer_max){ + return; + } + pa_assert(min_buffer_length >= u->window_size); + for(size_t c = 0; c < u->channels; ++c){ + float *tmp = alloc(min_buffer_length, sizeof(float)); + if(u->input[c]){ + if(!u->first_iteration){ + memcpy(tmp, u->input[c], u->overlap_size * sizeof(float)); + } + free(u->input[c]); + } + u->input[c] = tmp; + } + u->input_buffer_max = min_buffer_length; +} /* Called from I/O thread context */ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { @@ -359,7 +387,7 @@ static void dsp_logic( //preseve the needed input for the next window's overlap memmove(src, src + u->R, - u->overlap_size * sizeof(float) + (u->samples_gathered - u->R) * sizeof(float) ); } @@ -470,71 +498,64 @@ typedef union float_vector { //} static void process_samples(struct userdata *u, pa_memchunk *tchunk){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); float *dst; unsigned a_i; float *H, X; - pa_assert(u->samples_gathered >= u->R); + size_t iterations, offset; + pa_assert(u->samples_gathered >= u->window_size); + iterations = (u->samples_gathered - u->overlap_size) / u->R; tchunk->index = 0; - tchunk->length = u->R * fs; + tchunk->length = iterations * u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); - dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - - for(size_t c=0;c < u->channels; c++) { - a_i = pa_aupdate_read_begin(u->a_H[c]); - X = u->Xs[c][a_i]; - H = u->Hs[c][a_i]; - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - X, - H, - u->W, - u->output_window, - u - ); - pa_aupdate_read_end(u->a_H[c]); - if(u->first_iteration){ - /* The windowing function will make the audio ramped in, as a cheap fix we can - * undo the windowing (for non-zero window values) - */ - for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + dst = ((float*) pa_memblock_acquire(tchunk->memblock)); + for(size_t iter = 0; iter < iterations; ++iter){ + offset = iter * u->R * fs; + for(size_t c = 0;c < u->channels; c++) { + a_i = pa_aupdate_read_begin(u->a_H[c]); + X = u->Xs[c][a_i]; + H = u->Hs[c][a_i]; + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + X, + H, + u->W, + u->output_window, + u + ); + pa_aupdate_read_end(u->a_H[c]); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0; i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + } } + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, (uint8_t *) (dst + c) + offset, fs, u->work_buffer, sizeof(float), u->R); } - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); + if(u->first_iteration){ + u->first_iteration = FALSE; + } + u->samples_gathered -= u->R; } pa_memblock_release(tchunk->memblock); - u->samples_gathered -= u->R; -} - -static void initialize_buffer(struct userdata *u, pa_memchunk *in){ - size_t fs = pa_frame_size(&u->sink->sample_spec); - size_t samples = in->length / fs; - float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - pa_assert_se(u->samples_gathered + samples <= u->window_size); - for(size_t c = 0; c < u->channels; c++) { - //buffer with an offset after the overlap from previous - //iterations - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); - } - u->samples_gathered += samples; - pa_memblock_release(in->memblock); } static void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - pa_assert_se(samples <= u->window_size - u->samples_gathered); + pa_assert(u->samples_gathered + samples <= u->input_buffer_max); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size + u->input[c] + u->samples_gathered + samples <= u->input[c] + u->input_buffer_max ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered += samples; pa_memblock_release(in->memblock); @@ -543,7 +564,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){ /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; - size_t fs; + size_t fs, target_samples; struct timeval start, end; pa_memchunk tchunk; pa_sink_input_assert_ref(i); @@ -551,6 +572,16 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert(u->sink); fs = pa_frame_size(&(u->sink->sample_spec)); + target_samples = PA_ROUND_UP(nbytes / fs, u->R); + if(u->first_iteration){ + //allocate request_size + target_samples = PA_MAX(target_samples, u->window_size); + }else{ + //allocate request_size + overlap + target_samples += u->overlap_size; + alloc_input_buffers(u, target_samples); + } + alloc_input_buffers(u, target_samples); chunk->memblock = NULL; /* Hmm, process any rewind request that might be queued up */ @@ -559,18 +590,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); pa_rtclock_get(&start); do{ - size_t input_remaining = u->window_size - u->samples_gathered; + size_t input_remaining = target_samples - u->samples_gathered; pa_assert(input_remaining > 0); - //collect samples - - //buffer = &u->conv_buffer; - //buffer->length = input_remaining*fs; - //buffer->index = 0; - //pa_memblock_ref(buffer->memblock); - //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ - pa_sink_render(u->sink, input_remaining*fs, &tchunk); - //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); + //pa_sink_render(u->sink, input_remaining * fs, &tchunk); + pa_sink_render_full(u->sink, input_remaining * fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -581,15 +605,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ //pa_rtclock_get(start); - if(u->first_iteration){ - initialize_buffer(u, &tchunk); - }else{ - input_buffer(u, &tchunk); - } + input_buffer(u, &tchunk); //pa_rtclock_get(&end); //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered < u->window_size); + }while(u->samples_gathered < target_samples); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); @@ -605,9 +626,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); - if(u->first_iteration){ - u->first_iteration = FALSE; - } return 0; } @@ -632,11 +650,17 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) { } static void reset_filter(struct userdata *u){ + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t max_request; u->samples_gathered = 0; - for(size_t i = 0;i < u->channels; ++i){ + for(size_t i = 0; i < u->channels; ++i){ memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); } u->first_iteration = TRUE; + //set buffer size to max request, no overlap copy + max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R); + max_request = PA_MAX(max_request, u->window_size); + pa_sink_set_max_request_within_thread(u->sink, max_request * fs); } /* Called from I/O thread context */ @@ -658,11 +682,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - //pa_sample_spec *ss = &u->sink->sample_spec; //invalidate the output q pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); - //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); - //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log("Resetting filter"); reset_filter(u); } @@ -689,11 +710,11 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - + //if(u->first_iteration){ + // return; + //} fs = pa_frame_size(&(u->sink->sample_spec)); - //pa_sink_set_max_request_within_thread(u->sink, nbytes); - //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); - pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(nbytes / fs, u->R) * fs); } /* Called from I/O thread context */ @@ -703,8 +724,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -733,7 +752,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { /* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct userdata *u; - size_t fs; + size_t fs, max_request; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -741,19 +760,15 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); - fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs)); - - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); - //TODO: setting this guy minimizes drop outs but doesn't get rid - //of them completely, figure out why - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //TODO: this guy causes dropouts constantly+rewinds, it's unusable - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + fs = pa_frame_size(&u->sink->sample_spec); + //set buffer size to max request, no overlap copy + max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R); + max_request = PA_MAX(max_request, u->window_size); + pa_sink_set_max_request_within_thread(u->sink, max_request * fs); + pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); pa_sink_attach_within_thread(u->sink); if(u->set_default){ - pa_log("Setting default sink to %s", u->sink->name); + pa_log_debug("Setting default sink to %s", u->sink->name); pa_namereg_set_default_sink(u->module->core, u->sink); } } @@ -1005,17 +1020,6 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_set_asyncmsgq(u->sink, NULL); } -//ensure's memory allocated is a multiple of v_size -//and aligned -static void * alloc(size_t x,size_t s){ - size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); - float *t; - pa_assert(f >= x*s); - t = fftwf_malloc(f); - memset(t, 0, f); - return t; -} - int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -1068,15 +1072,15 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; + u->input_buffer_max = 0; u->a_H = pa_xnew0(pa_aupdate *, u->channels); - u->latency = u->window_size - u->R; u->Xs = pa_xnew0(float *, u->channels); u->Hs = pa_xnew0(float **, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->Xs[c] = pa_xnew0(float, 2); u->Hs[c] = pa_xnew0(float *, 2); for(size_t i = 0; i < 2; ++i){ - u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float)); + u->Hs[c][i] = alloc(FILTER_SIZE, sizeof(float)); } } u->W = alloc(u->window_size, sizeof(float)); @@ -1086,8 +1090,7 @@ int pa__init(pa_module*m) { u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->a_H[c] = pa_aupdate_new(); - u->input[c] = alloc(u->window_size, sizeof(float)); - memset(u->input[c], 0, (u->window_size)*sizeof(float)); + u->input[c] = NULL; u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } @@ -1151,7 +1154,7 @@ int pa__init(pa_module*m) { pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) -- cgit From 97056d2a0e4364fad86f4be5e26625592aef3d55 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 18 Sep 2009 05:48:01 -0700 Subject: module-equalizer-sink: *added client initiated sync support for filter state *added note of possible unstable behavior with next-power-of-2 sample rate calculation --- src/modules/module-equalizer-sink.c | 59 ++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index f556be78..74d7497b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -837,13 +837,10 @@ static void unpack(char *str, size_t length, char ***strs, size_t *len){ size_t l = *((uint16_t *)(str+offset)); size_t e = PA_MIN(offset + l, length) - offset; offset = PA_MIN(offset + sizeof(uint16_t), length); - if(e > 0){ - (*strs)[i] = pa_xnew(char, e + 1); - memcpy((*strs)[i], strs + offset, e); - (*strs)[i][e] = '\0'; - }else{ - (*strs)[i]=NULL; - } + (*strs)[i] = pa_xnew(char, e + 1); + memcpy((*strs)[i], str + offset, e); + (*strs)[i][e] = '\0'; + offset += l; } } static void save_profile(struct userdata *u, size_t channel, char *name){ @@ -905,7 +902,7 @@ static void save_state(struct userdata *u){ data.data = state; data.size = filter_state_size + packed_length; //thread safety for 0.9.17? - pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); pa_assert_se(database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); @@ -936,12 +933,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){ float *profile = (float *) value.data; a_i = pa_aupdate_write_begin(u->a_H[channel]); u->Xs[channel][a_i] = profile[0]; - memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); + memcpy(u->Hs[channel][a_i], profile + 1, FILTER_SIZE * sizeof(float)); fix_filter(u->Hs[channel][a_i], u->fft_size); pa_aupdate_write_end(u->a_H[channel]); - if(u->base_profiles[channel]){ - pa_xfree(u->base_profiles[channel]); - } + pa_xfree(u->base_profiles[channel]); u->base_profiles[channel] = pa_xstrdup(name); }else{ return "incompatible size"; @@ -964,6 +959,7 @@ static void load_state(struct userdata *u){ database = pa_database_open(dbname, FALSE); pa_xfree(dbname); if(!database){ + pa_log("No resume state"); return; } @@ -985,14 +981,14 @@ static void load_state(struct userdata *u){ unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); n_profs = PA_MIN(n_profs, u->channels); for(size_t c = 0; c < n_profs; ++c){ - if(u->base_profiles[c]){ - pa_xfree(u->base_profiles[c]); - } + pa_xfree(u->base_profiles[c]); u->base_profiles[c] = names[c]; } pa_xfree(names); } pa_datum_free(&value); + }else{ + pa_log("resume state exists but is wrong size!"); } pa_database_close(database); } @@ -1066,7 +1062,7 @@ int pa__init(pa_module*m) { pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); u->channels = ss.channels; - u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); + u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));//probably unstable near corner cases of powers of 2 pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; u->R = (u->window_size + 1) / 2; @@ -1102,6 +1098,9 @@ int pa__init(pa_module*m) { u->first_iteration = TRUE; u->base_profiles = pa_xnew0(char *, u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->base_profiles[c] = pa_xstrdup("default"); + } /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -1233,9 +1232,7 @@ void pa__done(pa_module*m) { dbus_done(u); for(size_t c = 0; c < u->channels; ++c){ - if(u->base_profiles[c]){ - pa_xfree(u->base_profiles[c]); - } + pa_xfree(u->base_profiles[c]); } pa_xfree(u->base_profiles); @@ -1308,6 +1305,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, @@ -1374,6 +1372,7 @@ enum equalizer_method_index { EQUALIZER_METHOD_LOAD_PROFILE, EQUALIZER_METHOD_SET_FILTER, EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_SAVE_STATE, EQUALIZER_METHOD_GET_PROFILE_NAME, EQUALIZER_METHOD_MAX }; @@ -1455,6 +1454,11 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_SAVE_STATE]{ + .method_name="SaveState", + .arguments=NULL, + .n_arguments=0, + .receive_cb=equalizer_handle_save_state}, [EQUALIZER_METHOD_GET_PROFILE_NAME]{ .method_name="BaseProfile", .arguments=base_profile_name_args, @@ -2037,6 +2041,16 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_message_unref(signal); } +void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + save_state(u); + pa_dbus_send_empty_reply(conn, msg); +} + void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u = (struct userdata *) _u; DBusError error; @@ -2060,11 +2074,8 @@ void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, v return; } r_channel = channel == u->channels ? 0 : channel; - if(u->base_profiles[r_channel]){ - pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); - }else{ - pa_dbus_send_empty_reply(conn, msg); - } + pa_assert(u->base_profiles[r_channel]); + pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); } void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ -- cgit From e8952001699317952318918835252f63c7a899f9 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 19 Sep 2009 11:15:05 -0700 Subject: module-equalizer-sink: disable active profile name restoration as something in pack/unpack is funky and I don't have time for a proper fix --- src/modules/module-equalizer-sink.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 74d7497b..3a28b497 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -978,13 +978,13 @@ static void load_state(struct userdata *u){ memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); pa_aupdate_write_end(u->a_H[c]); } - unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); - n_profs = PA_MIN(n_profs, u->channels); - for(size_t c = 0; c < n_profs; ++c){ - pa_xfree(u->base_profiles[c]); - u->base_profiles[c] = names[c]; - } - pa_xfree(names); + //unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); + //n_profs = PA_MIN(n_profs, u->channels); + //for(size_t c = 0; c < n_profs; ++c){ + // pa_xfree(u->base_profiles[c]); + // u->base_profiles[c] = names[c]; + //} + //pa_xfree(names); } pa_datum_free(&value); }else{ -- cgit From 6468dcf9d13c49299eba8c76ee41f4ff5fdba80f Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 2 Oct 2009 19:12:10 +0100 Subject: device-manager: No need to check the version after calling read_entry() --- src/modules/module-device-manager.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 86ea95d0..587def41 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -448,7 +448,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha name = pa_xstrndup(key.data, key.size); device_name = get_name(name, prefix); - if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) { + if ((e = read_entry(u, name))) { for (uint32_t i = 0; i < NUM_ROLES; ++i) { if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) { /* We've found a device with a higher priority than that we've currently got, @@ -1049,7 +1049,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if (!device || !*device || !description || !*description) goto fail; - if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) { + if ((e = read_entry(u, device))) { pa_datum key, data; pa_strlcpy(e->description, description, sizeof(e->description)); @@ -1217,7 +1217,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */ if (pa_hashmap_put(h, device->device, device) == 0 - && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) { + && (e = read_entry(u, device->device))) { /* We add offset on to the existing priorirty so that when we order, the existing entries are always lower priority than the new ones. */ device->prio = (offset + e->priority[role_index]); @@ -1272,7 +1272,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio idx = 1; first = TRUE; for (i = 0; i < n_devices; ++i) { - if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) { + if ((e = read_entry(u, devices[i]->device))) { if (e->priority[role_index] == idx) idx++; else { -- cgit From fdbb5500634bbe3481fb60ce373ad4f9c2063f75 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 2 Oct 2009 21:01:19 +0100 Subject: device-manager: Keep track as to whether or not the user specifically renamed the device. If the user has not (via our protocol extension) renamed a device, but it happens to now have a different name (e.g. module-combine automatically updating the description for us or udev-db getting better etc.) then make sure we update our cache with this updated version. If the user has set a name, enforce it's use, even if the description is updated by some other means (e.g. the user manually editing the proplist or another module doing it for them). --- src/modules/module-device-manager.c | 49 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 587def41..776a687a 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -133,6 +133,7 @@ struct userdata { struct entry { uint8_t version; char description[PA_NAME_MAX]; + pa_bool_t user_set_description; char icon[PA_NAME_MAX]; role_indexes_t priority; } PA_GCC_PACKED; @@ -181,6 +182,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } + if (!memchr(e->icon, 0, sizeof(e->icon))) { + pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name); + goto fail; + } + return e; fail: @@ -329,6 +335,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { pa_assert(b); if (strncmp(a->description, b->description, sizeof(a->description)) + || a->user_set_description != b->user_set_description || strncmp(a->icon, b->icon, sizeof(a->icon))) return FALSE; @@ -398,6 +405,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct for (uint32_t i = 0; i < NUM_ROLES; ++i) { entry->priority[i] = max_priority[i] + 1; } + entry->user_set_description = FALSE; } return old; @@ -672,7 +680,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 old = load_or_initialize_entry(u, &entry, name, "sink:"); - pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + if (!entry.user_set_description) + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) { + /* Warning: If two modules fight over the description, this could cause an infinite loop. + by changing the description here, we retrigger this subscription callback. The only thing stopping us from + looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage + the description, this will fail... */ + pa_sink_set_description(sink, entry.description); + } + pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon)); } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { @@ -690,7 +707,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 old = load_or_initialize_entry(u, &entry, name, "source:"); - pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + if (!entry.user_set_description) + pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)); + else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) { + /* Warning: If two modules fight over the description, this could cause an infinite loop. + by changing the description here, we retrigger this subscription callback. The only thing stopping us from + looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage + the description, this will fail... */ + pa_source_set_description(source, entry.description); + } + pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon)); } @@ -716,11 +742,12 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 pa_log_info("Storing device %s.", name); - pa_database_set(u->database, &key, &data, TRUE); + if (pa_database_set(u->database, &key, &data, TRUE) == 0) + trigger_save(u); + else + pa_log_warn("Could not save device");; pa_xfree(name); - - trigger_save(u); } static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { @@ -734,7 +761,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new name = pa_sprintf_malloc("sink:%s", new_data->name); if ((e = read_entry(u, name))) { - if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { pa_log_info("Restoring description for sink %s.", new_data->name); pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); } @@ -758,7 +785,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data name = pa_sprintf_malloc("source:%s", new_data->name); if ((e = read_entry(u, name))) { - if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { + if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) { /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ pa_log_info("Restoring description for source %s.", new_data->name); pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); @@ -911,13 +938,16 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { pa_assert(name); pa_assert(e); + if (!e->user_set_description) + return; + if ((n = get_name(name, "sink:"))) { for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) { if (!pa_streq(sink->name, n)) { continue; } - pa_log_info("Setting description for sink %s.", sink->name); + pa_log_info("Setting description for sink %s to '%s'", sink->name, e->description); pa_sink_set_description(sink, e->description); } pa_xfree(n); @@ -933,7 +963,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { continue; } - pa_log_info("Setting description for source %s.", source->name); + pa_log_info("Setting description for source %s to '%s'", source->name, e->description); pa_source_set_description(source, e->description); } pa_xfree(n); @@ -1053,6 +1083,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio pa_datum key, data; pa_strlcpy(e->description, description, sizeof(e->description)); + e->user_set_description = TRUE; key.data = (char *) device; key.size = strlen(device); -- cgit From cc31d7c35a509d398494e13a9020513ddf8b667f Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 2 Oct 2009 21:04:03 +0100 Subject: device-manager: Make use of PA_IDXSET_FOREACH when applying entries. --- src/modules/module-device-manager.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 776a687a..8d17bb06 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -929,8 +929,6 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc static void apply_entry(struct userdata *u, const char *name, struct entry *e) { - pa_sink *sink; - pa_source *source; uint32_t idx; char *n; @@ -942,29 +940,31 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { return; if ((n = get_name(name, "sink:"))) { - for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) { - if (!pa_streq(sink->name, n)) { + pa_sink *s; + PA_IDXSET_FOREACH(s, u->core->sinks, idx) { + if (!pa_streq(s->name, n)) { continue; } - pa_log_info("Setting description for sink %s to '%s'", sink->name, e->description); - pa_sink_set_description(sink, e->description); + pa_log_info("Setting description for sink %s to '%s'", s->name, e->description); + pa_sink_set_description(s, e->description); } pa_xfree(n); } else if ((n = get_name(name, "source:"))) { - for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) { - if (!pa_streq(source->name, n)) { + pa_source *s; + PA_IDXSET_FOREACH(s, u->core->sources, idx) { + if (!pa_streq(s->name, n)) { continue; } - if (source->monitor_of) { - pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name); + if (s->monitor_of) { + pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name); continue; } - pa_log_info("Setting description for source %s to '%s'", source->name, e->description); - pa_source_set_description(source, e->description); + pa_log_info("Setting description for source %s to '%s'", s->name, e->description); + pa_source_set_description(s, e->description); } pa_xfree(n); } -- cgit From 9d7a27ec8867ed7907944dbe52ed67bf252c00ee Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Fri, 2 Oct 2009 22:44:56 +0100 Subject: device-manager: Play nice with module-stream-restore. If m-s-r sets the device we let it do so. Otherwise we handle the routing. We run before module-intended-roles as the priority list will likely be configured appropriately to do the same job, albeit with manual setup. --- src/modules/module-device-manager.c | 84 +++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c index 8d17bb06..bfcbfeaf 100644 --- a/src/modules/module-device-manager.c +++ b/src/modules/module-device-manager.c @@ -518,6 +518,9 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) { pa_assert(u); pa_assert(u->do_routing); + if (si->save_sink) + return; + /* Skip this if it is already in the process of being moved anyway */ if (!si->sink) return; @@ -544,7 +547,7 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) { return; if (si->sink != sink) - pa_sink_input_move_to(si, sink, TRUE); + pa_sink_input_move_to(si, sink, FALSE); } static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) { @@ -573,6 +576,9 @@ static void route_source_output(struct userdata *u, pa_source_output *so) { pa_assert(u); pa_assert(u->do_routing); + if (so->save_source) + return; + if (so->direct_on_input) return; @@ -602,7 +608,7 @@ static void route_source_output(struct userdata *u, pa_source_output *so) { return; if (so->source != source) - pa_source_output_move_to(so, source, TRUE); + pa_source_output_move_to(so, source, FALSE); } static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) { @@ -811,22 +817,24 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n return PA_HOOK_OK; if (new_data->sink) - pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way..."); - - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index("none"); - else - role_index = get_role_index(role); + pa_log_debug("Not restoring device for stream because already set."); + else { + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index("none"); + else + role_index = get_role_index(role); - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; - device_index = u->preferred_sinks[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_sink *sink; + device_index = u->preferred_sinks[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_sink *sink; - if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { - new_data->sink = sink; + if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { + new_data->sink = sink; + new_data->save_sink = FALSE; + } } } } @@ -849,22 +857,24 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou return PA_HOOK_OK; if (new_data->source) - pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way..."); - - if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) - role_index = get_role_index("none"); - else - role_index = get_role_index(role); + pa_log_debug("Not restoring device for stream because already set."); + else { + if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) + role_index = get_role_index("none"); + else + role_index = get_role_index(role); - if (PA_INVALID_INDEX != role_index) { - uint32_t device_index; + if (PA_INVALID_INDEX != role_index) { + uint32_t device_index; - device_index = u->preferred_sources[role_index]; - if (PA_INVALID_INDEX != device_index) { - pa_source *source; + device_index = u->preferred_sources[role_index]; + if (PA_INVALID_INDEX != device_index) { + pa_source *source; - if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { - new_data->source = source; + if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) { + new_data->source = source; + new_data->save_source = FALSE; + } } } } @@ -1422,20 +1432,20 @@ int pa__init(pa_module*m) { u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u); /* The following slots are used to deal with routing */ - /* A little bit later than module-stream-restore, module-intended-roles */ - u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u); - u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u); + /* A little bit later than module-stream-restore, but before module-intended-roles */ + u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) sink_input_new_hook_callback, u); + u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) source_output_new_hook_callback, u); if (on_hotplug) { - /* A little bit later than module-stream-restore, module-intended-roles */ - u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u); - u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u); + /* A little bit later than module-stream-restore, but before module-intended-roles */ + u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_put_hook_callback, u); + u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) source_put_hook_callback, u); } if (on_rescue) { - /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */ - u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u); - u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u); + /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */ + u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_unlink_hook_callback, u); + u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) source_unlink_hook_callback, u); } if (!(fname = pa_state_path("device-manager", TRUE))) -- cgit From 40c1ca76c48147c7648e1f1a72cc2c747f3d0c9b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 4 Oct 2009 13:00:51 +0200 Subject: bluetooth: don't set auto_connect flag when discovering bt devices --- src/modules/bluetooth/module-bluetooth-device.c | 13 +++++++++++-- src/modules/bluetooth/module-bluetooth-discover.c | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 4592fca1..0ba1421b 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -74,7 +74,8 @@ PA_MODULE_USAGE( "profile= " "rate= " "channels= " - "path="); + "path= " + "auto_connect="); /* #ifdef NOKIA @@ -98,6 +99,7 @@ static const char* const valid_modargs[] = { "rate", "channels", "path", + "auto_connect", #ifdef NOKIA "sco_sink", "sco_source", @@ -141,6 +143,7 @@ struct userdata { char *address; char *path; pa_bluetooth_discovery *discovery; + pa_bool_t auto_connect; pa_dbus_connection *connection; @@ -399,7 +402,7 @@ static int get_caps(struct userdata *u, uint8_t seid) { pa_assert(u->profile == PROFILE_HSP); msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO; } - msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT; + msg.getcaps_req.flags = u->auto_connect ? BT_FLAG_AUTOCONNECT : 0; if (service_send(u, &msg.getcaps_req.h) < 0) return -1; @@ -2363,6 +2366,12 @@ int pa__init(pa_module* m) { goto fail; } + u->auto_connect = TRUE; + if (pa_modargs_get_value_boolean(ma, "auto_connect", &u->auto_connect)) { + pa_log("Failed to parse auto_connect= argument"); + goto fail; + } + channels = u->sample_spec.channels; if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 || channels <= 0 || channels > PA_CHANNELS_MAX) { diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index 02fd4240..0085fa8e 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -117,7 +117,7 @@ static pa_hook_result_t load_module_for_device(pa_bluetooth_discovery *y, const #endif if (d->audio_source_state >= PA_BT_AUDIO_STATE_CONNECTED) - args = pa_sprintf_malloc("%s profile=\"a2dp_source\"", args); + args = pa_sprintf_malloc("%s profile=\"a2dp_source\" auto_connect=no", args); pa_log_debug("Loading module-bluetooth-device %s", args); m = pa_module_load(u->module->core, "module-bluetooth-device", args); -- cgit From 7b682c969025845f75cbc74a9f830ad2dec8a415 Mon Sep 17 00:00:00 2001 From: Tor-Björn Claesson Date: Fri, 2 Oct 2009 14:13:59 +0200 Subject: loopback: Setting latency of module-loopback I checked the source code, and latency_msec is missing from the list of valid module arguments. Attaching a patch to add it. --- src/modules/module-loopback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c index 29c3ddab..bb0182b0 100644 --- a/src/modules/module-loopback.c +++ b/src/modules/module-loopback.c @@ -102,7 +102,7 @@ struct userdata { static const char* const valid_modargs[] = { "source", "sink", - "latency", + "latency_msec", "format", "rate", "channels", -- cgit From c96d2d1117a7e59b351358c8cdd79ef465ddbd49 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Oct 2009 00:17:24 +0200 Subject: alsa: disable period event only with tsched=1 --- src/modules/alsa/alsa-sink.c | 2 +- src/modules/alsa/alsa-source.c | 2 +- src/modules/alsa/alsa-util.c | 4 ++-- src/modules/alsa/alsa-util.h | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 22e88b4a..37419d98 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -927,7 +927,7 @@ static int update_sw_params(struct userdata *u) { pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); - if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min, !u->use_tsched)) < 0) { pa_log("Failed to set software parameters: %s", pa_alsa_strerror(err)); return err; } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index fa3ac0aa..37dd6476 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -877,7 +877,7 @@ static int update_sw_params(struct userdata *u) { pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); - if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min, !u->use_tsched)) < 0) { pa_log("Failed to set software parameters: %s", pa_alsa_strerror(err)); return err; } diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 43a8e829..0e22d17e 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -403,7 +403,7 @@ finish: return ret; } -int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { +int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, pa_bool_t period_event) { snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t boundary; int err; @@ -417,7 +417,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { return err; } - if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, 0)) < 0) { + if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) { pa_log_warn("Unable to disable period event: %s\n", pa_alsa_strerror(err)); return err; } diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 265cd28c..f6206fe2 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -51,7 +51,8 @@ int pa_alsa_set_hw_params( int pa_alsa_set_sw_params( snd_pcm_t *pcm, - snd_pcm_uframes_t avail_min); + snd_pcm_uframes_t avail_min, + pa_bool_t period_event); /* Picks a working mapping from the profile set based on the specified ss/map */ snd_pcm_t *pa_alsa_open_by_device_id_auto( -- cgit From b3592a160f0d2a28605048a81c0261bf7c45acbb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Oct 2009 23:34:22 +0200 Subject: position-event-sounds: never position test sounds in space --- src/modules/module-position-event-sounds.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index 7221b14f..ee4c8c88 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -74,7 +74,7 @@ static int parse_pos(const char *pos, double *f) { } static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) { - const char *hpos, *vpos, *role; + const char *hpos, *vpos, *role, *id; double f; char t[PA_CVOLUME_SNPRINT_MAX]; pa_cvolume v; @@ -87,6 +87,22 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i if (!pa_streq(role, "event")) return PA_HOOK_OK; + if ((id = pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID))) { + + /* The test sounds should never be positioned in space, since + * they might be trigered themselves to configure the speakers + * in space, which we don't want to mess up. */ + + if (pa_startswith(id, "audio-channel-")) + return PA_HOOK_OK; + + if (pa_streq(id, "audio-volume-change")) + return PA_HOOK_OK; + + if (pa_streq(id, "audio-test-signal")) + return PA_HOOK_OK; + } + if (!(hpos = pa_proplist_gets(data->proplist, PA_PROP_EVENT_MOUSE_HPOS))) hpos = pa_proplist_gets(data->proplist, PA_PROP_WINDOW_HPOS); -- cgit From b64b6bbf4c5eda8d4c80dd956987234d1378d055 Mon Sep 17 00:00:00 2001 From: Diego Elio 'Flameeyes' Pettenò Date: Wed, 7 Oct 2009 19:29:33 +0200 Subject: Fix out-of-tree builds when dbus module is enabled. --- src/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 35941a66..b277a9a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,8 @@ AM_CFLAGS = \ -I$(top_builddir)/src/modules/x11 \ -I$(top_srcdir)/src/modules/jack \ -I$(top_builddir)/src/modules/jack \ + -I$(top_srcdir)/src/modules/dbus \ + -I$(top_builddir)/src/modules/dbus \ $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \ $(LIBSAMPLERATE_CFLAGS) \ $(LIBSNDFILE_CFLAGS) \ -- cgit From f55357cd0bcbc0a86f4ea9056584885897a8ec6c Mon Sep 17 00:00:00 2001 From: Diego Elio 'Flameeyes' Pettenò Date: Wed, 7 Oct 2009 19:30:11 +0200 Subject: Add check for FFTW, and add option to disable it at build-time. This way there will be a message at configure if FFTW is not found, and this gets in-line with the rest of the modules' dependencies. --- src/Makefile.am | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index b277a9a3..c5d3ab7b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1017,7 +1017,6 @@ modlibexec_LTLIBRARIES += \ module-combine.la \ module-remap-sink.la \ module-ladspa-sink.la \ - module-equalizer-sink.la \ module-esound-sink.la \ module-tunnel-sink.la \ module-tunnel-source.la \ @@ -1195,6 +1194,11 @@ modlibexec_LTLIBRARIES += \ endif endif +if HAVE_FFTW +modlibexec_LTLIBRARIES += \ + module-equalizer-sink.la +endif + # These are generated by an M4 script SYMDEF_FILES = \ modules/module-cli-symdef.h \ @@ -1415,9 +1419,9 @@ module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c -module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) +module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(FFTW_CFLAGS) module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) $(FFTW_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) -- cgit From 8ec304d2d1e956cc3f5f35437ac4fe580b36f004 Mon Sep 17 00:00:00 2001 From: Diego Elio 'Flameeyes' Pettenò Date: Wed, 7 Oct 2009 19:31:11 +0200 Subject: Fix build when using -fweb, accept both register and memory constraints. This was reported as Gentoo bug #287391 by Torsten Kaiser, and the fix was suggested by Mike Frysinger. --- src/pulsecore/svolume_mmx.c | 4 ++-- src/pulsecore/svolume_sse.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c index 1768eb50..745c7de0 100644 --- a/src/pulsecore/svolume_mmx.c +++ b/src/pulsecore/svolume_mmx.c @@ -152,7 +152,7 @@ pa_volume_s16ne_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi " emms \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) - : "X" ((pa_reg_x86)channels) + : "rm" ((pa_reg_x86)channels) : "cc" ); } @@ -228,7 +228,7 @@ pa_volume_s16re_mmx (int16_t *samples, int32_t *volumes, unsigned channels, unsi " emms \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" ((pa_reg_x86)channel), "=&r" (temp) - : "X" ((pa_reg_x86)channels) + : "rm" ((pa_reg_x86)channels) : "cc" ); } diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c index ab9394fb..1cc4e0aa 100644 --- a/src/pulsecore/svolume_sse.c +++ b/src/pulsecore/svolume_sse.c @@ -149,7 +149,7 @@ pa_volume_s16ne_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns "8: \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) - : "X" ((pa_reg_x86)channels) + : "rm" ((pa_reg_x86)channels) : "cc" ); } @@ -237,7 +237,7 @@ pa_volume_s16re_sse2 (int16_t *samples, int32_t *volumes, unsigned channels, uns "8: \n\t" : "+r" (samples), "+r" (volumes), "+r" (length), "=D" (channel), "=&r" (temp) - : "X" ((pa_reg_x86)channels) + : "rm" ((pa_reg_x86)channels) : "cc" ); } -- cgit