2 This file is part of PulseAudio.
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <sys/types.h>
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/timeval.h>
37 #include <pulse/rtclock.h>
39 #include <pulsecore/core-error.h>
40 #include <pulsecore/module.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/modargs.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/core-subscribe.h>
45 #include <pulsecore/sink-input.h>
46 #include <pulsecore/source-output.h>
47 #include <pulsecore/namereg.h>
48 #include <pulsecore/protocol-native.h>
49 #include <pulsecore/pstream.h>
50 #include <pulsecore/pstream-util.h>
51 #include <pulsecore/database.h>
52 #include <pulsecore/tagstruct.h>
54 #include "module-device-manager-symdef.h"
56 PA_MODULE_AUTHOR("Colin Guthrie");
57 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
58 PA_MODULE_VERSION(PACKAGE_VERSION);
59 PA_MODULE_LOAD_ONCE(true);
61 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
62 "on_hotplug=<When new device becomes available, recheck streams?> "
63 "on_rescue=<When device becomes unavailable, recheck streams?>");
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
68 static const char* const valid_modargs[] = {
89 typedef uint32_t role_indexes_t[NUM_ROLES];
91 static const char* role_names[NUM_ROLES] = {
106 pa_subscription *subscription;
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_hook_slot,
113 *source_put_hook_slot,
114 *sink_unlink_hook_slot,
115 *source_unlink_hook_slot,
116 *connection_unlink_hook_slot;
117 pa_time_event *save_time_event;
118 pa_database *database;
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
131 #define ENTRY_VERSION 1
136 bool user_set_description;
138 role_indexes_t priority;
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
148 SUBCOMMAND_SUBSCRIBE,
152 /* Forward declarations */
154 static void dump_database(struct userdata *);
156 static void notify_subscribers(struct userdata *);
158 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
159 struct userdata *u = userdata;
165 pa_assert(e == u->save_time_event);
166 u->core->mainloop->time_free(u->save_time_event);
167 u->save_time_event = NULL;
169 pa_database_sync(u->database);
170 pa_log_info("Synced.");
177 static void trigger_save(struct userdata *u) {
181 notify_subscribers(u);
183 if (u->save_time_event)
186 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
189 static struct entry* entry_new(void) {
190 struct entry *r = pa_xnew0(struct entry, 1);
191 r->version = ENTRY_VERSION;
195 static void entry_free(struct entry* e) {
198 pa_xfree(e->description);
203 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
212 t = pa_tagstruct_new(NULL, 0);
213 pa_tagstruct_putu8(t, e->version);
214 pa_tagstruct_puts(t, e->description);
215 pa_tagstruct_put_boolean(t, e->user_set_description);
216 pa_tagstruct_puts(t, e->icon);
217 for (uint8_t i=0; i<ROLE_MAX; ++i)
218 pa_tagstruct_putu32(t, e->priority[i]);
220 key.data = (char *) name;
221 key.size = strlen(name);
223 data.data = (void*)pa_tagstruct_data(t, &data.size);
225 r = (pa_database_set(u->database, &key, &data, true) == 0);
227 pa_tagstruct_free(t);
232 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
234 #define LEGACY_ENTRY_VERSION 1
235 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
236 struct legacy_entry {
238 char description[PA_NAME_MAX];
239 bool user_set_description;
240 char icon[PA_NAME_MAX];
241 role_indexes_t priority;
243 struct legacy_entry *le;
249 if (data->size != sizeof(struct legacy_entry)) {
250 pa_log_debug("Size does not match.");
254 le = (struct legacy_entry*)data->data;
256 if (le->version != LEGACY_ENTRY_VERSION) {
257 pa_log_debug("Version mismatch.");
261 if (!memchr(le->description, 0, sizeof(le->description))) {
262 pa_log_warn("Description has missing NUL byte.");
266 if (!le->description[0]) {
267 pa_log_warn("Description is empty.");
271 if (!memchr(le->icon, 0, sizeof(le->icon))) {
272 pa_log_warn("Icon has missing NUL byte.");
277 e->description = pa_xstrdup(le->description);
278 e->icon = pa_xstrdup(le->icon);
283 static struct entry* entry_read(struct userdata *u, const char *name) {
285 struct entry *e = NULL;
286 pa_tagstruct *t = NULL;
287 const char *description, *icon;
292 key.data = (char*) name;
293 key.size = strlen(name);
297 if (!pa_database_get(u->database, &key, &data))
300 t = pa_tagstruct_new(data.data, data.size);
303 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
304 e->version > ENTRY_VERSION ||
305 pa_tagstruct_gets(t, &description) < 0 ||
306 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
307 pa_tagstruct_gets(t, &icon) < 0) {
312 if (e->user_set_description && !description) {
313 pa_log("Entry has user_set_description set, but the description is NULL.");
317 if (e->user_set_description && !*description) {
318 pa_log("Entry has user_set_description set, but the description is empty.");
322 e->description = pa_xstrdup(description);
323 e->icon = pa_xstrdup(icon);
325 for (uint8_t i=0; i<ROLE_MAX; ++i) {
326 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
330 if (!pa_tagstruct_eof(t))
333 pa_tagstruct_free(t);
334 pa_datum_free(&data);
339 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
344 pa_tagstruct_free(t);
346 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
347 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
348 if ((e = legacy_entry_read(u, &data))) {
349 pa_log_debug("Success. Saving new format for key: %s", name);
350 if (entry_write(u, name, e))
352 pa_datum_free(&data);
355 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
358 pa_datum_free(&data);
363 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) {
369 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
370 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
372 pa_log_debug(" %s No sink specified", human);
375 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
376 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
378 pa_log_debug(" %s No source specified", human);
382 static void dump_database(struct userdata *u) {
388 done = !pa_database_first(u->database, &key, NULL);
390 pa_log_debug("Dumping database");
396 done = !pa_database_next(u->database, &key, &next_key, NULL);
398 name = pa_xstrndup(key.data, key.size);
400 if ((e = entry_read(u, name))) {
401 pa_log_debug(" Got entry: %s", name);
402 pa_log_debug(" Description: %s", e->description);
403 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
404 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
405 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
406 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
417 pa_log_debug(" Highest priority devices per-role:");
419 pa_log_debug(" Sinks:");
420 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
422 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
423 strncpy(name, role_names[role], len);
424 for (int i = len+1; i < 12; ++i) name[i] = ' ';
425 name[len] = ':'; name[0] -= 32; name[12] = '\0';
426 dump_database_helper(u, role, name, true);
429 pa_log_debug(" Sources:");
430 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
432 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
433 strncpy(name, role_names[role], len);
434 for (int i = len+1; i < 12; ++i) name[i] = ' ';
435 name[len] = ':'; name[0] -= 32; name[12] = '\0';
436 dump_database_helper(u, role, name, false);
440 pa_log_debug("Completed database dump");
444 static void notify_subscribers(struct userdata *u) {
446 pa_native_connection *c;
451 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
454 t = pa_tagstruct_new(NULL, 0);
455 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
456 pa_tagstruct_putu32(t, 0);
457 pa_tagstruct_putu32(t, u->module->index);
458 pa_tagstruct_puts(t, u->module->name);
459 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
461 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
465 static bool entries_equal(const struct entry *a, const struct entry *b) {
470 if (!pa_streq(a->description, b->description)
471 || a->user_set_description != b->user_set_description
472 || !pa_streq(a->icon, b->icon))
475 for (int i=0; i < NUM_ROLES; ++i)
476 if (a->priority[i] != b->priority[i])
482 static char *get_name(const char *key, const char *prefix) {
485 if (strncmp(key, prefix, strlen(prefix)))
488 t = pa_xstrdup(key + strlen(prefix));
492 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
500 if ((old = entry_read(u, name))) {
502 entry->description = pa_xstrdup(old->description);
503 entry->icon = pa_xstrdup(old->icon);
505 /* This is a new device, so make sure we write it's priority list correctly */
506 role_indexes_t max_priority;
510 pa_zero(max_priority);
511 done = !pa_database_first(u->database, &key, NULL);
513 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
517 done = !pa_database_next(u->database, &key, &next_key, NULL);
519 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
523 name2 = pa_xstrndup(key.data, key.size);
525 if ((e = entry_read(u, name2))) {
526 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
527 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
539 /* Actually initialise our entry now we've calculated it */
540 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
541 entry->priority[i] = max_priority[i] + 1;
543 entry->user_set_description = false;
549 static uint32_t get_role_index(const char* role) {
552 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
553 if (pa_streq(role, role_names[i]))
556 return PA_INVALID_INDEX;
559 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
560 role_indexes_t *indexes, highest_priority_available;
562 bool done, sink_mode;
567 sink_mode = pa_streq(prefix, "sink:");
570 indexes = &u->preferred_sinks;
572 indexes = &u->preferred_sources;
574 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
575 (*indexes)[i] = PA_INVALID_INDEX;
577 pa_zero(highest_priority_available);
579 done = !pa_database_first(u->database, &key, NULL);
581 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
585 done = !pa_database_next(u->database, &key, &next_key, NULL);
587 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
588 char *name, *device_name;
591 name = pa_xstrndup(key.data, key.size);
592 pa_assert_se(device_name = get_name(name, prefix));
594 if ((e = entry_read(u, name))) {
595 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
596 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
597 /* We've found a device with a higher priority than that we've currently got,
598 so see if it is currently available or not and update our list */
605 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
606 if ((pa_sink*) ignore_device == sink)
608 if (pa_streq(sink->name, device_name)) {
610 idx = sink->index; /* Is this needed? */
617 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
618 if ((pa_source*) ignore_device == source)
620 if (pa_streq(source->name, device_name)) {
622 idx = source->index; /* Is this needed? */
628 highest_priority_available[i] = e->priority[i];
639 pa_xfree(device_name);
647 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
649 uint32_t role_index, device_index;
653 pa_assert(u->do_routing);
658 /* Skip this if it is already in the process of being moved anyway */
662 /* It might happen that a stream and a sink are set up at the
663 same time, in which case we want to make sure we don't
664 interfere with that */
665 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
668 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
669 role_index = get_role_index("none");
671 role_index = get_role_index(role);
673 if (PA_INVALID_INDEX == role_index)
676 device_index = u->preferred_sinks[role_index];
677 if (PA_INVALID_INDEX == device_index)
680 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
683 if (si->sink != sink)
684 pa_sink_input_move_to(si, sink, false);
687 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
696 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
698 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
699 route_sink_input(u, si);
705 static void route_source_output(struct userdata *u, pa_source_output *so) {
707 uint32_t role_index, device_index;
711 pa_assert(u->do_routing);
716 if (so->direct_on_input)
719 /* Skip this if it is already in the process of being moved anyway */
723 /* It might happen that a stream and a source are set up at the
724 same time, in which case we want to make sure we don't
725 interfere with that */
726 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
729 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
730 role_index = get_role_index("none");
732 role_index = get_role_index(role);
734 if (PA_INVALID_INDEX == role_index)
737 device_index = u->preferred_sources[role_index];
738 if (PA_INVALID_INDEX == device_index)
741 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
744 if (so->source != source)
745 pa_source_output_move_to(so, source, false);
748 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
749 pa_source_output *so;
757 update_highest_priority_device_indexes(u, "source:", ignore_source);
759 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
760 route_source_output(u, so);
766 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
767 struct userdata *u = userdata;
768 struct entry *entry, *old = NULL;
774 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
775 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
776 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
777 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
779 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
780 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
781 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
782 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
785 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
790 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
793 /* The role may change mid-stream, so we reroute */
794 route_sink_input(u, si);
797 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
798 pa_source_output *so;
802 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
805 /* The role may change mid-stream, so we reroute */
806 route_source_output(u, so);
809 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
812 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
816 name = pa_sprintf_malloc("sink:%s", sink->name);
818 old = load_or_initialize_entry(u, entry, name, "sink:");
820 if (!entry->user_set_description) {
821 pa_xfree(entry->description);
822 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
823 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
824 /* Warning: If two modules fight over the description, this could cause an infinite loop.
825 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
826 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
827 the description, this will fail... */
828 pa_sink_set_description(sink, entry->description);
831 pa_xfree(entry->icon);
832 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
834 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
837 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
839 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
842 if (source->monitor_of)
846 name = pa_sprintf_malloc("source:%s", source->name);
848 old = load_or_initialize_entry(u, entry, name, "source:");
850 if (!entry->user_set_description) {
851 pa_xfree(entry->description);
852 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
853 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
854 /* Warning: If two modules fight over the description, this could cause an infinite loop.
855 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
856 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
857 the description, this will fail... */
858 pa_source_set_description(source, entry->description);
861 pa_xfree(entry->icon);
862 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
864 pa_assert_not_reached();
871 if (entries_equal(old, entry)) {
882 pa_log_info("Storing device %s.", name);
884 if (entry_write(u, name, entry))
887 pa_log_warn("Could not save device");;
893 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
901 name = pa_sprintf_malloc("sink:%s", new_data->name);
903 if ((e = entry_read(u, name))) {
904 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
905 pa_log_info("Restoring description for sink %s.", new_data->name);
906 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
917 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
925 name = pa_sprintf_malloc("source:%s", new_data->name);
927 if ((e = entry_read(u, name))) {
928 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
929 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
930 pa_log_info("Restoring description for source %s.", new_data->name);
931 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
942 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
954 pa_log_debug("Not restoring device for stream because already set.");
956 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
957 role_index = get_role_index("none");
959 role_index = get_role_index(role);
961 if (PA_INVALID_INDEX != role_index) {
962 uint32_t device_index;
964 device_index = u->preferred_sinks[role_index];
965 if (PA_INVALID_INDEX != device_index) {
968 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
969 if (!pa_sink_input_new_data_set_sink(new_data, sink, false))
970 pa_log_debug("Not restoring device for stream because no supported format was found");
979 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
990 if (new_data->direct_on_input)
993 if (new_data->source)
994 pa_log_debug("Not restoring device for stream because already set.");
996 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
997 role_index = get_role_index("none");
999 role_index = get_role_index(role);
1001 if (PA_INVALID_INDEX != role_index) {
1002 uint32_t device_index;
1004 device_index = u->preferred_sources[role_index];
1005 if (PA_INVALID_INDEX != device_index) {
1008 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
1009 if (!pa_source_output_new_data_set_source(new_data, source, false))
1010 pa_log_debug("Not restoring device for stream because no supported format was found");
1018 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1021 pa_assert(u->core == c);
1022 pa_assert(u->on_hotplug);
1024 notify_subscribers(u);
1026 return route_sink_inputs(u, NULL);
1029 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1032 pa_assert(u->core == c);
1033 pa_assert(u->on_hotplug);
1035 notify_subscribers(u);
1037 return route_source_outputs(u, NULL);
1040 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1044 pa_assert(u->core == c);
1045 pa_assert(u->on_rescue);
1047 /* There's no point in doing anything if the core is shut down anyway */
1048 if (c->state == PA_CORE_SHUTDOWN)
1051 notify_subscribers(u);
1053 return route_sink_inputs(u, sink);
1056 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1060 pa_assert(u->core == c);
1061 pa_assert(u->on_rescue);
1063 /* There's no point in doing anything if the core is shut down anyway */
1064 if (c->state == PA_CORE_SHUTDOWN)
1067 notify_subscribers(u);
1069 return route_source_outputs(u, source);
1072 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1080 if (!e->user_set_description)
1083 if ((n = get_name(name, "sink:"))) {
1085 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1086 if (!pa_streq(s->name, n)) {
1090 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1091 pa_sink_set_description(s, e->description);
1095 else if ((n = get_name(name, "source:"))) {
1097 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1098 if (!pa_streq(s->name, n)) {
1102 if (s->monitor_of) {
1103 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1107 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1108 pa_source_set_description(s, e->description);
1114 #define EXT_VERSION 1
1116 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1119 pa_tagstruct *reply = NULL;
1128 if (pa_tagstruct_getu32(t, &command) < 0)
1131 reply = pa_tagstruct_new(NULL, 0);
1132 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1133 pa_tagstruct_putu32(reply, tag);
1136 case SUBCOMMAND_TEST: {
1137 if (!pa_tagstruct_eof(t))
1140 pa_tagstruct_putu32(reply, EXT_VERSION);
1144 case SUBCOMMAND_READ: {
1148 if (!pa_tagstruct_eof(t))
1151 done = !pa_database_first(u->database, &key, NULL);
1158 done = !pa_database_next(u->database, &key, &next_key, NULL);
1160 name = pa_xstrndup(key.data, key.size);
1161 pa_datum_free(&key);
1163 if ((e = entry_read(u, name))) {
1166 uint32_t found_index = PA_INVALID_INDEX;
1168 if ((device_name = get_name(name, "sink:"))) {
1170 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1171 if (pa_streq(s->name, device_name)) {
1172 found_index = s->index;
1176 pa_xfree(device_name);
1177 } else if ((device_name = get_name(name, "source:"))) {
1179 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1180 if (pa_streq(s->name, device_name)) {
1181 found_index = s->index;
1185 pa_xfree(device_name);
1188 pa_tagstruct_puts(reply, name);
1189 pa_tagstruct_puts(reply, e->description);
1190 pa_tagstruct_puts(reply, e->icon);
1191 pa_tagstruct_putu32(reply, found_index);
1192 pa_tagstruct_putu32(reply, NUM_ROLES);
1194 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1195 pa_tagstruct_puts(reply, role_names[i]);
1196 pa_tagstruct_putu32(reply, e->priority[i]);
1210 case SUBCOMMAND_RENAME: {
1213 const char *device, *description;
1215 if (pa_tagstruct_gets(t, &device) < 0 ||
1216 pa_tagstruct_gets(t, &description) < 0)
1219 if (!device || !*device || !description || !*description)
1222 if ((e = entry_read(u, device))) {
1223 pa_xfree(e->description);
1224 e->description = pa_xstrdup(description);
1225 e->user_set_description = true;
1227 if (entry_write(u, (char *)device, e)) {
1228 apply_entry(u, device, e);
1233 pa_log_warn("Could not save device");
1238 pa_log_warn("Could not rename device %s, no entry in database", device);
1243 case SUBCOMMAND_DELETE:
1245 while (!pa_tagstruct_eof(t)) {
1249 if (pa_tagstruct_gets(t, &name) < 0)
1252 key.data = (char*) name;
1253 key.size = strlen(name);
1255 /** @todo: Reindex the priorities */
1256 pa_database_unset(u->database, &key);
1263 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1267 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1270 if ((u->do_routing = enable)) {
1271 /* Update our caches */
1272 update_highest_priority_device_indexes(u, "sink:", NULL);
1273 update_highest_priority_device_indexes(u, "source:", NULL);
1279 case SUBCOMMAND_REORDER: {
1283 uint32_t role_index, n_devices;
1285 bool done, sink_mode = true;
1286 struct device_t { uint32_t prio; char *device; };
1287 struct device_t *device;
1288 struct device_t **devices;
1289 uint32_t i, idx, offset;
1294 if (pa_tagstruct_gets(t, &role) < 0 ||
1295 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1299 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1302 /* Cycle through the devices given and make sure they exist */
1303 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1306 for (i = 0; i < n_devices; ++i) {
1308 if (pa_tagstruct_gets(t, &s) < 0) {
1309 while ((device = pa_hashmap_steal_first(h))) {
1310 pa_xfree(device->device);
1314 pa_hashmap_free(h, NULL);
1315 pa_log_error("Protocol error on reorder");
1319 /* Ensure this is a valid entry */
1320 if (!(e = entry_read(u, s))) {
1321 while ((device = pa_hashmap_steal_first(h))) {
1322 pa_xfree(device->device);
1326 pa_hashmap_free(h, NULL);
1327 pa_log_error("Client specified an unknown device in it's reorder list.");
1334 sink_mode = (0 == strncmp("sink:", s, 5));
1335 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1336 while ((device = pa_hashmap_steal_first(h))) {
1337 pa_xfree(device->device);
1341 pa_hashmap_free(h, NULL);
1342 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1346 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1347 device = pa_xnew(struct device_t, 1);
1348 device->device = pa_xstrdup(s);
1349 if (pa_hashmap_put(h, device->device, device) == 0) {
1353 pa_xfree(device->device);
1358 /*pa_log_debug("Hashmap contents (received from client)");
1359 PA_HASHMAP_FOREACH(device, h, state) {
1360 pa_log_debug(" - %s (%d)", device->device, device->prio);
1363 /* Now cycle through our list and add all the devices.
1364 This has the effect of adding in any in our DB,
1365 not specified in the device list (and thus will be
1366 tacked on at the end) */
1368 done = !pa_database_first(u->database, &key, NULL);
1370 while (!done && idx < 256) {
1373 done = !pa_database_next(u->database, &key, &next_key, NULL);
1375 device = pa_xnew(struct device_t, 1);
1376 device->device = pa_xstrndup(key.data, key.size);
1377 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1378 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1380 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1381 if (pa_hashmap_put(h, device->device, device) == 0
1382 && (e = entry_read(u, device->device))) {
1383 /* We add offset on to the existing priority so that when we order, the
1384 existing entries are always lower priority than the new ones. */
1385 device->prio = (offset + e->priority[role_index]);
1389 pa_xfree(device->device);
1393 pa_xfree(device->device);
1397 pa_datum_free(&key);
1402 /*pa_log_debug("Hashmap contents (combined with database)");
1403 PA_HASHMAP_FOREACH(device, h, state) {
1404 pa_log_debug(" - %s (%d)", device->device, device->prio);
1407 /* Now we put all the entries in a simple list for sorting it. */
1408 n_devices = pa_hashmap_size(h);
1409 devices = pa_xnew(struct device_t *, n_devices);
1411 while ((device = pa_hashmap_steal_first(h))) {
1412 devices[idx++] = device;
1414 pa_hashmap_free(h, NULL);
1416 /* Simple bubble sort */
1417 for (i = 0; i < n_devices; ++i) {
1418 for (uint32_t j = i; j < n_devices; ++j) {
1419 if (devices[i]->prio > devices[j]->prio) {
1420 struct device_t *tmp;
1422 devices[i] = devices[j];
1428 /*pa_log_debug("Sorted device list");
1429 for (i = 0; i < n_devices; ++i) {
1430 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1433 /* Go through in order and write the new entry and cleanup our own list */
1436 for (i = 0; i < n_devices; ++i) {
1437 if ((e = entry_read(u, devices[i]->device))) {
1438 if (e->priority[role_index] == idx)
1441 e->priority[role_index] = idx;
1443 if (entry_write(u, (char *) devices[i]->device, e)) {
1451 pa_xfree(devices[i]->device);
1452 pa_xfree(devices[i]);
1461 route_sink_inputs(u, NULL);
1463 route_source_outputs(u, NULL);
1469 case SUBCOMMAND_SUBSCRIBE: {
1473 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1474 !pa_tagstruct_eof(t))
1478 pa_idxset_put(u->subscribed, c, NULL);
1480 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1489 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1495 pa_tagstruct_free(reply);
1500 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1505 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1509 struct prioritised_indexes {
1514 int pa__init(pa_module*m) {
1515 pa_modargs *ma = NULL;
1521 bool do_routing = false, on_hotplug = true, on_rescue = true;
1522 uint32_t total_devices;
1526 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1527 pa_log("Failed to parse module arguments");
1531 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1532 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1533 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1534 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1538 m->userdata = u = pa_xnew0(struct userdata, 1);
1541 u->do_routing = do_routing;
1542 u->on_hotplug = on_hotplug;
1543 u->on_rescue = on_rescue;
1544 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1546 u->protocol = pa_native_protocol_get(m->core);
1547 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1549 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);
1551 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);
1553 /* Used to handle device description management */
1554 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);
1555 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);
1557 /* The following slots are used to deal with routing */
1558 /* A little bit later than module-stream-restore, but before module-intended-roles */
1559 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);
1560 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);
1563 /* A little bit later than module-stream-restore, but before module-intended-roles */
1564 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);
1565 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);
1569 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1570 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);
1571 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);
1574 if (!(fname = pa_state_path("device-manager", true)))
1577 if (!(u->database = pa_database_open(fname, true))) {
1578 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1583 pa_log_info("Successfully opened database file '%s'.", fname);
1586 /* Attempt to inject the devices into the list in priority order */
1587 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1588 if (total_devices > 0 && total_devices < 128) {
1590 struct prioritised_indexes p_i[128];
1592 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1594 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1595 pa_log_debug("Found sink index %u", sink->index);
1596 p_i[i ].index = sink->index;
1597 p_i[i++].priority = sink->priority;
1599 /* Bubble sort it (only really useful for first time creation) */
1601 for (uint32_t j = 0; j < i; ++j)
1602 for (uint32_t k = 0; k < i; ++k)
1603 if (p_i[j].priority > p_i[k].priority) {
1604 struct prioritised_indexes tmp_pi = p_i[k];
1609 for (uint32_t j = 0; j < i; ++j)
1610 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1612 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1614 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1615 p_i[i ].index = source->index;
1616 p_i[i++].priority = source->priority;
1618 /* Bubble sort it (only really useful for first time creation) */
1620 for (uint32_t j = 0; j < i; ++j)
1621 for (uint32_t k = 0; k < i; ++k)
1622 if (p_i[j].priority > p_i[k].priority) {
1623 struct prioritised_indexes tmp_pi = p_i[k];
1628 for (uint32_t j = 0; j < i; ++j)
1629 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1631 else if (total_devices > 0) {
1632 /* This user has a *lot* of devices... */
1633 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1634 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1636 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1637 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1640 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1641 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1642 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1645 route_sink_inputs(u, NULL);
1646 route_source_outputs(u, NULL);
1648 #ifdef DUMP_DATABASE
1652 pa_modargs_free(ma);
1659 pa_modargs_free(ma);
1664 void pa__done(pa_module*m) {
1669 if (!(u = m->userdata))
1672 if (u->subscription)
1673 pa_subscription_free(u->subscription);
1675 if (u->sink_new_hook_slot)
1676 pa_hook_slot_free(u->sink_new_hook_slot);
1677 if (u->source_new_hook_slot)
1678 pa_hook_slot_free(u->source_new_hook_slot);
1680 if (u->sink_input_new_hook_slot)
1681 pa_hook_slot_free(u->sink_input_new_hook_slot);
1682 if (u->source_output_new_hook_slot)
1683 pa_hook_slot_free(u->source_output_new_hook_slot);
1685 if (u->sink_put_hook_slot)
1686 pa_hook_slot_free(u->sink_put_hook_slot);
1687 if (u->source_put_hook_slot)
1688 pa_hook_slot_free(u->source_put_hook_slot);
1690 if (u->sink_unlink_hook_slot)
1691 pa_hook_slot_free(u->sink_unlink_hook_slot);
1692 if (u->source_unlink_hook_slot)
1693 pa_hook_slot_free(u->source_unlink_hook_slot);
1695 if (u->connection_unlink_hook_slot)
1696 pa_hook_slot_free(u->connection_unlink_hook_slot);
1698 if (u->save_time_event)
1699 u->core->mainloop->time_free(u->save_time_event);
1702 pa_database_close(u->database);
1705 pa_native_protocol_remove_ext(u->protocol, m);
1706 pa_native_protocol_unref(u->protocol);
1710 pa_idxset_free(u->subscribed, NULL);