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, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
32 #include <pulse/gccmacro.h>
33 #include <pulse/xmalloc.h>
34 #include <pulse/timeval.h>
35 #include <pulse/rtclock.h>
37 #include <pulsecore/core-error.h>
38 #include <pulsecore/module.h>
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/log.h>
42 #include <pulsecore/core-subscribe.h>
43 #include <pulsecore/sink-input.h>
44 #include <pulsecore/source-output.h>
45 #include <pulsecore/namereg.h>
46 #include <pulsecore/protocol-native.h>
47 #include <pulsecore/pstream.h>
48 #include <pulsecore/pstream-util.h>
49 #include <pulsecore/database.h>
50 #include <pulsecore/tagstruct.h>
52 PA_MODULE_AUTHOR("Colin Guthrie");
53 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
54 PA_MODULE_VERSION(PACKAGE_VERSION);
55 PA_MODULE_LOAD_ONCE(true);
57 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
58 "on_hotplug=<When new device becomes available, recheck streams?> "
59 "on_rescue=<When device becomes unavailable, recheck streams?>");
61 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
64 static const char* const valid_modargs[] = {
85 typedef uint32_t role_indexes_t[NUM_ROLES];
87 static const char* role_names[NUM_ROLES] = {
102 pa_subscription *subscription;
105 *source_new_hook_slot,
106 *sink_input_new_hook_slot,
107 *source_output_new_hook_slot,
109 *source_put_hook_slot,
110 *sink_unlink_hook_slot,
111 *source_unlink_hook_slot,
112 *connection_unlink_hook_slot;
113 pa_time_event *save_time_event;
114 pa_database *database;
116 pa_native_protocol *protocol;
117 pa_idxset *subscribed;
123 role_indexes_t preferred_sinks;
124 role_indexes_t preferred_sources;
127 #define ENTRY_VERSION 1
132 bool user_set_description;
134 role_indexes_t priority;
142 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
144 SUBCOMMAND_SUBSCRIBE,
148 /* Forward declarations */
150 static void dump_database(struct userdata *);
152 static void notify_subscribers(struct userdata *);
154 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
155 struct userdata *u = userdata;
161 pa_assert(e == u->save_time_event);
162 u->core->mainloop->time_free(u->save_time_event);
163 u->save_time_event = NULL;
165 pa_database_sync(u->database);
166 pa_log_info("Synced.");
173 static void trigger_save(struct userdata *u) {
177 notify_subscribers(u);
179 if (u->save_time_event)
182 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
185 static struct entry* entry_new(void) {
186 struct entry *r = pa_xnew0(struct entry, 1);
187 r->version = ENTRY_VERSION;
191 static void entry_free(struct entry* e) {
194 pa_xfree(e->description);
199 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
208 t = pa_tagstruct_new();
209 pa_tagstruct_putu8(t, e->version);
210 pa_tagstruct_puts(t, e->description);
211 pa_tagstruct_put_boolean(t, e->user_set_description);
212 pa_tagstruct_puts(t, e->icon);
213 for (uint8_t i=0; i<ROLE_MAX; ++i)
214 pa_tagstruct_putu32(t, e->priority[i]);
216 key.data = (char *) name;
217 key.size = strlen(name);
219 data.data = (void*)pa_tagstruct_data(t, &data.size);
221 r = (pa_database_set(u->database, &key, &data, true) == 0);
223 pa_tagstruct_free(t);
228 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
230 #define LEGACY_ENTRY_VERSION 1
231 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
232 struct legacy_entry {
234 char description[PA_NAME_MAX];
235 bool user_set_description;
236 char icon[PA_NAME_MAX];
237 role_indexes_t priority;
239 struct legacy_entry *le;
245 if (data->size != sizeof(struct legacy_entry)) {
246 pa_log_debug("Size does not match.");
250 le = (struct legacy_entry*)data->data;
252 if (le->version != LEGACY_ENTRY_VERSION) {
253 pa_log_debug("Version mismatch.");
257 if (!memchr(le->description, 0, sizeof(le->description))) {
258 pa_log_warn("Description has missing NUL byte.");
262 if (!le->description[0]) {
263 pa_log_warn("Description is empty.");
267 if (!memchr(le->icon, 0, sizeof(le->icon))) {
268 pa_log_warn("Icon has missing NUL byte.");
273 e->description = pa_xstrdup(le->description);
274 e->icon = pa_xstrdup(le->icon);
279 static struct entry* entry_read(struct userdata *u, const char *name) {
281 struct entry *e = NULL;
282 pa_tagstruct *t = NULL;
283 const char *description, *icon;
288 key.data = (char*) name;
289 key.size = strlen(name);
293 if (!pa_database_get(u->database, &key, &data)) {
294 pa_log_debug("Database contains no data for key: %s", name);
298 t = pa_tagstruct_new_fixed(data.data, data.size);
301 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
302 e->version > ENTRY_VERSION ||
303 pa_tagstruct_gets(t, &description) < 0 ||
304 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
305 pa_tagstruct_gets(t, &icon) < 0) {
310 if (e->user_set_description && !description) {
311 pa_log("Entry has user_set_description set, but the description is NULL.");
315 if (e->user_set_description && !*description) {
316 pa_log("Entry has user_set_description set, but the description is empty.");
320 e->description = pa_xstrdup(description);
321 e->icon = pa_xstrdup(icon);
323 for (uint8_t i=0; i<ROLE_MAX; ++i) {
324 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
328 if (!pa_tagstruct_eof(t))
331 pa_tagstruct_free(t);
332 pa_datum_free(&data);
337 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
342 pa_tagstruct_free(t);
344 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
345 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
346 if ((e = legacy_entry_read(u, &data))) {
347 pa_log_debug("Success. Saving new format for key: %s", name);
348 if (entry_write(u, name, e))
350 pa_datum_free(&data);
353 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
356 pa_datum_free(&data);
361 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) {
367 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
368 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
370 pa_log_debug(" %s No sink specified", human);
373 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
374 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
376 pa_log_debug(" %s No source specified", human);
380 static void dump_database(struct userdata *u) {
386 done = !pa_database_first(u->database, &key, NULL);
388 pa_log_debug("Dumping database");
394 done = !pa_database_next(u->database, &key, &next_key, NULL);
396 name = pa_xstrndup(key.data, key.size);
398 if ((e = entry_read(u, name))) {
399 pa_log_debug(" Got entry: %s", name);
400 pa_log_debug(" Description: %s", e->description);
401 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
402 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
403 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
404 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
415 pa_log_debug(" Highest priority devices per-role:");
417 pa_log_debug(" Sinks:");
418 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
420 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
421 strncpy(name, role_names[role], len);
422 for (int i = len+1; i < 12; ++i) name[i] = ' ';
423 name[len] = ':'; name[0] -= 32; name[12] = '\0';
424 dump_database_helper(u, role, name, true);
427 pa_log_debug(" Sources:");
428 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
430 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
431 strncpy(name, role_names[role], len);
432 for (int i = len+1; i < 12; ++i) name[i] = ' ';
433 name[len] = ':'; name[0] -= 32; name[12] = '\0';
434 dump_database_helper(u, role, name, false);
438 pa_log_debug("Completed database dump");
442 static void notify_subscribers(struct userdata *u) {
444 pa_native_connection *c;
449 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
452 t = pa_tagstruct_new();
453 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
454 pa_tagstruct_putu32(t, 0);
455 pa_tagstruct_putu32(t, u->module->index);
456 pa_tagstruct_puts(t, u->module->name);
457 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
459 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
463 static bool entries_equal(const struct entry *a, const struct entry *b) {
468 if (!pa_streq(a->description, b->description)
469 || a->user_set_description != b->user_set_description
470 || !pa_streq(a->icon, b->icon))
473 for (int i=0; i < NUM_ROLES; ++i)
474 if (a->priority[i] != b->priority[i])
480 static char *get_name(const char *key, const char *prefix) {
483 if (strncmp(key, prefix, strlen(prefix)))
486 t = pa_xstrdup(key + strlen(prefix));
490 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
498 if ((old = entry_read(u, name))) {
500 entry->description = pa_xstrdup(old->description);
501 entry->icon = pa_xstrdup(old->icon);
503 /* This is a new device, so make sure we write its priority list correctly */
504 role_indexes_t max_priority;
508 pa_zero(max_priority);
509 done = !pa_database_first(u->database, &key, NULL);
511 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
515 done = !pa_database_next(u->database, &key, &next_key, NULL);
517 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
521 name2 = pa_xstrndup(key.data, key.size);
523 if ((e = entry_read(u, name2))) {
524 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
525 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
537 /* Actually initialise our entry now we've calculated it */
538 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
539 entry->priority[i] = max_priority[i] + 1;
541 entry->user_set_description = false;
547 static uint32_t get_role_index(const char* role) {
550 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
551 if (pa_streq(role, role_names[i]))
554 return PA_INVALID_INDEX;
557 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
558 role_indexes_t *indexes, highest_priority_available;
560 bool done, sink_mode;
565 sink_mode = pa_streq(prefix, "sink:");
568 indexes = &u->preferred_sinks;
570 indexes = &u->preferred_sources;
572 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
573 (*indexes)[i] = PA_INVALID_INDEX;
575 pa_zero(highest_priority_available);
577 done = !pa_database_first(u->database, &key, NULL);
579 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
583 done = !pa_database_next(u->database, &key, &next_key, NULL);
585 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
586 char *name, *device_name;
589 name = pa_xstrndup(key.data, key.size);
590 pa_assert_se(device_name = get_name(name, prefix));
592 if ((e = entry_read(u, name))) {
593 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
594 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
595 /* We've found a device with a higher priority than that we've currently got,
596 so see if it is currently available or not and update our list */
603 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
604 if ((pa_sink*) ignore_device == sink)
606 if (!PA_SINK_IS_LINKED(sink->state))
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_SOURCE_IS_LINKED(source->state))
622 if (pa_streq(source->name, device_name)) {
624 idx = source->index; /* Is this needed? */
630 highest_priority_available[i] = e->priority[i];
641 pa_xfree(device_name);
649 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
650 const char *auto_filtered_prop;
652 uint32_t role_index, device_index;
653 bool auto_filtered = false;
657 pa_assert(u->do_routing);
659 /* Skip this if it is already in the process of being moved anyway */
663 /* Don't override user or application routing requests. */
664 if (pa_safe_streq(si->sink->name, si->preferred_sink) || si->sink_requested_by_application)
667 auto_filtered_prop = pa_proplist_gets(si->proplist, "module-device-manager.auto_filtered");
668 if (auto_filtered_prop)
669 auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
671 /* It might happen that a stream and a sink are set up at the
672 same time, in which case we want to make sure we don't
673 interfere with that */
674 if (!PA_SINK_INPUT_IS_LINKED(si->state))
677 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
678 role_index = get_role_index("none");
680 role_index = get_role_index(role);
682 if (PA_INVALID_INDEX == role_index)
685 device_index = u->preferred_sinks[role_index];
686 if (PA_INVALID_INDEX == device_index)
689 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
693 /* For streams for which a filter has been loaded by another module, we
694 * do not try to execute moves within the same filter hierarchy */
695 if (pa_sink_get_master(si->sink) == pa_sink_get_master(sink))
699 if (si->sink != sink)
700 pa_sink_input_move_to(si, sink, false);
703 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
712 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
714 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
715 route_sink_input(u, si);
721 static void route_source_output(struct userdata *u, pa_source_output *so) {
722 const char *auto_filtered_prop;
724 uint32_t role_index, device_index;
725 bool auto_filtered = false;
729 pa_assert(u->do_routing);
731 if (so->direct_on_input)
734 /* Skip this if it is already in the process of being moved anyway */
738 /* Don't override user or application routing requests. */
739 if (pa_safe_streq(so->source->name, so->preferred_source) || so->source_requested_by_application)
742 auto_filtered_prop = pa_proplist_gets(so->proplist, "module-device-manager.auto_filtered");
743 if (auto_filtered_prop)
744 auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
746 /* It might happen that a stream and a source are set up at the
747 same time, in which case we want to make sure we don't
748 interfere with that */
749 if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state))
752 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
753 role_index = get_role_index("none");
755 role_index = get_role_index(role);
757 if (PA_INVALID_INDEX == role_index)
760 device_index = u->preferred_sources[role_index];
761 if (PA_INVALID_INDEX == device_index)
764 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
768 /* For streams for which a filter has been loaded by another module, we
769 * do not try to execute moves within the same filter hierarchy */
770 if (pa_source_get_master(so->source) == pa_source_get_master(source))
774 if (so->source != source)
775 pa_source_output_move_to(so, source, false);
778 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
779 pa_source_output *so;
787 update_highest_priority_device_indexes(u, "source:", ignore_source);
789 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
790 route_source_output(u, so);
796 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
797 struct userdata *u = userdata;
798 struct entry *entry, *old = NULL;
804 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
805 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
806 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
807 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
809 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
810 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
811 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
812 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
815 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
820 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
823 /* The role may change mid-stream, so we reroute */
824 route_sink_input(u, si);
827 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
828 pa_source_output *so;
832 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
835 /* The role may change mid-stream, so we reroute */
836 route_source_output(u, so);
839 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
842 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
846 name = pa_sprintf_malloc("sink:%s", sink->name);
848 old = load_or_initialize_entry(u, entry, name, "sink:");
850 if (!entry->user_set_description) {
851 pa_xfree(entry->description);
852 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
853 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->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_sink_set_description(sink, entry->description);
861 pa_xfree(entry->icon);
862 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
864 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
867 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
869 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
872 if (source->monitor_of)
876 name = pa_sprintf_malloc("source:%s", source->name);
878 old = load_or_initialize_entry(u, entry, name, "source:");
880 if (!entry->user_set_description) {
881 pa_xfree(entry->description);
882 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
883 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
884 /* Warning: If two modules fight over the description, this could cause an infinite loop.
885 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
886 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
887 the description, this will fail... */
888 pa_source_set_description(source, entry->description);
891 pa_xfree(entry->icon);
892 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
894 pa_assert_not_reached();
901 if (entries_equal(old, entry)) {
912 pa_log_info("Storing device %s.", name);
914 if (entry_write(u, name, entry))
917 pa_log_warn("Could not save device");;
923 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
931 name = pa_sprintf_malloc("sink:%s", new_data->name);
933 if ((e = entry_read(u, name))) {
934 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
935 pa_log_info("Restoring description for sink %s.", new_data->name);
936 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
947 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
955 name = pa_sprintf_malloc("source:%s", new_data->name);
957 if ((e = entry_read(u, name))) {
958 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
959 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
960 pa_log_info("Restoring description for source %s.", new_data->name);
961 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
972 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
984 pa_log_debug("Not restoring device for stream because already set.");
986 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
987 role_index = get_role_index("none");
989 role_index = get_role_index(role);
991 if (PA_INVALID_INDEX != role_index) {
992 uint32_t device_index;
994 device_index = u->preferred_sinks[role_index];
995 if (PA_INVALID_INDEX != device_index) {
998 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
999 if (!pa_sink_input_new_data_set_sink(new_data, sink, false, false))
1000 pa_log_debug("Not restoring device for stream because no supported format was found");
1009 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
1011 uint32_t role_index;
1014 pa_assert(new_data);
1020 if (new_data->direct_on_input)
1023 if (new_data->source)
1024 pa_log_debug("Not restoring device for stream because already set.");
1026 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
1027 role_index = get_role_index("none");
1029 role_index = get_role_index(role);
1031 if (PA_INVALID_INDEX != role_index) {
1032 uint32_t device_index;
1034 device_index = u->preferred_sources[role_index];
1035 if (PA_INVALID_INDEX != device_index) {
1038 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
1039 if (!pa_source_output_new_data_set_source(new_data, source, false, false))
1040 pa_log_debug("Not restoring device for stream because no supported format was found");
1048 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1051 pa_assert(u->core == c);
1052 pa_assert(u->on_hotplug);
1054 notify_subscribers(u);
1056 return route_sink_inputs(u, NULL);
1059 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1062 pa_assert(u->core == c);
1063 pa_assert(u->on_hotplug);
1065 notify_subscribers(u);
1067 return route_source_outputs(u, NULL);
1070 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1074 pa_assert(u->core == c);
1075 pa_assert(u->on_rescue);
1077 /* There's no point in doing anything if the core is shut down anyway */
1078 if (c->state == PA_CORE_SHUTDOWN)
1081 notify_subscribers(u);
1083 return route_sink_inputs(u, sink);
1086 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1090 pa_assert(u->core == c);
1091 pa_assert(u->on_rescue);
1093 /* There's no point in doing anything if the core is shut down anyway */
1094 if (c->state == PA_CORE_SHUTDOWN)
1097 notify_subscribers(u);
1099 return route_source_outputs(u, source);
1102 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1110 if (!e->user_set_description)
1113 if ((n = get_name(name, "sink:"))) {
1115 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1116 if (!pa_streq(s->name, n)) {
1120 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1121 pa_sink_set_description(s, e->description);
1125 else if ((n = get_name(name, "source:"))) {
1127 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1128 if (!pa_streq(s->name, n)) {
1132 if (s->monitor_of) {
1133 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1137 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1138 pa_source_set_description(s, e->description);
1144 #define EXT_VERSION 1
1146 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1149 pa_tagstruct *reply = NULL;
1158 if (pa_tagstruct_getu32(t, &command) < 0)
1161 reply = pa_tagstruct_new();
1162 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1163 pa_tagstruct_putu32(reply, tag);
1166 case SUBCOMMAND_TEST: {
1167 if (!pa_tagstruct_eof(t))
1170 pa_tagstruct_putu32(reply, EXT_VERSION);
1174 case SUBCOMMAND_READ: {
1178 if (!pa_tagstruct_eof(t))
1181 done = !pa_database_first(u->database, &key, NULL);
1188 done = !pa_database_next(u->database, &key, &next_key, NULL);
1190 name = pa_xstrndup(key.data, key.size);
1191 pa_datum_free(&key);
1193 if ((e = entry_read(u, name))) {
1196 uint32_t found_index = PA_INVALID_INDEX;
1198 if ((device_name = get_name(name, "sink:"))) {
1200 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1201 if (pa_streq(s->name, device_name)) {
1202 found_index = s->index;
1206 pa_xfree(device_name);
1207 } else if ((device_name = get_name(name, "source:"))) {
1209 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1210 if (pa_streq(s->name, device_name)) {
1211 found_index = s->index;
1215 pa_xfree(device_name);
1218 pa_tagstruct_puts(reply, name);
1219 pa_tagstruct_puts(reply, e->description);
1220 pa_tagstruct_puts(reply, e->icon);
1221 pa_tagstruct_putu32(reply, found_index);
1222 pa_tagstruct_putu32(reply, NUM_ROLES);
1224 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1225 pa_tagstruct_puts(reply, role_names[i]);
1226 pa_tagstruct_putu32(reply, e->priority[i]);
1240 case SUBCOMMAND_RENAME: {
1243 const char *device, *description;
1245 if (pa_tagstruct_gets(t, &device) < 0 ||
1246 pa_tagstruct_gets(t, &description) < 0)
1249 if (!device || !*device || !description || !*description)
1252 if ((e = entry_read(u, device))) {
1253 pa_xfree(e->description);
1254 e->description = pa_xstrdup(description);
1255 e->user_set_description = true;
1257 if (entry_write(u, (char *)device, e)) {
1258 apply_entry(u, device, e);
1263 pa_log_warn("Could not save device");
1268 pa_log_warn("Could not rename device %s, no entry in database", device);
1273 case SUBCOMMAND_DELETE:
1275 while (!pa_tagstruct_eof(t)) {
1279 if (pa_tagstruct_gets(t, &name) < 0)
1282 key.data = (char*) name;
1283 key.size = strlen(name);
1285 /** @todo: Reindex the priorities */
1286 pa_database_unset(u->database, &key);
1293 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1297 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1300 if ((u->do_routing = enable)) {
1301 /* Update our caches */
1302 update_highest_priority_device_indexes(u, "sink:", NULL);
1303 update_highest_priority_device_indexes(u, "source:", NULL);
1309 case SUBCOMMAND_REORDER: {
1313 uint32_t role_index, n_devices;
1315 bool done, sink_mode = true;
1316 struct device_t { uint32_t prio; char *device; };
1317 struct device_t *device;
1318 struct device_t **devices;
1319 uint32_t i, idx, offset;
1324 if (pa_tagstruct_gets(t, &role) < 0 ||
1325 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1329 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1332 /* Cycle through the devices given and make sure they exist */
1333 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1336 for (i = 0; i < n_devices; ++i) {
1338 if (pa_tagstruct_gets(t, &s) < 0) {
1339 while ((device = pa_hashmap_steal_first(h))) {
1340 pa_xfree(device->device);
1345 pa_log_error("Protocol error on reorder");
1349 /* Ensure this is a valid entry */
1350 if (!(e = entry_read(u, s))) {
1351 while ((device = pa_hashmap_steal_first(h))) {
1352 pa_xfree(device->device);
1357 pa_log_error("Client specified an unknown device in its reorder list.");
1364 sink_mode = (0 == strncmp("sink:", s, 5));
1365 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1366 while ((device = pa_hashmap_steal_first(h))) {
1367 pa_xfree(device->device);
1372 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1376 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1377 device = pa_xnew(struct device_t, 1);
1378 device->device = pa_xstrdup(s);
1379 if (pa_hashmap_put(h, device->device, device) == 0) {
1383 pa_xfree(device->device);
1388 /*pa_log_debug("Hashmap contents (received from client)");
1389 PA_HASHMAP_FOREACH(device, h, state) {
1390 pa_log_debug(" - %s (%d)", device->device, device->prio);
1393 /* Now cycle through our list and add all the devices.
1394 This has the effect of adding in any in our DB,
1395 not specified in the device list (and thus will be
1396 tacked on at the end) */
1398 done = !pa_database_first(u->database, &key, NULL);
1400 while (!done && idx < 256) {
1403 done = !pa_database_next(u->database, &key, &next_key, NULL);
1405 device = pa_xnew(struct device_t, 1);
1406 device->device = pa_xstrndup(key.data, key.size);
1407 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1408 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1410 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1411 if (pa_hashmap_put(h, device->device, device) == 0
1412 && (e = entry_read(u, device->device))) {
1413 /* We add offset on to the existing priority so that when we order, the
1414 existing entries are always lower priority than the new ones. */
1415 device->prio = (offset + e->priority[role_index]);
1419 pa_xfree(device->device);
1423 pa_xfree(device->device);
1427 pa_datum_free(&key);
1432 /*pa_log_debug("Hashmap contents (combined with database)");
1433 PA_HASHMAP_FOREACH(device, h, state) {
1434 pa_log_debug(" - %s (%d)", device->device, device->prio);
1437 /* Now we put all the entries in a simple list for sorting it. */
1438 n_devices = pa_hashmap_size(h);
1439 devices = pa_xnew(struct device_t *, n_devices);
1441 while ((device = pa_hashmap_steal_first(h))) {
1442 devices[idx++] = device;
1446 /* Simple bubble sort */
1447 for (i = 0; i < n_devices; ++i) {
1448 for (uint32_t j = i; j < n_devices; ++j) {
1449 if (devices[i]->prio > devices[j]->prio) {
1450 struct device_t *tmp;
1452 devices[i] = devices[j];
1458 /*pa_log_debug("Sorted device list");
1459 for (i = 0; i < n_devices; ++i) {
1460 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1463 /* Go through in order and write the new entry and cleanup our own list */
1466 for (i = 0; i < n_devices; ++i) {
1467 if ((e = entry_read(u, devices[i]->device))) {
1468 if (e->priority[role_index] == idx)
1471 e->priority[role_index] = idx;
1473 if (entry_write(u, (char *) devices[i]->device, e)) {
1481 pa_xfree(devices[i]->device);
1482 pa_xfree(devices[i]);
1491 route_sink_inputs(u, NULL);
1493 route_source_outputs(u, NULL);
1499 case SUBCOMMAND_SUBSCRIBE: {
1503 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1504 !pa_tagstruct_eof(t))
1508 pa_idxset_put(u->subscribed, c, NULL);
1510 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1519 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1525 pa_tagstruct_free(reply);
1530 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1535 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1539 struct prioritised_indexes {
1544 int pa__init(pa_module*m) {
1545 pa_modargs *ma = NULL;
1551 bool do_routing = false, on_hotplug = true, on_rescue = true;
1552 uint32_t total_devices;
1556 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1557 pa_log("Failed to parse module arguments");
1561 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1562 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1563 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1564 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1568 m->userdata = u = pa_xnew0(struct userdata, 1);
1571 u->do_routing = do_routing;
1572 u->on_hotplug = on_hotplug;
1573 u->on_rescue = on_rescue;
1574 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1576 u->protocol = pa_native_protocol_get(m->core);
1577 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1579 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);
1581 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);
1583 /* Used to handle device description management */
1584 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);
1585 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);
1587 /* The following slots are used to deal with routing */
1588 /* A little bit later than module-stream-restore, but before module-intended-roles */
1589 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);
1590 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);
1593 /* A little bit later than module-stream-restore, but before module-intended-roles */
1594 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);
1595 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);
1599 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, ... */
1600 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);
1601 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);
1604 if (!(state_path = pa_state_path(NULL, true)))
1607 if (!(u->database = pa_database_open(state_path, "device-manager", true, true))) {
1608 pa_xfree(state_path);
1612 pa_xfree(state_path);
1614 /* Attempt to inject the devices into the list in priority order */
1615 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1616 if (total_devices > 0 && total_devices < 128) {
1618 struct prioritised_indexes p_i[128];
1620 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1622 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1623 pa_log_debug("Found sink index %u", sink->index);
1624 p_i[i ].index = sink->index;
1625 p_i[i++].priority = sink->priority;
1627 /* Bubble sort it (only really useful for first time creation) */
1629 for (uint32_t j = 0; j < i; ++j)
1630 for (uint32_t k = 0; k < i; ++k)
1631 if (p_i[j].priority > p_i[k].priority) {
1632 struct prioritised_indexes tmp_pi = p_i[k];
1637 for (uint32_t j = 0; j < i; ++j)
1638 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1640 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1642 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1643 p_i[i ].index = source->index;
1644 p_i[i++].priority = source->priority;
1646 /* Bubble sort it (only really useful for first time creation) */
1648 for (uint32_t j = 0; j < i; ++j)
1649 for (uint32_t k = 0; k < i; ++k)
1650 if (p_i[j].priority > p_i[k].priority) {
1651 struct prioritised_indexes tmp_pi = p_i[k];
1656 for (uint32_t j = 0; j < i; ++j)
1657 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1659 else if (total_devices > 0) {
1660 /* This user has a *lot* of devices... */
1661 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1662 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1664 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1665 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1668 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1669 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1670 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1673 route_sink_inputs(u, NULL);
1674 route_source_outputs(u, NULL);
1676 #ifdef DUMP_DATABASE
1680 pa_modargs_free(ma);
1687 pa_modargs_free(ma);
1692 void pa__done(pa_module*m) {
1697 if (!(u = m->userdata))
1700 if (u->subscription)
1701 pa_subscription_free(u->subscription);
1703 if (u->sink_new_hook_slot)
1704 pa_hook_slot_free(u->sink_new_hook_slot);
1705 if (u->source_new_hook_slot)
1706 pa_hook_slot_free(u->source_new_hook_slot);
1708 if (u->sink_input_new_hook_slot)
1709 pa_hook_slot_free(u->sink_input_new_hook_slot);
1710 if (u->source_output_new_hook_slot)
1711 pa_hook_slot_free(u->source_output_new_hook_slot);
1713 if (u->sink_put_hook_slot)
1714 pa_hook_slot_free(u->sink_put_hook_slot);
1715 if (u->source_put_hook_slot)
1716 pa_hook_slot_free(u->source_put_hook_slot);
1718 if (u->sink_unlink_hook_slot)
1719 pa_hook_slot_free(u->sink_unlink_hook_slot);
1720 if (u->source_unlink_hook_slot)
1721 pa_hook_slot_free(u->source_unlink_hook_slot);
1723 if (u->connection_unlink_hook_slot)
1724 pa_hook_slot_free(u->connection_unlink_hook_slot);
1726 if (u->save_time_event)
1727 u->core->mainloop->time_free(u->save_time_event);
1730 pa_database_close(u->database);
1733 pa_native_protocol_remove_ext(u->protocol, m);
1734 pa_native_protocol_unref(u->protocol);
1738 pa_idxset_free(u->subscribed, NULL);