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 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 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(-) 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(+) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(+) 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(-) 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(+) 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(-) 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(+) 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(-) 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(-) 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(-) 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(-) 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(-) 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(+) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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(-) 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 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(-) 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 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(-) 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(-) 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(-) 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(-) 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