Fix up according to Coding Style
[pulseaudio-mirror.git] / src / modules / module-device-manager.c
blob73b86a20102b454f85c2ec299e7934ebf7d49530
1 /***
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
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/volume.h>
37 #include <pulse/timeval.h>
38 #include <pulse/util.h>
39 #include <pulse/rtclock.h>
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/module.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/modargs.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/core-subscribe.h>
47 #include <pulsecore/sink-input.h>
48 #include <pulsecore/source-output.h>
49 #include <pulsecore/namereg.h>
50 #include <pulsecore/protocol-native.h>
51 #include <pulsecore/pstream.h>
52 #include <pulsecore/pstream-util.h>
53 #include <pulsecore/database.h>
55 #include "module-device-manager-symdef.h"
57 PA_MODULE_AUTHOR("Colin Guthrie");
58 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
59 PA_MODULE_VERSION(PACKAGE_VERSION);
60 PA_MODULE_LOAD_ONCE(TRUE);
61 PA_MODULE_USAGE(
62 "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
63 "on_hotplug=<When new device becomes available, recheck streams?> "
64 "on_rescue=<When device becomes unavailable, recheck streams?>");
66 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
67 #define DUMP_DATABASE
69 static const char* const valid_modargs[] = {
70 "do_routing",
71 "on_hotplug",
72 "on_rescue",
73 NULL
76 #define NUM_ROLES 9
77 enum {
78 ROLE_NONE,
79 ROLE_VIDEO,
80 ROLE_MUSIC,
81 ROLE_GAME,
82 ROLE_EVENT,
83 ROLE_PHONE,
84 ROLE_ANIMATION,
85 ROLE_PRODUCTION,
86 ROLE_A11Y,
89 typedef uint32_t role_indexes_t[NUM_ROLES];
91 static const char* role_names[NUM_ROLES] = {
92 "none",
93 "video",
94 "music",
95 "game",
96 "event",
97 "phone",
98 "animation",
99 "production",
100 "a11y",
103 struct userdata {
104 pa_core *core;
105 pa_module *module;
106 pa_subscription *subscription;
107 pa_hook_slot
108 *sink_new_hook_slot,
109 *source_new_hook_slot,
110 *sink_input_new_hook_slot,
111 *source_output_new_hook_slot,
112 *sink_put_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;
124 pa_bool_t on_rescue;
125 pa_bool_t do_routing;
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
131 #define ENTRY_VERSION 1
133 struct entry {
134 uint8_t version;
135 char description[PA_NAME_MAX];
136 pa_bool_t user_set_description;
137 char icon[PA_NAME_MAX];
138 role_indexes_t priority;
139 } PA_GCC_PACKED;
141 enum {
142 SUBCOMMAND_TEST,
143 SUBCOMMAND_READ,
144 SUBCOMMAND_RENAME,
145 SUBCOMMAND_DELETE,
146 SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
147 SUBCOMMAND_REORDER,
148 SUBCOMMAND_SUBSCRIBE,
149 SUBCOMMAND_EVENT
153 static struct entry* read_entry(struct userdata *u, const char *name) {
154 pa_datum key, data;
155 struct entry *e;
157 pa_assert(u);
158 pa_assert(name);
160 key.data = (char*) name;
161 key.size = strlen(name);
163 pa_zero(data);
165 if (!pa_database_get(u->database, &key, &data))
166 goto fail;
168 if (data.size != sizeof(struct entry)) {
169 pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
170 goto fail;
173 e = (struct entry*) data.data;
175 if (e->version != ENTRY_VERSION) {
176 pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
177 goto fail;
180 if (!memchr(e->description, 0, sizeof(e->description))) {
181 pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
182 goto fail;
185 if (!memchr(e->icon, 0, sizeof(e->icon))) {
186 pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name);
187 goto fail;
190 return e;
192 fail:
194 pa_datum_free(&data);
195 return NULL;
198 #ifdef DUMP_DATABASE
199 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
200 pa_assert(u);
201 pa_assert(human);
203 if (sink_mode) {
204 pa_sink *s;
205 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
206 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
207 else
208 pa_log_debug(" %s No sink specified", human);
209 } else {
210 pa_source *s;
211 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
212 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
213 else
214 pa_log_debug(" %s No source specified", human);
218 static void dump_database(struct userdata *u) {
219 pa_datum key;
220 pa_bool_t done;
222 pa_assert(u);
224 done = !pa_database_first(u->database, &key, NULL);
226 pa_log_debug("Dumping database");
227 while (!done) {
228 char *name;
229 struct entry *e;
230 pa_datum next_key;
232 done = !pa_database_next(u->database, &key, &next_key, NULL);
234 name = pa_xstrndup(key.data, key.size);
236 if ((e = read_entry(u, name))) {
237 pa_log_debug(" Got entry: %s", name);
238 pa_log_debug(" Description: %s", e->description);
239 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
240 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
241 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
242 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
243 pa_xfree(e);
246 pa_xfree(name);
248 pa_datum_free(&key);
249 key = next_key;
252 if (u->do_routing) {
253 pa_log_debug(" Highest priority devices per-role:");
255 pa_log_debug(" Sinks:");
256 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
257 char name[13];
258 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
259 strncpy(name, role_names[role], len);
260 for (int i = len+1; i < 12; ++i) name[i] = ' ';
261 name[len] = ':'; name[0] -= 32; name[12] = '\0';
262 dump_database_helper(u, role, name, TRUE);
265 pa_log_debug(" Sources:");
266 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
267 char name[13];
268 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
269 strncpy(name, role_names[role], len);
270 for (int i = len+1; i < 12; ++i) name[i] = ' ';
271 name[len] = ':'; name[0] -= 32; name[12] = '\0';
272 dump_database_helper(u, role, name, FALSE);
276 pa_log_debug("Completed database dump");
278 #endif
280 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
281 struct userdata *u = userdata;
283 pa_assert(a);
284 pa_assert(e);
285 pa_assert(u);
287 pa_assert(e == u->save_time_event);
288 u->core->mainloop->time_free(u->save_time_event);
289 u->save_time_event = NULL;
291 pa_database_sync(u->database);
292 pa_log_info("Synced.");
294 #ifdef DUMP_DATABASE
295 dump_database(u);
296 #endif
299 static void notify_subscribers(struct userdata *u) {
301 pa_native_connection *c;
302 uint32_t idx;
304 pa_assert(u);
306 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
307 pa_tagstruct *t;
309 t = pa_tagstruct_new(NULL, 0);
310 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
311 pa_tagstruct_putu32(t, 0);
312 pa_tagstruct_putu32(t, u->module->index);
313 pa_tagstruct_puts(t, u->module->name);
314 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
316 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
320 static void trigger_save(struct userdata *u) {
322 pa_assert(u);
324 notify_subscribers(u);
326 if (u->save_time_event)
327 return;
329 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
332 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
334 pa_assert(a);
335 pa_assert(b);
337 if (strncmp(a->description, b->description, sizeof(a->description))
338 || a->user_set_description != b->user_set_description
339 || strncmp(a->icon, b->icon, sizeof(a->icon)))
340 return FALSE;
342 for (int i=0; i < NUM_ROLES; ++i)
343 if (a->priority[i] != b->priority[i])
344 return FALSE;
346 return TRUE;
349 static char *get_name(const char *key, const char *prefix) {
350 char *t;
352 if (strncmp(key, prefix, strlen(prefix)))
353 return NULL;
355 t = pa_xstrdup(key + strlen(prefix));
356 return t;
359 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
360 struct entry *old;
362 pa_assert(u);
363 pa_assert(entry);
364 pa_assert(name);
365 pa_assert(prefix);
367 if ((old = read_entry(u, name)))
368 *entry = *old;
369 else {
370 /* This is a new device, so make sure we write it's priority list correctly */
371 role_indexes_t max_priority;
372 pa_datum key;
373 pa_bool_t done;
375 pa_zero(max_priority);
376 done = !pa_database_first(u->database, &key, NULL);
378 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
379 while (!done) {
380 pa_datum next_key;
382 done = !pa_database_next(u->database, &key, &next_key, NULL);
384 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
385 char *name2;
386 struct entry *e;
388 name2 = pa_xstrndup(key.data, key.size);
390 if ((e = read_entry(u, name2))) {
391 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
392 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
395 pa_xfree(e);
398 pa_xfree(name2);
400 pa_datum_free(&key);
401 key = next_key;
404 /* Actually initialise our entry now we've calculated it */
405 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
406 entry->priority[i] = max_priority[i] + 1;
408 entry->user_set_description = FALSE;
411 return old;
414 static uint32_t get_role_index(const char* role) {
415 pa_assert(role);
417 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
418 if (strcmp(role, role_names[i]) == 0)
419 return i;
421 return PA_INVALID_INDEX;
424 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
425 role_indexes_t *indexes, highest_priority_available;
426 pa_datum key;
427 pa_bool_t done, sink_mode;
429 pa_assert(u);
430 pa_assert(prefix);
432 sink_mode = (strcmp(prefix, "sink:") == 0);
434 if (sink_mode)
435 indexes = &u->preferred_sinks;
436 else
437 indexes = &u->preferred_sources;
439 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
440 (*indexes)[i] = PA_INVALID_INDEX;
442 pa_zero(highest_priority_available);
444 done = !pa_database_first(u->database, &key, NULL);
446 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
447 while (!done) {
448 pa_datum next_key;
450 done = !pa_database_next(u->database, &key, &next_key, NULL);
452 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
453 char *name, *device_name;
454 struct entry *e;
456 name = pa_xstrndup(key.data, key.size);
457 device_name = get_name(name, prefix);
459 if ((e = read_entry(u, name))) {
460 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
461 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
462 /* We've found a device with a higher priority than that we've currently got,
463 so see if it is currently available or not and update our list */
464 uint32_t idx;
465 pa_bool_t found = FALSE;
467 if (sink_mode) {
468 pa_sink *sink;
470 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
471 if ((pa_sink*) ignore_device == sink)
472 continue;
473 if (strcmp(sink->name, device_name) == 0) {
474 found = TRUE;
475 idx = sink->index; /* Is this needed? */
476 break;
479 } else {
480 pa_source *source;
482 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
483 if ((pa_source*) ignore_device == source)
484 continue;
485 if (strcmp(source->name, device_name) == 0) {
486 found = TRUE;
487 idx = source->index; /* Is this needed? */
488 break;
492 if (found) {
493 highest_priority_available[i] = e->priority[i];
494 (*indexes)[i] = idx;
500 pa_xfree(e);
503 pa_xfree(name);
504 pa_xfree(device_name);
507 pa_datum_free(&key);
508 key = next_key;
513 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
514 const char *role;
515 uint32_t role_index, device_index;
516 pa_sink *sink;
518 pa_assert(u);
519 pa_assert(u->do_routing);
521 if (si->save_sink)
522 return;
524 /* Skip this if it is already in the process of being moved anyway */
525 if (!si->sink)
526 return;
528 /* It might happen that a stream and a sink are set up at the
529 same time, in which case we want to make sure we don't
530 interfere with that */
531 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
532 return;
534 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
535 role_index = get_role_index("none");
536 else
537 role_index = get_role_index(role);
539 if (PA_INVALID_INDEX == role_index)
540 return;
542 device_index = u->preferred_sinks[role_index];
543 if (PA_INVALID_INDEX == device_index)
544 return;
546 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
547 return;
549 if (si->sink != sink)
550 pa_sink_input_move_to(si, sink, FALSE);
553 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
554 pa_sink_input *si;
555 uint32_t idx;
557 pa_assert(u);
559 if (!u->do_routing)
560 return PA_HOOK_OK;
562 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
564 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
565 route_sink_input(u, si);
568 return PA_HOOK_OK;
571 static void route_source_output(struct userdata *u, pa_source_output *so) {
572 const char *role;
573 uint32_t role_index, device_index;
574 pa_source *source;
576 pa_assert(u);
577 pa_assert(u->do_routing);
579 if (so->save_source)
580 return;
582 if (so->direct_on_input)
583 return;
585 /* Skip this if it is already in the process of being moved anyway */
586 if (!so->source)
587 return;
589 /* It might happen that a stream and a source are set up at the
590 same time, in which case we want to make sure we don't
591 interfere with that */
592 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
593 return;
595 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
596 role_index = get_role_index("none");
597 else
598 role_index = get_role_index(role);
600 if (PA_INVALID_INDEX == role_index)
601 return;
603 device_index = u->preferred_sources[role_index];
604 if (PA_INVALID_INDEX == device_index)
605 return;
607 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
608 return;
610 if (so->source != source)
611 pa_source_output_move_to(so, source, FALSE);
614 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
615 pa_source_output *so;
616 uint32_t idx;
618 pa_assert(u);
620 if (!u->do_routing)
621 return PA_HOOK_OK;
623 update_highest_priority_device_indexes(u, "source:", ignore_source);
625 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
626 route_source_output(u, so);
629 return PA_HOOK_OK;
632 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
633 struct userdata *u = userdata;
634 struct entry entry, *old = NULL;
635 char *name = NULL;
636 pa_datum key, data;
638 pa_assert(c);
639 pa_assert(u);
641 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
642 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
643 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
644 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
646 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
647 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
648 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
649 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
650 return;
652 pa_zero(entry);
653 entry.version = ENTRY_VERSION;
655 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
656 pa_sink_input *si;
658 if (!u->do_routing)
659 return;
660 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
661 return;
663 /* The role may change mid-stream, so we reroute */
664 route_sink_input(u, si);
666 return;
667 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
668 pa_source_output *so;
670 if (!u->do_routing)
671 return;
672 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
673 return;
675 /* The role may change mid-stream, so we reroute */
676 route_source_output(u, so);
678 return;
679 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
680 pa_sink *sink;
682 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
683 return;
685 name = pa_sprintf_malloc("sink:%s", sink->name);
687 old = load_or_initialize_entry(u, &entry, name, "sink:");
689 if (!entry.user_set_description)
690 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
691 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
692 /* Warning: If two modules fight over the description, this could cause an infinite loop.
693 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
694 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
695 the description, this will fail... */
696 pa_sink_set_description(sink, entry.description);
699 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
701 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
702 pa_source *source;
704 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
706 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
707 return;
709 if (source->monitor_of)
710 return;
712 name = pa_sprintf_malloc("source:%s", source->name);
714 old = load_or_initialize_entry(u, &entry, name, "source:");
716 if (!entry.user_set_description)
717 pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
718 else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
719 /* Warning: If two modules fight over the description, this could cause an infinite loop.
720 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
721 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
722 the description, this will fail... */
723 pa_source_set_description(source, entry.description);
726 pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
729 pa_assert(name);
731 if (old) {
733 if (entries_equal(old, &entry)) {
734 pa_xfree(old);
735 pa_xfree(name);
737 return;
740 pa_xfree(old);
743 key.data = name;
744 key.size = strlen(name);
746 data.data = &entry;
747 data.size = sizeof(entry);
749 pa_log_info("Storing device %s.", name);
751 if (pa_database_set(u->database, &key, &data, TRUE) == 0)
752 trigger_save(u);
753 else
754 pa_log_warn("Could not save device");;
756 pa_xfree(name);
759 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
760 char *name;
761 struct entry *e;
763 pa_assert(c);
764 pa_assert(new_data);
765 pa_assert(u);
767 name = pa_sprintf_malloc("sink:%s", new_data->name);
769 if ((e = read_entry(u, name))) {
770 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
771 pa_log_info("Restoring description for sink %s.", new_data->name);
772 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
775 pa_xfree(e);
778 pa_xfree(name);
780 return PA_HOOK_OK;
783 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
784 char *name;
785 struct entry *e;
787 pa_assert(c);
788 pa_assert(new_data);
789 pa_assert(u);
791 name = pa_sprintf_malloc("source:%s", new_data->name);
793 if ((e = read_entry(u, name))) {
794 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
795 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
796 pa_log_info("Restoring description for source %s.", new_data->name);
797 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
800 pa_xfree(e);
803 pa_xfree(name);
805 return PA_HOOK_OK;
808 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
809 const char *role;
810 uint32_t role_index;
812 pa_assert(c);
813 pa_assert(new_data);
814 pa_assert(u);
816 if (!u->do_routing)
817 return PA_HOOK_OK;
819 if (new_data->sink)
820 pa_log_debug("Not restoring device for stream because already set.");
821 else {
822 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
823 role_index = get_role_index("none");
824 else
825 role_index = get_role_index(role);
827 if (PA_INVALID_INDEX != role_index) {
828 uint32_t device_index;
830 device_index = u->preferred_sinks[role_index];
831 if (PA_INVALID_INDEX != device_index) {
832 pa_sink *sink;
834 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
835 new_data->sink = sink;
836 new_data->save_sink = FALSE;
842 return PA_HOOK_OK;
845 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
846 const char *role;
847 uint32_t role_index;
849 pa_assert(c);
850 pa_assert(new_data);
851 pa_assert(u);
853 if (!u->do_routing)
854 return PA_HOOK_OK;
856 if (new_data->direct_on_input)
857 return PA_HOOK_OK;
859 if (new_data->source)
860 pa_log_debug("Not restoring device for stream because already set.");
861 else {
862 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
863 role_index = get_role_index("none");
864 else
865 role_index = get_role_index(role);
867 if (PA_INVALID_INDEX != role_index) {
868 uint32_t device_index;
870 device_index = u->preferred_sources[role_index];
871 if (PA_INVALID_INDEX != device_index) {
872 pa_source *source;
874 if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
875 new_data->source = source;
876 new_data->save_source = FALSE;
882 return PA_HOOK_OK;
886 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
887 pa_assert(c);
888 pa_assert(u);
889 pa_assert(u->core == c);
890 pa_assert(u->on_hotplug);
892 notify_subscribers(u);
894 return route_sink_inputs(u, NULL);
897 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
898 pa_assert(c);
899 pa_assert(u);
900 pa_assert(u->core == c);
901 pa_assert(u->on_hotplug);
903 notify_subscribers(u);
905 return route_source_outputs(u, NULL);
908 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
909 pa_assert(c);
910 pa_assert(sink);
911 pa_assert(u);
912 pa_assert(u->core == c);
913 pa_assert(u->on_rescue);
915 /* There's no point in doing anything if the core is shut down anyway */
916 if (c->state == PA_CORE_SHUTDOWN)
917 return PA_HOOK_OK;
919 notify_subscribers(u);
921 return route_sink_inputs(u, sink);
924 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
925 pa_assert(c);
926 pa_assert(source);
927 pa_assert(u);
928 pa_assert(u->core == c);
929 pa_assert(u->on_rescue);
931 /* There's no point in doing anything if the core is shut down anyway */
932 if (c->state == PA_CORE_SHUTDOWN)
933 return PA_HOOK_OK;
935 notify_subscribers(u);
937 return route_source_outputs(u, source);
941 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
942 uint32_t idx;
943 char *n;
945 pa_assert(u);
946 pa_assert(name);
947 pa_assert(e);
949 if (!e->user_set_description)
950 return;
952 if ((n = get_name(name, "sink:"))) {
953 pa_sink *s;
954 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
955 if (!pa_streq(s->name, n)) {
956 continue;
959 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
960 pa_sink_set_description(s, e->description);
962 pa_xfree(n);
964 else if ((n = get_name(name, "source:"))) {
965 pa_source *s;
966 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
967 if (!pa_streq(s->name, n)) {
968 continue;
971 if (s->monitor_of) {
972 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
973 continue;
976 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
977 pa_source_set_description(s, e->description);
979 pa_xfree(n);
984 #define EXT_VERSION 1
986 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
987 struct userdata *u;
988 uint32_t command;
989 pa_tagstruct *reply = NULL;
991 pa_assert(p);
992 pa_assert(m);
993 pa_assert(c);
994 pa_assert(t);
996 u = m->userdata;
998 if (pa_tagstruct_getu32(t, &command) < 0)
999 goto fail;
1001 reply = pa_tagstruct_new(NULL, 0);
1002 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1003 pa_tagstruct_putu32(reply, tag);
1005 switch (command) {
1006 case SUBCOMMAND_TEST: {
1007 if (!pa_tagstruct_eof(t))
1008 goto fail;
1010 pa_tagstruct_putu32(reply, EXT_VERSION);
1011 break;
1014 case SUBCOMMAND_READ: {
1015 pa_datum key;
1016 pa_bool_t done;
1018 if (!pa_tagstruct_eof(t))
1019 goto fail;
1021 done = !pa_database_first(u->database, &key, NULL);
1023 while (!done) {
1024 pa_datum next_key;
1025 struct entry *e;
1026 char *name;
1028 done = !pa_database_next(u->database, &key, &next_key, NULL);
1030 name = pa_xstrndup(key.data, key.size);
1031 pa_datum_free(&key);
1033 if ((e = read_entry(u, name))) {
1034 uint32_t idx;
1035 char *device_name;
1036 uint32_t found_index = PA_INVALID_INDEX;
1038 if ((device_name = get_name(name, "sink:"))) {
1039 pa_sink* s;
1040 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1041 if (strcmp(s->name, device_name) == 0) {
1042 found_index = s->index;
1043 break;
1046 pa_xfree(device_name);
1047 } else if ((device_name = get_name(name, "source:"))) {
1048 pa_source* s;
1049 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1050 if (strcmp(s->name, device_name) == 0) {
1051 found_index = s->index;
1052 break;
1055 pa_xfree(device_name);
1058 pa_tagstruct_puts(reply, name);
1059 pa_tagstruct_puts(reply, e->description);
1060 pa_tagstruct_puts(reply, e->icon);
1061 pa_tagstruct_putu32(reply, found_index);
1062 pa_tagstruct_putu32(reply, NUM_ROLES);
1064 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1065 pa_tagstruct_puts(reply, role_names[i]);
1066 pa_tagstruct_putu32(reply, e->priority[i]);
1069 pa_xfree(e);
1072 pa_xfree(name);
1074 key = next_key;
1077 break;
1080 case SUBCOMMAND_RENAME: {
1082 struct entry *e;
1083 const char *device, *description;
1085 if (pa_tagstruct_gets(t, &device) < 0 ||
1086 pa_tagstruct_gets(t, &description) < 0)
1087 goto fail;
1089 if (!device || !*device || !description || !*description)
1090 goto fail;
1092 if ((e = read_entry(u, device))) {
1093 pa_datum key, data;
1095 pa_strlcpy(e->description, description, sizeof(e->description));
1096 e->user_set_description = TRUE;
1098 key.data = (char *) device;
1099 key.size = strlen(device);
1101 data.data = e;
1102 data.size = sizeof(*e);
1104 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1105 apply_entry(u, device, e);
1107 trigger_save(u);
1109 else
1110 pa_log_warn("Could not save device");
1112 pa_xfree(e);
1114 else
1115 pa_log_warn("Could not rename device %s, no entry in database", device);
1117 break;
1120 case SUBCOMMAND_DELETE:
1122 while (!pa_tagstruct_eof(t)) {
1123 const char *name;
1124 pa_datum key;
1126 if (pa_tagstruct_gets(t, &name) < 0)
1127 goto fail;
1129 key.data = (char*) name;
1130 key.size = strlen(name);
1132 /** @todo: Reindex the priorities */
1133 pa_database_unset(u->database, &key);
1136 trigger_save(u);
1138 break;
1140 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1142 pa_bool_t enable;
1144 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1145 goto fail;
1147 if ((u->do_routing = enable)) {
1148 /* Update our caches */
1149 update_highest_priority_device_indexes(u, "sink:", NULL);
1150 update_highest_priority_device_indexes(u, "source:", NULL);
1153 break;
1156 case SUBCOMMAND_REORDER: {
1158 const char *role;
1159 struct entry *e;
1160 uint32_t role_index, n_devices;
1161 pa_datum key, data;
1162 pa_bool_t done, sink_mode = TRUE;
1163 struct device_t { uint32_t prio; char *device; };
1164 struct device_t *device;
1165 struct device_t **devices;
1166 uint32_t i, idx, offset;
1167 pa_hashmap *h;
1168 /*void *state;*/
1169 pa_bool_t first;
1171 if (pa_tagstruct_gets(t, &role) < 0 ||
1172 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1173 n_devices < 1)
1174 goto fail;
1176 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1177 goto fail;
1179 /* Cycle through the devices given and make sure they exist */
1180 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1181 first = TRUE;
1182 idx = 0;
1183 for (i = 0; i < n_devices; ++i) {
1184 const char *s;
1185 if (pa_tagstruct_gets(t, &s) < 0) {
1186 while ((device = pa_hashmap_steal_first(h))) {
1187 pa_xfree(device->device);
1188 pa_xfree(device);
1191 pa_hashmap_free(h, NULL, NULL);
1192 pa_log_error("Protocol error on reorder");
1193 goto fail;
1196 /* Ensure this is a valid entry */
1197 if (!(e = read_entry(u, s))) {
1198 while ((device = pa_hashmap_steal_first(h))) {
1199 pa_xfree(device->device);
1200 pa_xfree(device);
1203 pa_hashmap_free(h, NULL, NULL);
1204 pa_log_error("Client specified an unknown device in it's reorder list.");
1205 goto fail;
1207 pa_xfree(e);
1209 if (first) {
1210 first = FALSE;
1211 sink_mode = (0 == strncmp("sink:", s, 5));
1212 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1213 while ((device = pa_hashmap_steal_first(h))) {
1214 pa_xfree(device->device);
1215 pa_xfree(device);
1218 pa_hashmap_free(h, NULL, NULL);
1219 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1220 goto fail;
1223 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1224 device = pa_xnew(struct device_t, 1);
1225 device->device = pa_xstrdup(s);
1226 if (pa_hashmap_put(h, device->device, device) == 0) {
1227 device->prio = idx;
1228 idx++;
1229 } else {
1230 pa_xfree(device->device);
1231 pa_xfree(device);
1235 /*pa_log_debug("Hashmap contents (received from client)");
1236 PA_HASHMAP_FOREACH(device, h, state) {
1237 pa_log_debug(" - %s (%d)", device->device, device->prio);
1240 /* Now cycle through our list and add all the devices.
1241 This has the effect of addign in any in our DB,
1242 not specified in the device list (and thus will be
1243 tacked on at the end) */
1244 offset = idx;
1245 done = !pa_database_first(u->database, &key, NULL);
1247 while (!done && idx < 256) {
1248 pa_datum next_key;
1250 done = !pa_database_next(u->database, &key, &next_key, NULL);
1252 device = pa_xnew(struct device_t, 1);
1253 device->device = pa_xstrndup(key.data, key.size);
1254 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1255 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1257 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1258 if (pa_hashmap_put(h, device->device, device) == 0
1259 && (e = read_entry(u, device->device))) {
1260 /* We add offset on to the existing priorirty so that when we order, the
1261 existing entries are always lower priority than the new ones. */
1262 device->prio = (offset + e->priority[role_index]);
1263 pa_xfree(e);
1265 else {
1266 pa_xfree(device->device);
1267 pa_xfree(device);
1269 } else {
1270 pa_xfree(device->device);
1271 pa_xfree(device);
1274 pa_datum_free(&key);
1276 key = next_key;
1279 /*pa_log_debug("Hashmap contents (combined with database)");
1280 PA_HASHMAP_FOREACH(device, h, state) {
1281 pa_log_debug(" - %s (%d)", device->device, device->prio);
1284 /* Now we put all the entries in a simple list for sorting it. */
1285 n_devices = pa_hashmap_size(h);
1286 devices = pa_xnew(struct device_t *, n_devices);
1287 idx = 0;
1288 while ((device = pa_hashmap_steal_first(h))) {
1289 devices[idx++] = device;
1291 pa_hashmap_free(h, NULL, NULL);
1293 /* Simple bubble sort */
1294 for (i = 0; i < n_devices; ++i) {
1295 for (uint32_t j = i; j < n_devices; ++j) {
1296 if (devices[i]->prio > devices[j]->prio) {
1297 struct device_t *tmp;
1298 tmp = devices[i];
1299 devices[i] = devices[j];
1300 devices[j] = tmp;
1305 /*pa_log_debug("Sorted device list");
1306 for (i = 0; i < n_devices; ++i) {
1307 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1310 /* Go through in order and write the new entry and cleanup our own list */
1311 idx = 1;
1312 first = TRUE;
1313 for (i = 0; i < n_devices; ++i) {
1314 if ((e = read_entry(u, devices[i]->device))) {
1315 if (e->priority[role_index] == idx)
1316 idx++;
1317 else {
1318 e->priority[role_index] = idx;
1320 key.data = (char *) devices[i]->device;
1321 key.size = strlen(devices[i]->device);
1323 data.data = e;
1324 data.size = sizeof(*e);
1326 if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
1327 first = FALSE;
1328 idx++;
1332 pa_xfree(e);
1334 pa_xfree(devices[i]->device);
1335 pa_xfree(devices[i]);
1338 if (!first) {
1339 trigger_save(u);
1341 if (sink_mode)
1342 route_sink_inputs(u, NULL);
1343 else
1344 route_source_outputs(u, NULL);
1347 break;
1350 case SUBCOMMAND_SUBSCRIBE: {
1352 pa_bool_t enabled;
1354 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1355 !pa_tagstruct_eof(t))
1356 goto fail;
1358 if (enabled)
1359 pa_idxset_put(u->subscribed, c, NULL);
1360 else
1361 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1363 break;
1366 default:
1367 goto fail;
1370 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1371 return 0;
1373 fail:
1375 if (reply)
1376 pa_tagstruct_free(reply);
1378 return -1;
1381 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1382 pa_assert(p);
1383 pa_assert(c);
1384 pa_assert(u);
1386 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1387 return PA_HOOK_OK;
1390 struct prioritised_indexes {
1391 uint32_t index;
1392 int32_t priority;
1395 int pa__init(pa_module*m) {
1396 pa_modargs *ma = NULL;
1397 struct userdata *u;
1398 char *fname;
1399 pa_sink *sink;
1400 pa_source *source;
1401 uint32_t idx;
1402 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1403 uint32_t total_devices;
1405 pa_assert(m);
1407 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1408 pa_log("Failed to parse module arguments");
1409 goto fail;
1412 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1413 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1414 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1415 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1416 goto fail;
1419 m->userdata = u = pa_xnew0(struct userdata, 1);
1420 u->core = m->core;
1421 u->module = m;
1422 u->do_routing = do_routing;
1423 u->on_hotplug = on_hotplug;
1424 u->on_rescue = on_rescue;
1425 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1427 u->protocol = pa_native_protocol_get(m->core);
1428 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1430 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);
1432 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);
1434 /* Used to handle device description management */
1435 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);
1436 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);
1438 /* The following slots are used to deal with routing */
1439 /* A little bit later than module-stream-restore, but before module-intended-roles */
1440 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);
1441 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);
1443 if (on_hotplug) {
1444 /* A little bit later than module-stream-restore, but before module-intended-roles */
1445 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);
1446 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);
1449 if (on_rescue) {
1450 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1451 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);
1452 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);
1455 if (!(fname = pa_state_path("device-manager", TRUE)))
1456 goto fail;
1458 if (!(u->database = pa_database_open(fname, TRUE))) {
1459 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1460 pa_xfree(fname);
1461 goto fail;
1464 pa_log_info("Successfully opened database file '%s'.", fname);
1465 pa_xfree(fname);
1467 /* Attempt to inject the devices into the list in priority order */
1468 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1469 if (total_devices > 0 && total_devices < 128) {
1470 uint32_t i;
1471 struct prioritised_indexes p_i[128];
1473 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1474 i = 0;
1475 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1476 pa_log_debug("Found sink index %u", sink->index);
1477 p_i[i ].index = sink->index;
1478 p_i[i++].priority = sink->priority;
1480 /* Bubble sort it (only really useful for first time creation) */
1481 if (i > 1)
1482 for (uint32_t j = 0; j < i; ++j)
1483 for (uint32_t k = 0; k < i; ++k)
1484 if (p_i[j].priority > p_i[k].priority) {
1485 struct prioritised_indexes tmp_pi = p_i[k];
1486 p_i[k] = p_i[j];
1487 p_i[j] = tmp_pi;
1489 /* Register it */
1490 for (uint32_t j = 0; j < i; ++j)
1491 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1494 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1495 i = 0;
1496 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1497 p_i[i ].index = source->index;
1498 p_i[i++].priority = source->priority;
1500 /* Bubble sort it (only really useful for first time creation) */
1501 if (i > 1)
1502 for (uint32_t j = 0; j < i; ++j)
1503 for (uint32_t k = 0; k < i; ++k)
1504 if (p_i[j].priority > p_i[k].priority) {
1505 struct prioritised_indexes tmp_pi = p_i[k];
1506 p_i[k] = p_i[j];
1507 p_i[j] = tmp_pi;
1509 /* Register it */
1510 for (uint32_t j = 0; j < i; ++j)
1511 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1513 else if (total_devices > 0) {
1514 /* This user has a *lot* of devices... */
1515 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1516 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1518 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1519 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1522 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1523 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1524 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1527 route_sink_inputs(u, NULL);
1528 route_source_outputs(u, NULL);
1530 #ifdef DUMP_DATABASE
1531 dump_database(u);
1532 #endif
1534 pa_modargs_free(ma);
1535 return 0;
1537 fail:
1538 pa__done(m);
1540 if (ma)
1541 pa_modargs_free(ma);
1543 return -1;
1546 void pa__done(pa_module*m) {
1547 struct userdata* u;
1549 pa_assert(m);
1551 if (!(u = m->userdata))
1552 return;
1554 if (u->subscription)
1555 pa_subscription_free(u->subscription);
1557 if (u->sink_new_hook_slot)
1558 pa_hook_slot_free(u->sink_new_hook_slot);
1559 if (u->source_new_hook_slot)
1560 pa_hook_slot_free(u->source_new_hook_slot);
1562 if (u->sink_input_new_hook_slot)
1563 pa_hook_slot_free(u->sink_input_new_hook_slot);
1564 if (u->source_output_new_hook_slot)
1565 pa_hook_slot_free(u->source_output_new_hook_slot);
1567 if (u->sink_put_hook_slot)
1568 pa_hook_slot_free(u->sink_put_hook_slot);
1569 if (u->source_put_hook_slot)
1570 pa_hook_slot_free(u->source_put_hook_slot);
1572 if (u->sink_unlink_hook_slot)
1573 pa_hook_slot_free(u->sink_unlink_hook_slot);
1574 if (u->source_unlink_hook_slot)
1575 pa_hook_slot_free(u->source_unlink_hook_slot);
1577 if (u->save_time_event)
1578 u->core->mainloop->time_free(u->save_time_event);
1580 if (u->database)
1581 pa_database_close(u->database);
1583 if (u->protocol) {
1584 pa_native_protocol_remove_ext(u->protocol, m);
1585 pa_native_protocol_unref(u->protocol);
1588 if (u->subscribed)
1589 pa_idxset_free(u->subscribed, NULL, NULL);
1591 pa_xfree(u);