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;
123 pa_bool_t on_hotplug;
125 pa_bool_t do_routing;
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
131 #define ENTRY_VERSION 1
136 pa_bool_t 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 pa_bool_t 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 pa_bool_t 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 (!memchr(le->icon, 0, sizeof(le->icon))) {
267 pa_log_warn("Icon has missing NUL byte.");
272 e->description = pa_xstrdup(le->description);
273 e->icon = pa_xstrdup(le->icon);
278 static struct entry* entry_read(struct userdata *u, const char *name) {
280 struct entry *e = NULL;
281 pa_tagstruct *t = NULL;
282 const char *description, *icon;
287 key.data = (char*) name;
288 key.size = strlen(name);
292 if (!pa_database_get(u->database, &key, &data))
295 t = pa_tagstruct_new(data.data, data.size);
298 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
299 e->version > ENTRY_VERSION ||
300 pa_tagstruct_gets(t, &description) < 0 ||
301 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
302 pa_tagstruct_gets(t, &icon) < 0) {
307 e->description = pa_xstrdup(description);
308 e->icon = pa_xstrdup(icon);
310 for (uint8_t i=0; i<ROLE_MAX; ++i) {
311 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
315 if (!pa_tagstruct_eof(t))
318 pa_tagstruct_free(t);
319 pa_datum_free(&data);
324 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
329 pa_tagstruct_free(t);
331 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
332 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
333 if ((e = legacy_entry_read(u, &data))) {
334 pa_log_debug("Success. Saving new format for key: %s", name);
335 if (entry_write(u, name, e))
337 pa_datum_free(&data);
340 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
343 pa_datum_free(&data);
348 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
354 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
355 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
357 pa_log_debug(" %s No sink specified", human);
360 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
361 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
363 pa_log_debug(" %s No source specified", human);
367 static void dump_database(struct userdata *u) {
373 done = !pa_database_first(u->database, &key, NULL);
375 pa_log_debug("Dumping database");
381 done = !pa_database_next(u->database, &key, &next_key, NULL);
383 name = pa_xstrndup(key.data, key.size);
385 if ((e = entry_read(u, name))) {
386 pa_log_debug(" Got entry: %s", name);
387 pa_log_debug(" Description: %s", e->description);
388 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
389 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
390 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
391 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
402 pa_log_debug(" Highest priority devices per-role:");
404 pa_log_debug(" Sinks:");
405 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
407 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
408 strncpy(name, role_names[role], len);
409 for (int i = len+1; i < 12; ++i) name[i] = ' ';
410 name[len] = ':'; name[0] -= 32; name[12] = '\0';
411 dump_database_helper(u, role, name, TRUE);
414 pa_log_debug(" Sources:");
415 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
417 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
418 strncpy(name, role_names[role], len);
419 for (int i = len+1; i < 12; ++i) name[i] = ' ';
420 name[len] = ':'; name[0] -= 32; name[12] = '\0';
421 dump_database_helper(u, role, name, FALSE);
425 pa_log_debug("Completed database dump");
429 static void notify_subscribers(struct userdata *u) {
431 pa_native_connection *c;
436 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
439 t = pa_tagstruct_new(NULL, 0);
440 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
441 pa_tagstruct_putu32(t, 0);
442 pa_tagstruct_putu32(t, u->module->index);
443 pa_tagstruct_puts(t, u->module->name);
444 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
446 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
450 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
455 if (!pa_streq(a->description, b->description)
456 || a->user_set_description != b->user_set_description
457 || !pa_streq(a->icon, b->icon))
460 for (int i=0; i < NUM_ROLES; ++i)
461 if (a->priority[i] != b->priority[i])
467 static char *get_name(const char *key, const char *prefix) {
470 if (strncmp(key, prefix, strlen(prefix)))
473 t = pa_xstrdup(key + strlen(prefix));
477 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
485 if ((old = entry_read(u, name))) {
487 entry->description = pa_xstrdup(old->description);
488 entry->icon = pa_xstrdup(old->icon);
490 /* This is a new device, so make sure we write it's priority list correctly */
491 role_indexes_t max_priority;
495 pa_zero(max_priority);
496 done = !pa_database_first(u->database, &key, NULL);
498 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
502 done = !pa_database_next(u->database, &key, &next_key, NULL);
504 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
508 name2 = pa_xstrndup(key.data, key.size);
510 if ((e = entry_read(u, name2))) {
511 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
512 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
524 /* Actually initialise our entry now we've calculated it */
525 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
526 entry->priority[i] = max_priority[i] + 1;
528 entry->user_set_description = FALSE;
534 static uint32_t get_role_index(const char* role) {
537 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
538 if (pa_streq(role, role_names[i]))
541 return PA_INVALID_INDEX;
544 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
545 role_indexes_t *indexes, highest_priority_available;
547 pa_bool_t done, sink_mode;
552 sink_mode = pa_streq(prefix, "sink:");
555 indexes = &u->preferred_sinks;
557 indexes = &u->preferred_sources;
559 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
560 (*indexes)[i] = PA_INVALID_INDEX;
562 pa_zero(highest_priority_available);
564 done = !pa_database_first(u->database, &key, NULL);
566 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
570 done = !pa_database_next(u->database, &key, &next_key, NULL);
572 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
573 char *name, *device_name;
576 name = pa_xstrndup(key.data, key.size);
577 pa_assert_se(device_name = get_name(name, prefix));
579 if ((e = entry_read(u, name))) {
580 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
581 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
582 /* We've found a device with a higher priority than that we've currently got,
583 so see if it is currently available or not and update our list */
585 pa_bool_t found = FALSE;
590 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
591 if ((pa_sink*) ignore_device == sink)
593 if (pa_streq(sink->name, device_name)) {
595 idx = sink->index; /* Is this needed? */
602 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
603 if ((pa_source*) ignore_device == source)
605 if (pa_streq(source->name, device_name)) {
607 idx = source->index; /* Is this needed? */
613 highest_priority_available[i] = e->priority[i];
624 pa_xfree(device_name);
632 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
634 uint32_t role_index, device_index;
638 pa_assert(u->do_routing);
643 /* Skip this if it is already in the process of being moved anyway */
647 /* It might happen that a stream and a sink are set up at the
648 same time, in which case we want to make sure we don't
649 interfere with that */
650 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
653 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
654 role_index = get_role_index("none");
656 role_index = get_role_index(role);
658 if (PA_INVALID_INDEX == role_index)
661 device_index = u->preferred_sinks[role_index];
662 if (PA_INVALID_INDEX == device_index)
665 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
668 if (si->sink != sink)
669 pa_sink_input_move_to(si, sink, FALSE);
672 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
681 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
683 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
684 route_sink_input(u, si);
690 static void route_source_output(struct userdata *u, pa_source_output *so) {
692 uint32_t role_index, device_index;
696 pa_assert(u->do_routing);
701 if (so->direct_on_input)
704 /* Skip this if it is already in the process of being moved anyway */
708 /* It might happen that a stream and a source are set up at the
709 same time, in which case we want to make sure we don't
710 interfere with that */
711 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
714 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
715 role_index = get_role_index("none");
717 role_index = get_role_index(role);
719 if (PA_INVALID_INDEX == role_index)
722 device_index = u->preferred_sources[role_index];
723 if (PA_INVALID_INDEX == device_index)
726 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
729 if (so->source != source)
730 pa_source_output_move_to(so, source, FALSE);
733 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
734 pa_source_output *so;
742 update_highest_priority_device_indexes(u, "source:", ignore_source);
744 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
745 route_source_output(u, so);
751 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
752 struct userdata *u = userdata;
753 struct entry *entry, *old = NULL;
759 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
760 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
761 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
762 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
764 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
765 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
766 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
767 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
770 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
775 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
778 /* The role may change mid-stream, so we reroute */
779 route_sink_input(u, si);
782 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
783 pa_source_output *so;
787 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
790 /* The role may change mid-stream, so we reroute */
791 route_source_output(u, so);
794 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
797 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
801 name = pa_sprintf_malloc("sink:%s", sink->name);
803 old = load_or_initialize_entry(u, entry, name, "sink:");
805 if (!entry->user_set_description) {
806 pa_xfree(entry->description);
807 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
808 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
809 /* Warning: If two modules fight over the description, this could cause an infinite loop.
810 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
811 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
812 the description, this will fail... */
813 pa_sink_set_description(sink, entry->description);
816 pa_xfree(entry->icon);
817 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
819 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
822 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
824 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
827 if (source->monitor_of)
831 name = pa_sprintf_malloc("source:%s", source->name);
833 old = load_or_initialize_entry(u, entry, name, "source:");
835 if (!entry->user_set_description) {
836 pa_xfree(entry->description);
837 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
838 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
839 /* Warning: If two modules fight over the description, this could cause an infinite loop.
840 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
841 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
842 the description, this will fail... */
843 pa_source_set_description(source, entry->description);
846 pa_xfree(entry->icon);
847 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
849 pa_assert_not_reached();
856 if (entries_equal(old, entry)) {
867 pa_log_info("Storing device %s.", name);
869 if (entry_write(u, name, entry))
872 pa_log_warn("Could not save device");;
878 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
886 name = pa_sprintf_malloc("sink:%s", new_data->name);
888 if ((e = entry_read(u, name))) {
889 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
890 pa_log_info("Restoring description for sink %s.", new_data->name);
891 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
902 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
910 name = pa_sprintf_malloc("source:%s", new_data->name);
912 if ((e = entry_read(u, name))) {
913 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
914 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
915 pa_log_info("Restoring description for source %s.", new_data->name);
916 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
927 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
939 pa_log_debug("Not restoring device for stream because already set.");
941 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
942 role_index = get_role_index("none");
944 role_index = get_role_index(role);
946 if (PA_INVALID_INDEX != role_index) {
947 uint32_t device_index;
949 device_index = u->preferred_sinks[role_index];
950 if (PA_INVALID_INDEX != device_index) {
953 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
954 if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
955 pa_log_debug("Not restoring device for stream because no supported format was found");
964 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
975 if (new_data->direct_on_input)
978 if (new_data->source)
979 pa_log_debug("Not restoring device for stream because already set.");
981 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
982 role_index = get_role_index("none");
984 role_index = get_role_index(role);
986 if (PA_INVALID_INDEX != role_index) {
987 uint32_t device_index;
989 device_index = u->preferred_sources[role_index];
990 if (PA_INVALID_INDEX != device_index) {
993 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
994 if (!pa_source_output_new_data_set_source(new_data, source, FALSE))
995 pa_log_debug("Not restoring device for stream because no supported format was found");
1003 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1006 pa_assert(u->core == c);
1007 pa_assert(u->on_hotplug);
1009 notify_subscribers(u);
1011 return route_sink_inputs(u, NULL);
1014 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1017 pa_assert(u->core == c);
1018 pa_assert(u->on_hotplug);
1020 notify_subscribers(u);
1022 return route_source_outputs(u, NULL);
1025 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1029 pa_assert(u->core == c);
1030 pa_assert(u->on_rescue);
1032 /* There's no point in doing anything if the core is shut down anyway */
1033 if (c->state == PA_CORE_SHUTDOWN)
1036 notify_subscribers(u);
1038 return route_sink_inputs(u, sink);
1041 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1045 pa_assert(u->core == c);
1046 pa_assert(u->on_rescue);
1048 /* There's no point in doing anything if the core is shut down anyway */
1049 if (c->state == PA_CORE_SHUTDOWN)
1052 notify_subscribers(u);
1054 return route_source_outputs(u, source);
1057 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1065 if (!e->user_set_description)
1068 if ((n = get_name(name, "sink:"))) {
1070 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1071 if (!pa_streq(s->name, n)) {
1075 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1076 pa_sink_set_description(s, e->description);
1080 else if ((n = get_name(name, "source:"))) {
1082 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1083 if (!pa_streq(s->name, n)) {
1087 if (s->monitor_of) {
1088 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1092 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1093 pa_source_set_description(s, e->description);
1099 #define EXT_VERSION 1
1101 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1104 pa_tagstruct *reply = NULL;
1113 if (pa_tagstruct_getu32(t, &command) < 0)
1116 reply = pa_tagstruct_new(NULL, 0);
1117 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1118 pa_tagstruct_putu32(reply, tag);
1121 case SUBCOMMAND_TEST: {
1122 if (!pa_tagstruct_eof(t))
1125 pa_tagstruct_putu32(reply, EXT_VERSION);
1129 case SUBCOMMAND_READ: {
1133 if (!pa_tagstruct_eof(t))
1136 done = !pa_database_first(u->database, &key, NULL);
1143 done = !pa_database_next(u->database, &key, &next_key, NULL);
1145 name = pa_xstrndup(key.data, key.size);
1146 pa_datum_free(&key);
1148 if ((e = entry_read(u, name))) {
1151 uint32_t found_index = PA_INVALID_INDEX;
1153 if ((device_name = get_name(name, "sink:"))) {
1155 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1156 if (pa_streq(s->name, device_name)) {
1157 found_index = s->index;
1161 pa_xfree(device_name);
1162 } else if ((device_name = get_name(name, "source:"))) {
1164 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1165 if (pa_streq(s->name, device_name)) {
1166 found_index = s->index;
1170 pa_xfree(device_name);
1173 pa_tagstruct_puts(reply, name);
1174 pa_tagstruct_puts(reply, e->description);
1175 pa_tagstruct_puts(reply, e->icon);
1176 pa_tagstruct_putu32(reply, found_index);
1177 pa_tagstruct_putu32(reply, NUM_ROLES);
1179 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1180 pa_tagstruct_puts(reply, role_names[i]);
1181 pa_tagstruct_putu32(reply, e->priority[i]);
1195 case SUBCOMMAND_RENAME: {
1198 const char *device, *description;
1200 if (pa_tagstruct_gets(t, &device) < 0 ||
1201 pa_tagstruct_gets(t, &description) < 0)
1204 if (!device || !*device || !description || !*description)
1207 if ((e = entry_read(u, device))) {
1208 pa_xfree(e->description);
1209 e->description = pa_xstrdup(description);
1210 e->user_set_description = TRUE;
1212 if (entry_write(u, (char *)device, e)) {
1213 apply_entry(u, device, e);
1218 pa_log_warn("Could not save device");
1223 pa_log_warn("Could not rename device %s, no entry in database", device);
1228 case SUBCOMMAND_DELETE:
1230 while (!pa_tagstruct_eof(t)) {
1234 if (pa_tagstruct_gets(t, &name) < 0)
1237 key.data = (char*) name;
1238 key.size = strlen(name);
1240 /** @todo: Reindex the priorities */
1241 pa_database_unset(u->database, &key);
1248 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1252 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1255 if ((u->do_routing = enable)) {
1256 /* Update our caches */
1257 update_highest_priority_device_indexes(u, "sink:", NULL);
1258 update_highest_priority_device_indexes(u, "source:", NULL);
1264 case SUBCOMMAND_REORDER: {
1268 uint32_t role_index, n_devices;
1270 pa_bool_t done, sink_mode = TRUE;
1271 struct device_t { uint32_t prio; char *device; };
1272 struct device_t *device;
1273 struct device_t **devices;
1274 uint32_t i, idx, offset;
1279 if (pa_tagstruct_gets(t, &role) < 0 ||
1280 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1284 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1287 /* Cycle through the devices given and make sure they exist */
1288 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1291 for (i = 0; i < n_devices; ++i) {
1293 if (pa_tagstruct_gets(t, &s) < 0) {
1294 while ((device = pa_hashmap_steal_first(h))) {
1295 pa_xfree(device->device);
1299 pa_hashmap_free(h, NULL);
1300 pa_log_error("Protocol error on reorder");
1304 /* Ensure this is a valid entry */
1305 if (!(e = entry_read(u, s))) {
1306 while ((device = pa_hashmap_steal_first(h))) {
1307 pa_xfree(device->device);
1311 pa_hashmap_free(h, NULL);
1312 pa_log_error("Client specified an unknown device in it's reorder list.");
1319 sink_mode = (0 == strncmp("sink:", s, 5));
1320 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1321 while ((device = pa_hashmap_steal_first(h))) {
1322 pa_xfree(device->device);
1326 pa_hashmap_free(h, NULL);
1327 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1331 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1332 device = pa_xnew(struct device_t, 1);
1333 device->device = pa_xstrdup(s);
1334 if (pa_hashmap_put(h, device->device, device) == 0) {
1338 pa_xfree(device->device);
1343 /*pa_log_debug("Hashmap contents (received from client)");
1344 PA_HASHMAP_FOREACH(device, h, state) {
1345 pa_log_debug(" - %s (%d)", device->device, device->prio);
1348 /* Now cycle through our list and add all the devices.
1349 This has the effect of adding in any in our DB,
1350 not specified in the device list (and thus will be
1351 tacked on at the end) */
1353 done = !pa_database_first(u->database, &key, NULL);
1355 while (!done && idx < 256) {
1358 done = !pa_database_next(u->database, &key, &next_key, NULL);
1360 device = pa_xnew(struct device_t, 1);
1361 device->device = pa_xstrndup(key.data, key.size);
1362 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1363 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1365 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1366 if (pa_hashmap_put(h, device->device, device) == 0
1367 && (e = entry_read(u, device->device))) {
1368 /* We add offset on to the existing priority so that when we order, the
1369 existing entries are always lower priority than the new ones. */
1370 device->prio = (offset + e->priority[role_index]);
1374 pa_xfree(device->device);
1378 pa_xfree(device->device);
1382 pa_datum_free(&key);
1387 /*pa_log_debug("Hashmap contents (combined with database)");
1388 PA_HASHMAP_FOREACH(device, h, state) {
1389 pa_log_debug(" - %s (%d)", device->device, device->prio);
1392 /* Now we put all the entries in a simple list for sorting it. */
1393 n_devices = pa_hashmap_size(h);
1394 devices = pa_xnew(struct device_t *, n_devices);
1396 while ((device = pa_hashmap_steal_first(h))) {
1397 devices[idx++] = device;
1399 pa_hashmap_free(h, NULL);
1401 /* Simple bubble sort */
1402 for (i = 0; i < n_devices; ++i) {
1403 for (uint32_t j = i; j < n_devices; ++j) {
1404 if (devices[i]->prio > devices[j]->prio) {
1405 struct device_t *tmp;
1407 devices[i] = devices[j];
1413 /*pa_log_debug("Sorted device list");
1414 for (i = 0; i < n_devices; ++i) {
1415 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1418 /* Go through in order and write the new entry and cleanup our own list */
1421 for (i = 0; i < n_devices; ++i) {
1422 if ((e = entry_read(u, devices[i]->device))) {
1423 if (e->priority[role_index] == idx)
1426 e->priority[role_index] = idx;
1428 if (entry_write(u, (char *) devices[i]->device, e)) {
1436 pa_xfree(devices[i]->device);
1437 pa_xfree(devices[i]);
1446 route_sink_inputs(u, NULL);
1448 route_source_outputs(u, NULL);
1454 case SUBCOMMAND_SUBSCRIBE: {
1458 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1459 !pa_tagstruct_eof(t))
1463 pa_idxset_put(u->subscribed, c, NULL);
1465 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1474 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1480 pa_tagstruct_free(reply);
1485 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1490 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1494 struct prioritised_indexes {
1499 int pa__init(pa_module*m) {
1500 pa_modargs *ma = NULL;
1506 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1507 uint32_t total_devices;
1511 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1512 pa_log("Failed to parse module arguments");
1516 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1517 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1518 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1519 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1523 m->userdata = u = pa_xnew0(struct userdata, 1);
1526 u->do_routing = do_routing;
1527 u->on_hotplug = on_hotplug;
1528 u->on_rescue = on_rescue;
1529 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1531 u->protocol = pa_native_protocol_get(m->core);
1532 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1534 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);
1536 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);
1538 /* Used to handle device description management */
1539 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);
1540 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);
1542 /* The following slots are used to deal with routing */
1543 /* A little bit later than module-stream-restore, but before module-intended-roles */
1544 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);
1545 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);
1548 /* A little bit later than module-stream-restore, but before module-intended-roles */
1549 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);
1550 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);
1554 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1555 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);
1556 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);
1559 if (!(fname = pa_state_path("device-manager", TRUE)))
1562 if (!(u->database = pa_database_open(fname, TRUE))) {
1563 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1568 pa_log_info("Successfully opened database file '%s'.", fname);
1571 /* Attempt to inject the devices into the list in priority order */
1572 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1573 if (total_devices > 0 && total_devices < 128) {
1575 struct prioritised_indexes p_i[128];
1577 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1579 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1580 pa_log_debug("Found sink index %u", sink->index);
1581 p_i[i ].index = sink->index;
1582 p_i[i++].priority = sink->priority;
1584 /* Bubble sort it (only really useful for first time creation) */
1586 for (uint32_t j = 0; j < i; ++j)
1587 for (uint32_t k = 0; k < i; ++k)
1588 if (p_i[j].priority > p_i[k].priority) {
1589 struct prioritised_indexes tmp_pi = p_i[k];
1594 for (uint32_t j = 0; j < i; ++j)
1595 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1597 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1599 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1600 p_i[i ].index = source->index;
1601 p_i[i++].priority = source->priority;
1603 /* Bubble sort it (only really useful for first time creation) */
1605 for (uint32_t j = 0; j < i; ++j)
1606 for (uint32_t k = 0; k < i; ++k)
1607 if (p_i[j].priority > p_i[k].priority) {
1608 struct prioritised_indexes tmp_pi = p_i[k];
1613 for (uint32_t j = 0; j < i; ++j)
1614 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1616 else if (total_devices > 0) {
1617 /* This user has a *lot* of devices... */
1618 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1619 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1621 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1622 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1625 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1626 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1627 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1630 route_sink_inputs(u, NULL);
1631 route_source_outputs(u, NULL);
1633 #ifdef DUMP_DATABASE
1637 pa_modargs_free(ma);
1644 pa_modargs_free(ma);
1649 void pa__done(pa_module*m) {
1654 if (!(u = m->userdata))
1657 if (u->subscription)
1658 pa_subscription_free(u->subscription);
1660 if (u->sink_new_hook_slot)
1661 pa_hook_slot_free(u->sink_new_hook_slot);
1662 if (u->source_new_hook_slot)
1663 pa_hook_slot_free(u->source_new_hook_slot);
1665 if (u->sink_input_new_hook_slot)
1666 pa_hook_slot_free(u->sink_input_new_hook_slot);
1667 if (u->source_output_new_hook_slot)
1668 pa_hook_slot_free(u->source_output_new_hook_slot);
1670 if (u->sink_put_hook_slot)
1671 pa_hook_slot_free(u->sink_put_hook_slot);
1672 if (u->source_put_hook_slot)
1673 pa_hook_slot_free(u->source_put_hook_slot);
1675 if (u->sink_unlink_hook_slot)
1676 pa_hook_slot_free(u->sink_unlink_hook_slot);
1677 if (u->source_unlink_hook_slot)
1678 pa_hook_slot_free(u->source_unlink_hook_slot);
1680 if (u->connection_unlink_hook_slot)
1681 pa_hook_slot_free(u->connection_unlink_hook_slot);
1683 if (u->save_time_event)
1684 u->core->mainloop->time_free(u->save_time_event);
1687 pa_database_close(u->database);
1690 pa_native_protocol_remove_ext(u->protocol, m);
1691 pa_native_protocol_unref(u->protocol);
1695 pa_idxset_free(u->subscribed, NULL);