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,
153 /* Forward declarations */
155 static void dump_database(struct userdata *);
157 static void notify_subscribers(struct userdata *);
160 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
161 struct userdata *u = userdata;
167 pa_assert(e == u->save_time_event);
168 u->core->mainloop->time_free(u->save_time_event);
169 u->save_time_event = NULL;
171 pa_database_sync(u->database);
172 pa_log_info("Synced.");
179 static void trigger_save(struct userdata *u) {
183 notify_subscribers(u);
185 if (u->save_time_event)
188 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
191 static struct entry* entry_new(void) {
192 struct entry *r = pa_xnew0(struct entry, 1);
193 r->version = ENTRY_VERSION;
197 static void entry_free(struct entry* e) {
200 pa_xfree(e->description);
205 static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
214 t = pa_tagstruct_new(NULL, 0);
215 pa_tagstruct_putu8(t, e->version);
216 pa_tagstruct_puts(t, e->description);
217 pa_tagstruct_put_boolean(t, e->user_set_description);
218 pa_tagstruct_puts(t, e->icon);
219 for (uint8_t i=0; i<ROLE_MAX; ++i)
220 pa_tagstruct_putu32(t, e->priority[i]);
222 key.data = (char *) name;
223 key.size = strlen(name);
225 data.data = (void*)pa_tagstruct_data(t, &data.size);
227 r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
229 pa_tagstruct_free(t);
234 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
236 #define LEGACY_ENTRY_VERSION 1
237 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
238 struct legacy_entry {
240 char description[PA_NAME_MAX];
241 pa_bool_t user_set_description;
242 char icon[PA_NAME_MAX];
243 role_indexes_t priority;
245 struct legacy_entry *le;
251 if (data->size != sizeof(struct legacy_entry)) {
252 pa_log_debug("Size does not match.");
256 le = (struct legacy_entry*)data->data;
258 if (le->version != LEGACY_ENTRY_VERSION) {
259 pa_log_debug("Version mismatch.");
263 if (!memchr(le->description, 0, sizeof(le->description))) {
264 pa_log_warn("Description has missing NUL byte.");
268 if (!memchr(le->icon, 0, sizeof(le->icon))) {
269 pa_log_warn("Icon has missing NUL byte.");
274 e->description = pa_xstrdup(le->description);
275 e->icon = pa_xstrdup(le->icon);
280 static struct entry* entry_read(struct userdata *u, const char *name) {
282 struct entry *e = NULL;
283 pa_tagstruct *t = NULL;
284 const char *description, *icon;
289 key.data = (char*) name;
290 key.size = strlen(name);
294 if (!pa_database_get(u->database, &key, &data))
297 t = pa_tagstruct_new(data.data, data.size);
300 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
301 e->version > ENTRY_VERSION ||
302 pa_tagstruct_gets(t, &description) < 0 ||
303 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
304 pa_tagstruct_gets(t, &icon) < 0) {
309 e->description = pa_xstrdup(description);
310 e->icon = pa_xstrdup(icon);
312 for (uint8_t i=0; i<ROLE_MAX; ++i) {
313 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
317 if (!pa_tagstruct_eof(t))
320 pa_tagstruct_free(t);
321 pa_datum_free(&data);
326 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
331 pa_tagstruct_free(t);
333 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
334 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
335 if ((e = legacy_entry_read(u, &data))) {
336 pa_log_debug("Success. Saving new format for key: %s", name);
337 if (entry_write(u, name, e))
339 pa_datum_free(&data);
342 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
345 pa_datum_free(&data);
350 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
356 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
357 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
359 pa_log_debug(" %s No sink specified", human);
362 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
363 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
365 pa_log_debug(" %s No source specified", human);
369 static void dump_database(struct userdata *u) {
375 done = !pa_database_first(u->database, &key, NULL);
377 pa_log_debug("Dumping database");
383 done = !pa_database_next(u->database, &key, &next_key, NULL);
385 name = pa_xstrndup(key.data, key.size);
387 if ((e = entry_read(u, name))) {
388 pa_log_debug(" Got entry: %s", name);
389 pa_log_debug(" Description: %s", e->description);
390 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
391 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
392 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
393 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
404 pa_log_debug(" Highest priority devices per-role:");
406 pa_log_debug(" Sinks:");
407 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
409 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
410 strncpy(name, role_names[role], len);
411 for (int i = len+1; i < 12; ++i) name[i] = ' ';
412 name[len] = ':'; name[0] -= 32; name[12] = '\0';
413 dump_database_helper(u, role, name, TRUE);
416 pa_log_debug(" Sources:");
417 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
419 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
420 strncpy(name, role_names[role], len);
421 for (int i = len+1; i < 12; ++i) name[i] = ' ';
422 name[len] = ':'; name[0] -= 32; name[12] = '\0';
423 dump_database_helper(u, role, name, FALSE);
427 pa_log_debug("Completed database dump");
431 static void notify_subscribers(struct userdata *u) {
433 pa_native_connection *c;
438 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
441 t = pa_tagstruct_new(NULL, 0);
442 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
443 pa_tagstruct_putu32(t, 0);
444 pa_tagstruct_putu32(t, u->module->index);
445 pa_tagstruct_puts(t, u->module->name);
446 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
448 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
452 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
457 if (!pa_streq(a->description, b->description)
458 || a->user_set_description != b->user_set_description
459 || !pa_streq(a->icon, b->icon))
462 for (int i=0; i < NUM_ROLES; ++i)
463 if (a->priority[i] != b->priority[i])
469 static char *get_name(const char *key, const char *prefix) {
472 if (strncmp(key, prefix, strlen(prefix)))
475 t = pa_xstrdup(key + strlen(prefix));
479 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
487 if ((old = entry_read(u, name))) {
489 entry->description = pa_xstrdup(old->description);
490 entry->icon = pa_xstrdup(old->icon);
492 /* This is a new device, so make sure we write it's priority list correctly */
493 role_indexes_t max_priority;
497 pa_zero(max_priority);
498 done = !pa_database_first(u->database, &key, NULL);
500 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
504 done = !pa_database_next(u->database, &key, &next_key, NULL);
506 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
510 name2 = pa_xstrndup(key.data, key.size);
512 if ((e = entry_read(u, name2))) {
513 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
514 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
526 /* Actually initialise our entry now we've calculated it */
527 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
528 entry->priority[i] = max_priority[i] + 1;
530 entry->user_set_description = FALSE;
536 static uint32_t get_role_index(const char* role) {
539 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
540 if (strcmp(role, role_names[i]) == 0)
543 return PA_INVALID_INDEX;
546 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
547 role_indexes_t *indexes, highest_priority_available;
549 pa_bool_t done, sink_mode;
554 sink_mode = (strcmp(prefix, "sink:") == 0);
557 indexes = &u->preferred_sinks;
559 indexes = &u->preferred_sources;
561 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
562 (*indexes)[i] = PA_INVALID_INDEX;
564 pa_zero(highest_priority_available);
566 done = !pa_database_first(u->database, &key, NULL);
568 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
572 done = !pa_database_next(u->database, &key, &next_key, NULL);
574 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
575 char *name, *device_name;
578 name = pa_xstrndup(key.data, key.size);
579 pa_assert_se(device_name = get_name(name, prefix));
581 if ((e = entry_read(u, name))) {
582 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
583 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
584 /* We've found a device with a higher priority than that we've currently got,
585 so see if it is currently available or not and update our list */
587 pa_bool_t found = FALSE;
592 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
593 if ((pa_sink*) ignore_device == sink)
595 if (strcmp(sink->name, device_name) == 0) {
597 idx = sink->index; /* Is this needed? */
604 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
605 if ((pa_source*) ignore_device == source)
607 if (strcmp(source->name, device_name) == 0) {
609 idx = source->index; /* Is this needed? */
615 highest_priority_available[i] = e->priority[i];
626 pa_xfree(device_name);
635 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
637 uint32_t role_index, device_index;
641 pa_assert(u->do_routing);
646 /* Skip this if it is already in the process of being moved anyway */
650 /* It might happen that a stream and a sink are set up at the
651 same time, in which case we want to make sure we don't
652 interfere with that */
653 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
656 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
657 role_index = get_role_index("none");
659 role_index = get_role_index(role);
661 if (PA_INVALID_INDEX == role_index)
664 device_index = u->preferred_sinks[role_index];
665 if (PA_INVALID_INDEX == device_index)
668 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
671 if (si->sink != sink)
672 pa_sink_input_move_to(si, sink, FALSE);
675 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
684 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
686 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
687 route_sink_input(u, si);
693 static void route_source_output(struct userdata *u, pa_source_output *so) {
695 uint32_t role_index, device_index;
699 pa_assert(u->do_routing);
704 if (so->direct_on_input)
707 /* Skip this if it is already in the process of being moved anyway */
711 /* It might happen that a stream and a source are set up at the
712 same time, in which case we want to make sure we don't
713 interfere with that */
714 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
717 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
718 role_index = get_role_index("none");
720 role_index = get_role_index(role);
722 if (PA_INVALID_INDEX == role_index)
725 device_index = u->preferred_sources[role_index];
726 if (PA_INVALID_INDEX == device_index)
729 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
732 if (so->source != source)
733 pa_source_output_move_to(so, source, FALSE);
736 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
737 pa_source_output *so;
745 update_highest_priority_device_indexes(u, "source:", ignore_source);
747 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
748 route_source_output(u, so);
754 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
755 struct userdata *u = userdata;
756 struct entry *entry, *old = NULL;
762 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
763 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
764 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
765 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
767 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
768 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
769 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
770 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
773 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
778 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
781 /* The role may change mid-stream, so we reroute */
782 route_sink_input(u, si);
785 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
786 pa_source_output *so;
790 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
793 /* The role may change mid-stream, so we reroute */
794 route_source_output(u, so);
797 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
800 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
804 name = pa_sprintf_malloc("sink:%s", sink->name);
806 old = load_or_initialize_entry(u, entry, name, "sink:");
808 if (!entry->user_set_description) {
809 pa_xfree(entry->description);
810 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
811 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
812 /* Warning: If two modules fight over the description, this could cause an infinite loop.
813 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
814 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
815 the description, this will fail... */
816 pa_sink_set_description(sink, entry->description);
819 pa_xfree(entry->icon);
820 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
822 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
825 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
827 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
830 if (source->monitor_of)
834 name = pa_sprintf_malloc("source:%s", source->name);
836 old = load_or_initialize_entry(u, entry, name, "source:");
838 if (!entry->user_set_description) {
839 pa_xfree(entry->description);
840 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
841 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
842 /* Warning: If two modules fight over the description, this could cause an infinite loop.
843 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
844 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
845 the description, this will fail... */
846 pa_source_set_description(source, entry->description);
849 pa_xfree(entry->icon);
850 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
852 pa_assert_not_reached();
859 if (entries_equal(old, entry)) {
870 pa_log_info("Storing device %s.", name);
872 if (entry_write(u, name, entry))
875 pa_log_warn("Could not save device");;
881 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
889 name = pa_sprintf_malloc("sink:%s", new_data->name);
891 if ((e = entry_read(u, name))) {
892 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
893 pa_log_info("Restoring description for sink %s.", new_data->name);
894 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
905 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
913 name = pa_sprintf_malloc("source:%s", new_data->name);
915 if ((e = entry_read(u, name))) {
916 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
917 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
918 pa_log_info("Restoring description for source %s.", new_data->name);
919 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
930 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
942 pa_log_debug("Not restoring device for stream because already set.");
944 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
945 role_index = get_role_index("none");
947 role_index = get_role_index(role);
949 if (PA_INVALID_INDEX != role_index) {
950 uint32_t device_index;
952 device_index = u->preferred_sinks[role_index];
953 if (PA_INVALID_INDEX != device_index) {
956 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
957 if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
958 pa_log_debug("Not restoring device for stream because no supported format was found");
967 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
978 if (new_data->direct_on_input)
981 if (new_data->source)
982 pa_log_debug("Not restoring device for stream because already set.");
984 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
985 role_index = get_role_index("none");
987 role_index = get_role_index(role);
989 if (PA_INVALID_INDEX != role_index) {
990 uint32_t device_index;
992 device_index = u->preferred_sources[role_index];
993 if (PA_INVALID_INDEX != device_index) {
996 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
997 if (!pa_source_output_new_data_set_source(new_data, source, FALSE))
998 pa_log_debug("Not restoring device for stream because no supported format was found");
1007 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1010 pa_assert(u->core == c);
1011 pa_assert(u->on_hotplug);
1013 notify_subscribers(u);
1015 return route_sink_inputs(u, NULL);
1018 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1021 pa_assert(u->core == c);
1022 pa_assert(u->on_hotplug);
1024 notify_subscribers(u);
1026 return route_source_outputs(u, NULL);
1029 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1033 pa_assert(u->core == c);
1034 pa_assert(u->on_rescue);
1036 /* There's no point in doing anything if the core is shut down anyway */
1037 if (c->state == PA_CORE_SHUTDOWN)
1040 notify_subscribers(u);
1042 return route_sink_inputs(u, sink);
1045 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1049 pa_assert(u->core == c);
1050 pa_assert(u->on_rescue);
1052 /* There's no point in doing anything if the core is shut down anyway */
1053 if (c->state == PA_CORE_SHUTDOWN)
1056 notify_subscribers(u);
1058 return route_source_outputs(u, source);
1062 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1070 if (!e->user_set_description)
1073 if ((n = get_name(name, "sink:"))) {
1075 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1076 if (!pa_streq(s->name, n)) {
1080 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1081 pa_sink_set_description(s, e->description);
1085 else if ((n = get_name(name, "source:"))) {
1087 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1088 if (!pa_streq(s->name, n)) {
1092 if (s->monitor_of) {
1093 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1097 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1098 pa_source_set_description(s, e->description);
1105 #define EXT_VERSION 1
1107 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1110 pa_tagstruct *reply = NULL;
1119 if (pa_tagstruct_getu32(t, &command) < 0)
1122 reply = pa_tagstruct_new(NULL, 0);
1123 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1124 pa_tagstruct_putu32(reply, tag);
1127 case SUBCOMMAND_TEST: {
1128 if (!pa_tagstruct_eof(t))
1131 pa_tagstruct_putu32(reply, EXT_VERSION);
1135 case SUBCOMMAND_READ: {
1139 if (!pa_tagstruct_eof(t))
1142 done = !pa_database_first(u->database, &key, NULL);
1149 done = !pa_database_next(u->database, &key, &next_key, NULL);
1151 name = pa_xstrndup(key.data, key.size);
1152 pa_datum_free(&key);
1154 if ((e = entry_read(u, name))) {
1157 uint32_t found_index = PA_INVALID_INDEX;
1159 if ((device_name = get_name(name, "sink:"))) {
1161 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1162 if (strcmp(s->name, device_name) == 0) {
1163 found_index = s->index;
1167 pa_xfree(device_name);
1168 } else if ((device_name = get_name(name, "source:"))) {
1170 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1171 if (strcmp(s->name, device_name) == 0) {
1172 found_index = s->index;
1176 pa_xfree(device_name);
1179 pa_tagstruct_puts(reply, name);
1180 pa_tagstruct_puts(reply, e->description);
1181 pa_tagstruct_puts(reply, e->icon);
1182 pa_tagstruct_putu32(reply, found_index);
1183 pa_tagstruct_putu32(reply, NUM_ROLES);
1185 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1186 pa_tagstruct_puts(reply, role_names[i]);
1187 pa_tagstruct_putu32(reply, e->priority[i]);
1201 case SUBCOMMAND_RENAME: {
1204 const char *device, *description;
1206 if (pa_tagstruct_gets(t, &device) < 0 ||
1207 pa_tagstruct_gets(t, &description) < 0)
1210 if (!device || !*device || !description || !*description)
1213 if ((e = entry_read(u, device))) {
1214 pa_xfree(e->description);
1215 e->description = pa_xstrdup(description);
1216 e->user_set_description = TRUE;
1218 if (entry_write(u, (char *)device, e)) {
1219 apply_entry(u, device, e);
1224 pa_log_warn("Could not save device");
1229 pa_log_warn("Could not rename device %s, no entry in database", device);
1234 case SUBCOMMAND_DELETE:
1236 while (!pa_tagstruct_eof(t)) {
1240 if (pa_tagstruct_gets(t, &name) < 0)
1243 key.data = (char*) name;
1244 key.size = strlen(name);
1246 /** @todo: Reindex the priorities */
1247 pa_database_unset(u->database, &key);
1254 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1258 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1261 if ((u->do_routing = enable)) {
1262 /* Update our caches */
1263 update_highest_priority_device_indexes(u, "sink:", NULL);
1264 update_highest_priority_device_indexes(u, "source:", NULL);
1270 case SUBCOMMAND_REORDER: {
1274 uint32_t role_index, n_devices;
1276 pa_bool_t done, sink_mode = TRUE;
1277 struct device_t { uint32_t prio; char *device; };
1278 struct device_t *device;
1279 struct device_t **devices;
1280 uint32_t i, idx, offset;
1285 if (pa_tagstruct_gets(t, &role) < 0 ||
1286 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1290 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1293 /* Cycle through the devices given and make sure they exist */
1294 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1297 for (i = 0; i < n_devices; ++i) {
1299 if (pa_tagstruct_gets(t, &s) < 0) {
1300 while ((device = pa_hashmap_steal_first(h))) {
1301 pa_xfree(device->device);
1305 pa_hashmap_free(h, NULL, NULL);
1306 pa_log_error("Protocol error on reorder");
1310 /* Ensure this is a valid entry */
1311 if (!(e = entry_read(u, s))) {
1312 while ((device = pa_hashmap_steal_first(h))) {
1313 pa_xfree(device->device);
1317 pa_hashmap_free(h, NULL, NULL);
1318 pa_log_error("Client specified an unknown device in it's reorder list.");
1325 sink_mode = (0 == strncmp("sink:", s, 5));
1326 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1327 while ((device = pa_hashmap_steal_first(h))) {
1328 pa_xfree(device->device);
1332 pa_hashmap_free(h, NULL, NULL);
1333 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1337 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1338 device = pa_xnew(struct device_t, 1);
1339 device->device = pa_xstrdup(s);
1340 if (pa_hashmap_put(h, device->device, device) == 0) {
1344 pa_xfree(device->device);
1349 /*pa_log_debug("Hashmap contents (received from client)");
1350 PA_HASHMAP_FOREACH(device, h, state) {
1351 pa_log_debug(" - %s (%d)", device->device, device->prio);
1354 /* Now cycle through our list and add all the devices.
1355 This has the effect of adding in any in our DB,
1356 not specified in the device list (and thus will be
1357 tacked on at the end) */
1359 done = !pa_database_first(u->database, &key, NULL);
1361 while (!done && idx < 256) {
1364 done = !pa_database_next(u->database, &key, &next_key, NULL);
1366 device = pa_xnew(struct device_t, 1);
1367 device->device = pa_xstrndup(key.data, key.size);
1368 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1369 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1371 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1372 if (pa_hashmap_put(h, device->device, device) == 0
1373 && (e = entry_read(u, device->device))) {
1374 /* We add offset on to the existing priority so that when we order, the
1375 existing entries are always lower priority than the new ones. */
1376 device->prio = (offset + e->priority[role_index]);
1380 pa_xfree(device->device);
1384 pa_xfree(device->device);
1388 pa_datum_free(&key);
1393 /*pa_log_debug("Hashmap contents (combined with database)");
1394 PA_HASHMAP_FOREACH(device, h, state) {
1395 pa_log_debug(" - %s (%d)", device->device, device->prio);
1398 /* Now we put all the entries in a simple list for sorting it. */
1399 n_devices = pa_hashmap_size(h);
1400 devices = pa_xnew(struct device_t *, n_devices);
1402 while ((device = pa_hashmap_steal_first(h))) {
1403 devices[idx++] = device;
1405 pa_hashmap_free(h, NULL, NULL);
1407 /* Simple bubble sort */
1408 for (i = 0; i < n_devices; ++i) {
1409 for (uint32_t j = i; j < n_devices; ++j) {
1410 if (devices[i]->prio > devices[j]->prio) {
1411 struct device_t *tmp;
1413 devices[i] = devices[j];
1419 /*pa_log_debug("Sorted device list");
1420 for (i = 0; i < n_devices; ++i) {
1421 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1424 /* Go through in order and write the new entry and cleanup our own list */
1427 for (i = 0; i < n_devices; ++i) {
1428 if ((e = entry_read(u, devices[i]->device))) {
1429 if (e->priority[role_index] == idx)
1432 e->priority[role_index] = idx;
1434 if (entry_write(u, (char *) devices[i]->device, e)) {
1442 pa_xfree(devices[i]->device);
1443 pa_xfree(devices[i]);
1452 route_sink_inputs(u, NULL);
1454 route_source_outputs(u, NULL);
1460 case SUBCOMMAND_SUBSCRIBE: {
1464 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1465 !pa_tagstruct_eof(t))
1469 pa_idxset_put(u->subscribed, c, NULL);
1471 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1480 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1486 pa_tagstruct_free(reply);
1491 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1496 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1500 struct prioritised_indexes {
1505 int pa__init(pa_module*m) {
1506 pa_modargs *ma = NULL;
1512 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1513 uint32_t total_devices;
1517 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1518 pa_log("Failed to parse module arguments");
1522 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1523 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1524 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1525 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1529 m->userdata = u = pa_xnew0(struct userdata, 1);
1532 u->do_routing = do_routing;
1533 u->on_hotplug = on_hotplug;
1534 u->on_rescue = on_rescue;
1535 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1537 u->protocol = pa_native_protocol_get(m->core);
1538 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1540 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);
1542 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);
1544 /* Used to handle device description management */
1545 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);
1546 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);
1548 /* The following slots are used to deal with routing */
1549 /* A little bit later than module-stream-restore, but before module-intended-roles */
1550 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);
1551 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);
1554 /* A little bit later than module-stream-restore, but before module-intended-roles */
1555 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);
1556 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);
1560 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1561 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);
1562 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);
1565 if (!(fname = pa_state_path("device-manager", TRUE)))
1568 if (!(u->database = pa_database_open(fname, TRUE))) {
1569 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1574 pa_log_info("Successfully opened database file '%s'.", fname);
1577 /* Attempt to inject the devices into the list in priority order */
1578 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1579 if (total_devices > 0 && total_devices < 128) {
1581 struct prioritised_indexes p_i[128];
1583 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1585 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1586 pa_log_debug("Found sink index %u", sink->index);
1587 p_i[i ].index = sink->index;
1588 p_i[i++].priority = sink->priority;
1590 /* Bubble sort it (only really useful for first time creation) */
1592 for (uint32_t j = 0; j < i; ++j)
1593 for (uint32_t k = 0; k < i; ++k)
1594 if (p_i[j].priority > p_i[k].priority) {
1595 struct prioritised_indexes tmp_pi = p_i[k];
1600 for (uint32_t j = 0; j < i; ++j)
1601 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1604 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1606 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1607 p_i[i ].index = source->index;
1608 p_i[i++].priority = source->priority;
1610 /* Bubble sort it (only really useful for first time creation) */
1612 for (uint32_t j = 0; j < i; ++j)
1613 for (uint32_t k = 0; k < i; ++k)
1614 if (p_i[j].priority > p_i[k].priority) {
1615 struct prioritised_indexes tmp_pi = p_i[k];
1620 for (uint32_t j = 0; j < i; ++j)
1621 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1623 else if (total_devices > 0) {
1624 /* This user has a *lot* of devices... */
1625 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1626 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1628 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1629 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1632 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1633 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1634 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1637 route_sink_inputs(u, NULL);
1638 route_source_outputs(u, NULL);
1640 #ifdef DUMP_DATABASE
1644 pa_modargs_free(ma);
1651 pa_modargs_free(ma);
1656 void pa__done(pa_module*m) {
1661 if (!(u = m->userdata))
1664 if (u->subscription)
1665 pa_subscription_free(u->subscription);
1667 if (u->sink_new_hook_slot)
1668 pa_hook_slot_free(u->sink_new_hook_slot);
1669 if (u->source_new_hook_slot)
1670 pa_hook_slot_free(u->source_new_hook_slot);
1672 if (u->sink_input_new_hook_slot)
1673 pa_hook_slot_free(u->sink_input_new_hook_slot);
1674 if (u->source_output_new_hook_slot)
1675 pa_hook_slot_free(u->source_output_new_hook_slot);
1677 if (u->sink_put_hook_slot)
1678 pa_hook_slot_free(u->sink_put_hook_slot);
1679 if (u->source_put_hook_slot)
1680 pa_hook_slot_free(u->source_put_hook_slot);
1682 if (u->sink_unlink_hook_slot)
1683 pa_hook_slot_free(u->sink_unlink_hook_slot);
1684 if (u->source_unlink_hook_slot)
1685 pa_hook_slot_free(u->source_unlink_hook_slot);
1687 if (u->connection_unlink_hook_slot)
1688 pa_hook_slot_free(u->connection_unlink_hook_slot);
1690 if (u->save_time_event)
1691 u->core->mainloop->time_free(u->save_time_event);
1694 pa_database_close(u->database);
1697 pa_native_protocol_remove_ext(u->protocol, m);
1698 pa_native_protocol_unref(u->protocol);
1702 pa_idxset_free(u->subscribed, NULL, NULL);