build-sys: Use ax_check_flag macros from autoconf archive
[pulseaudio-mirror.git] / src / modules / module-device-manager.c
blob67baef31ddf0cef62569a1f2b526136fec5ad587
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>
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);
60 PA_MODULE_USAGE(
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)
66 #define DUMP_DATABASE
68 static const char* const valid_modargs[] = {
69 "do_routing",
70 "on_hotplug",
71 "on_rescue",
72 NULL
75 #define NUM_ROLES 9
76 enum {
77 ROLE_NONE,
78 ROLE_VIDEO,
79 ROLE_MUSIC,
80 ROLE_GAME,
81 ROLE_EVENT,
82 ROLE_PHONE,
83 ROLE_ANIMATION,
84 ROLE_PRODUCTION,
85 ROLE_A11Y,
86 ROLE_MAX
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;
136 pa_bool_t user_set_description;
137 char *icon;
138 role_indexes_t priority;
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 /* Forward declarations */
154 #ifdef DUMP_DATABASE
155 static void dump_database(struct userdata *);
156 #endif
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;
163 pa_assert(a);
164 pa_assert(e);
165 pa_assert(u);
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.");
174 #ifdef DUMP_DATABASE
175 dump_database(u);
176 #endif
179 static void trigger_save(struct userdata *u) {
181 pa_assert(u);
183 notify_subscribers(u);
185 if (u->save_time_event)
186 return;
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;
194 return r;
197 static void entry_free(struct entry* e) {
198 pa_assert(e);
200 pa_xfree(e->description);
201 pa_xfree(e->icon);
204 static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
205 pa_tagstruct *t;
206 pa_datum key, data;
207 pa_bool_t r;
209 pa_assert(u);
210 pa_assert(name);
211 pa_assert(e);
213 t = pa_tagstruct_new(NULL, 0);
214 pa_tagstruct_putu8(t, e->version);
215 pa_tagstruct_puts(t, e->description);
216 pa_tagstruct_put_boolean(t, e->user_set_description);
217 pa_tagstruct_puts(t, e->icon);
218 for (uint8_t i=0; i<ROLE_MAX; ++i)
219 pa_tagstruct_putu32(t, e->priority[i]);
221 key.data = (char *) name;
222 key.size = strlen(name);
224 data.data = (void*)pa_tagstruct_data(t, &data.size);
226 r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
228 pa_tagstruct_free(t);
230 return r;
233 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
235 #define LEGACY_ENTRY_VERSION 1
236 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
237 struct legacy_entry {
238 uint8_t version;
239 char description[PA_NAME_MAX];
240 pa_bool_t user_set_description;
241 char icon[PA_NAME_MAX];
242 role_indexes_t priority;
243 } PA_GCC_PACKED;
244 struct legacy_entry *le;
245 struct entry *e;
247 pa_assert(u);
248 pa_assert(data);
250 if (data->size != sizeof(struct legacy_entry)) {
251 pa_log_debug("Size does not match.");
252 return NULL;
255 le = (struct legacy_entry*)data->data;
257 if (le->version != LEGACY_ENTRY_VERSION) {
258 pa_log_debug("Version mismatch.");
259 return NULL;
262 if (!memchr(le->description, 0, sizeof(le->description))) {
263 pa_log_warn("Description has missing NUL byte.");
264 return NULL;
267 if (!memchr(le->icon, 0, sizeof(le->icon))) {
268 pa_log_warn("Icon has missing NUL byte.");
269 return NULL;
272 e = entry_new();
273 e->description = pa_xstrdup(le->description);
274 e->icon = pa_xstrdup(le->icon);
275 return e;
277 #endif
279 static struct entry* entry_read(struct userdata *u, const char *name) {
280 pa_datum key, data;
281 struct entry *e = NULL;
282 pa_tagstruct *t = NULL;
283 const char *description, *icon;
285 pa_assert(u);
286 pa_assert(name);
288 key.data = (char*) name;
289 key.size = strlen(name);
291 pa_zero(data);
293 if (!pa_database_get(u->database, &key, &data))
294 goto fail;
296 t = pa_tagstruct_new(data.data, data.size);
297 e = entry_new();
299 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
300 e->version > ENTRY_VERSION ||
301 pa_tagstruct_gets(t, &description) < 0 ||
302 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
303 pa_tagstruct_gets(t, &icon) < 0) {
305 goto fail;
308 e->description = pa_xstrdup(description);
309 e->icon = pa_xstrdup(icon);
311 for (uint8_t i=0; i<ROLE_MAX; ++i) {
312 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
313 goto fail;
316 if (!pa_tagstruct_eof(t))
317 goto fail;
319 pa_tagstruct_free(t);
320 pa_datum_free(&data);
322 return e;
324 fail:
325 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
327 if (e)
328 entry_free(e);
329 if (t)
330 pa_tagstruct_free(t);
332 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
333 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
334 if ((e = legacy_entry_read(u, &data))) {
335 pa_log_debug("Success. Saving new format for key: %s", name);
336 if (entry_write(u, name, e))
337 trigger_save(u);
338 pa_datum_free(&data);
339 return e;
340 } else
341 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
342 #endif
344 pa_datum_free(&data);
345 return NULL;
348 #ifdef DUMP_DATABASE
349 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
350 pa_assert(u);
351 pa_assert(human);
353 if (sink_mode) {
354 pa_sink *s;
355 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
356 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
357 else
358 pa_log_debug(" %s No sink specified", human);
359 } else {
360 pa_source *s;
361 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
362 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
363 else
364 pa_log_debug(" %s No source specified", human);
368 static void dump_database(struct userdata *u) {
369 pa_datum key;
370 pa_bool_t done;
372 pa_assert(u);
374 done = !pa_database_first(u->database, &key, NULL);
376 pa_log_debug("Dumping database");
377 while (!done) {
378 char *name;
379 struct entry *e;
380 pa_datum next_key;
382 done = !pa_database_next(u->database, &key, &next_key, NULL);
384 name = pa_xstrndup(key.data, key.size);
386 if ((e = entry_read(u, name))) {
387 pa_log_debug(" Got entry: %s", name);
388 pa_log_debug(" Description: %s", e->description);
389 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
390 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
391 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
392 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
393 entry_free(e);
396 pa_xfree(name);
398 pa_datum_free(&key);
399 key = next_key;
402 if (u->do_routing) {
403 pa_log_debug(" Highest priority devices per-role:");
405 pa_log_debug(" Sinks:");
406 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
407 char name[13];
408 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
409 strncpy(name, role_names[role], len);
410 for (int i = len+1; i < 12; ++i) name[i] = ' ';
411 name[len] = ':'; name[0] -= 32; name[12] = '\0';
412 dump_database_helper(u, role, name, TRUE);
415 pa_log_debug(" Sources:");
416 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
417 char name[13];
418 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
419 strncpy(name, role_names[role], len);
420 for (int i = len+1; i < 12; ++i) name[i] = ' ';
421 name[len] = ':'; name[0] -= 32; name[12] = '\0';
422 dump_database_helper(u, role, name, FALSE);
426 pa_log_debug("Completed database dump");
428 #endif
430 static void notify_subscribers(struct userdata *u) {
432 pa_native_connection *c;
433 uint32_t idx;
435 pa_assert(u);
437 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
438 pa_tagstruct *t;
440 t = pa_tagstruct_new(NULL, 0);
441 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
442 pa_tagstruct_putu32(t, 0);
443 pa_tagstruct_putu32(t, u->module->index);
444 pa_tagstruct_puts(t, u->module->name);
445 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
447 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
451 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
453 pa_assert(a);
454 pa_assert(b);
456 if (!pa_streq(a->description, b->description)
457 || a->user_set_description != b->user_set_description
458 || !pa_streq(a->icon, b->icon))
459 return FALSE;
461 for (int i=0; i < NUM_ROLES; ++i)
462 if (a->priority[i] != b->priority[i])
463 return FALSE;
465 return TRUE;
468 static char *get_name(const char *key, const char *prefix) {
469 char *t;
471 if (strncmp(key, prefix, strlen(prefix)))
472 return NULL;
474 t = pa_xstrdup(key + strlen(prefix));
475 return t;
478 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
479 struct entry *old;
481 pa_assert(u);
482 pa_assert(entry);
483 pa_assert(name);
484 pa_assert(prefix);
486 if ((old = entry_read(u, name))) {
487 *entry = *old;
488 entry->description = pa_xstrdup(old->description);
489 entry->icon = pa_xstrdup(old->icon);
490 } else {
491 /* This is a new device, so make sure we write it's priority list correctly */
492 role_indexes_t max_priority;
493 pa_datum key;
494 pa_bool_t done;
496 pa_zero(max_priority);
497 done = !pa_database_first(u->database, &key, NULL);
499 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
500 while (!done) {
501 pa_datum next_key;
503 done = !pa_database_next(u->database, &key, &next_key, NULL);
505 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
506 char *name2;
507 struct entry *e;
509 name2 = pa_xstrndup(key.data, key.size);
511 if ((e = entry_read(u, name2))) {
512 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
513 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
516 entry_free(e);
519 pa_xfree(name2);
521 pa_datum_free(&key);
522 key = next_key;
525 /* Actually initialise our entry now we've calculated it */
526 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
527 entry->priority[i] = max_priority[i] + 1;
529 entry->user_set_description = FALSE;
532 return old;
535 static uint32_t get_role_index(const char* role) {
536 pa_assert(role);
538 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
539 if (strcmp(role, role_names[i]) == 0)
540 return i;
542 return PA_INVALID_INDEX;
545 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
546 role_indexes_t *indexes, highest_priority_available;
547 pa_datum key;
548 pa_bool_t done, sink_mode;
550 pa_assert(u);
551 pa_assert(prefix);
553 sink_mode = (strcmp(prefix, "sink:") == 0);
555 if (sink_mode)
556 indexes = &u->preferred_sinks;
557 else
558 indexes = &u->preferred_sources;
560 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
561 (*indexes)[i] = PA_INVALID_INDEX;
563 pa_zero(highest_priority_available);
565 done = !pa_database_first(u->database, &key, NULL);
567 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
568 while (!done) {
569 pa_datum next_key;
571 done = !pa_database_next(u->database, &key, &next_key, NULL);
573 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
574 char *name, *device_name;
575 struct entry *e;
577 name = pa_xstrndup(key.data, key.size);
578 device_name = get_name(name, prefix);
580 if ((e = entry_read(u, name))) {
581 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
582 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
583 /* We've found a device with a higher priority than that we've currently got,
584 so see if it is currently available or not and update our list */
585 uint32_t idx;
586 pa_bool_t found = FALSE;
588 if (sink_mode) {
589 pa_sink *sink;
591 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
592 if ((pa_sink*) ignore_device == sink)
593 continue;
594 if (strcmp(sink->name, device_name) == 0) {
595 found = TRUE;
596 idx = sink->index; /* Is this needed? */
597 break;
600 } else {
601 pa_source *source;
603 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
604 if ((pa_source*) ignore_device == source)
605 continue;
606 if (strcmp(source->name, device_name) == 0) {
607 found = TRUE;
608 idx = source->index; /* Is this needed? */
609 break;
613 if (found) {
614 highest_priority_available[i] = e->priority[i];
615 (*indexes)[i] = idx;
621 entry_free(e);
624 pa_xfree(name);
625 pa_xfree(device_name);
628 pa_datum_free(&key);
629 key = next_key;
634 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
635 const char *role;
636 uint32_t role_index, device_index;
637 pa_sink *sink;
639 pa_assert(u);
640 pa_assert(u->do_routing);
642 if (si->save_sink)
643 return;
645 /* Skip this if it is already in the process of being moved anyway */
646 if (!si->sink)
647 return;
649 /* It might happen that a stream and a sink are set up at the
650 same time, in which case we want to make sure we don't
651 interfere with that */
652 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
653 return;
655 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
656 role_index = get_role_index("none");
657 else
658 role_index = get_role_index(role);
660 if (PA_INVALID_INDEX == role_index)
661 return;
663 device_index = u->preferred_sinks[role_index];
664 if (PA_INVALID_INDEX == device_index)
665 return;
667 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
668 return;
670 if (si->sink != sink)
671 pa_sink_input_move_to(si, sink, FALSE);
674 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
675 pa_sink_input *si;
676 uint32_t idx;
678 pa_assert(u);
680 if (!u->do_routing)
681 return PA_HOOK_OK;
683 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
685 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
686 route_sink_input(u, si);
689 return PA_HOOK_OK;
692 static void route_source_output(struct userdata *u, pa_source_output *so) {
693 const char *role;
694 uint32_t role_index, device_index;
695 pa_source *source;
697 pa_assert(u);
698 pa_assert(u->do_routing);
700 if (so->save_source)
701 return;
703 if (so->direct_on_input)
704 return;
706 /* Skip this if it is already in the process of being moved anyway */
707 if (!so->source)
708 return;
710 /* It might happen that a stream and a source are set up at the
711 same time, in which case we want to make sure we don't
712 interfere with that */
713 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
714 return;
716 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
717 role_index = get_role_index("none");
718 else
719 role_index = get_role_index(role);
721 if (PA_INVALID_INDEX == role_index)
722 return;
724 device_index = u->preferred_sources[role_index];
725 if (PA_INVALID_INDEX == device_index)
726 return;
728 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
729 return;
731 if (so->source != source)
732 pa_source_output_move_to(so, source, FALSE);
735 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
736 pa_source_output *so;
737 uint32_t idx;
739 pa_assert(u);
741 if (!u->do_routing)
742 return PA_HOOK_OK;
744 update_highest_priority_device_indexes(u, "source:", ignore_source);
746 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
747 route_source_output(u, so);
750 return PA_HOOK_OK;
753 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
754 struct userdata *u = userdata;
755 struct entry *entry, *old = NULL;
756 char *name = NULL;
758 pa_assert(c);
759 pa_assert(u);
761 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
762 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
763 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
764 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
766 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
767 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
768 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
769 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
770 return;
772 entry = entry_new();
774 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
775 pa_sink_input *si;
777 if (!u->do_routing)
778 return;
779 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
780 return;
782 /* The role may change mid-stream, so we reroute */
783 route_sink_input(u, si);
785 return;
786 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
787 pa_source_output *so;
789 if (!u->do_routing)
790 return;
791 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
792 return;
794 /* The role may change mid-stream, so we reroute */
795 route_source_output(u, so);
797 return;
798 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
799 pa_sink *sink;
801 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
802 return;
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) {
823 pa_source *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)))
828 return;
830 if (source->monitor_of)
831 return;
833 name = pa_sprintf_malloc("source:%s", source->name);
835 old = load_or_initialize_entry(u, entry, name, "source:");
837 if (!entry->user_set_description) {
838 pa_xfree(entry->description);
839 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
840 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
841 /* Warning: If two modules fight over the description, this could cause an infinite loop.
842 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
843 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
844 the description, this will fail... */
845 pa_source_set_description(source, entry->description);
848 pa_xfree(entry->icon);
849 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
852 pa_assert(name);
854 if (old) {
856 if (entries_equal(old, entry)) {
857 entry_free(old);
858 entry_free(entry);
859 pa_xfree(name);
861 return;
864 entry_free(old);
867 pa_log_info("Storing device %s.", name);
869 if (entry_write(u, name, entry))
870 trigger_save(u);
871 else
872 pa_log_warn("Could not save device");;
874 entry_free(entry);
875 pa_xfree(name);
878 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
879 char *name;
880 struct entry *e;
882 pa_assert(c);
883 pa_assert(new_data);
884 pa_assert(u);
886 name = pa_sprintf_malloc("sink:%s", new_data->name);
888 if ((e = entry_read(u, name))) {
889 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
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);
894 entry_free(e);
897 pa_xfree(name);
899 return PA_HOOK_OK;
902 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
903 char *name;
904 struct entry *e;
906 pa_assert(c);
907 pa_assert(new_data);
908 pa_assert(u);
910 name = pa_sprintf_malloc("source:%s", new_data->name);
912 if ((e = entry_read(u, name))) {
913 if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
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);
919 entry_free(e);
922 pa_xfree(name);
924 return PA_HOOK_OK;
927 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
928 const char *role;
929 uint32_t role_index;
931 pa_assert(c);
932 pa_assert(new_data);
933 pa_assert(u);
935 if (!u->do_routing)
936 return PA_HOOK_OK;
938 if (new_data->sink)
939 pa_log_debug("Not restoring device for stream because already set.");
940 else {
941 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
942 role_index = get_role_index("none");
943 else
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) {
951 pa_sink *sink;
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");
961 return PA_HOOK_OK;
964 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
965 const char *role;
966 uint32_t role_index;
968 pa_assert(c);
969 pa_assert(new_data);
970 pa_assert(u);
972 if (!u->do_routing)
973 return PA_HOOK_OK;
975 if (new_data->direct_on_input)
976 return PA_HOOK_OK;
978 if (new_data->source)
979 pa_log_debug("Not restoring device for stream because already set.");
980 else {
981 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
982 role_index = get_role_index("none");
983 else
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) {
991 pa_source *source;
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");
1000 return PA_HOOK_OK;
1004 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1005 pa_assert(c);
1006 pa_assert(u);
1007 pa_assert(u->core == c);
1008 pa_assert(u->on_hotplug);
1010 notify_subscribers(u);
1012 return route_sink_inputs(u, NULL);
1015 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1016 pa_assert(c);
1017 pa_assert(u);
1018 pa_assert(u->core == c);
1019 pa_assert(u->on_hotplug);
1021 notify_subscribers(u);
1023 return route_source_outputs(u, NULL);
1026 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1027 pa_assert(c);
1028 pa_assert(sink);
1029 pa_assert(u);
1030 pa_assert(u->core == c);
1031 pa_assert(u->on_rescue);
1033 /* There's no point in doing anything if the core is shut down anyway */
1034 if (c->state == PA_CORE_SHUTDOWN)
1035 return PA_HOOK_OK;
1037 notify_subscribers(u);
1039 return route_sink_inputs(u, sink);
1042 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1043 pa_assert(c);
1044 pa_assert(source);
1045 pa_assert(u);
1046 pa_assert(u->core == c);
1047 pa_assert(u->on_rescue);
1049 /* There's no point in doing anything if the core is shut down anyway */
1050 if (c->state == PA_CORE_SHUTDOWN)
1051 return PA_HOOK_OK;
1053 notify_subscribers(u);
1055 return route_source_outputs(u, source);
1059 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1060 uint32_t idx;
1061 char *n;
1063 pa_assert(u);
1064 pa_assert(name);
1065 pa_assert(e);
1067 if (!e->user_set_description)
1068 return;
1070 if ((n = get_name(name, "sink:"))) {
1071 pa_sink *s;
1072 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1073 if (!pa_streq(s->name, n)) {
1074 continue;
1077 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1078 pa_sink_set_description(s, e->description);
1080 pa_xfree(n);
1082 else if ((n = get_name(name, "source:"))) {
1083 pa_source *s;
1084 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1085 if (!pa_streq(s->name, n)) {
1086 continue;
1089 if (s->monitor_of) {
1090 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1091 continue;
1094 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1095 pa_source_set_description(s, e->description);
1097 pa_xfree(n);
1102 #define EXT_VERSION 1
1104 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1105 struct userdata *u;
1106 uint32_t command;
1107 pa_tagstruct *reply = NULL;
1109 pa_assert(p);
1110 pa_assert(m);
1111 pa_assert(c);
1112 pa_assert(t);
1114 u = m->userdata;
1116 if (pa_tagstruct_getu32(t, &command) < 0)
1117 goto fail;
1119 reply = pa_tagstruct_new(NULL, 0);
1120 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1121 pa_tagstruct_putu32(reply, tag);
1123 switch (command) {
1124 case SUBCOMMAND_TEST: {
1125 if (!pa_tagstruct_eof(t))
1126 goto fail;
1128 pa_tagstruct_putu32(reply, EXT_VERSION);
1129 break;
1132 case SUBCOMMAND_READ: {
1133 pa_datum key;
1134 pa_bool_t done;
1136 if (!pa_tagstruct_eof(t))
1137 goto fail;
1139 done = !pa_database_first(u->database, &key, NULL);
1141 while (!done) {
1142 pa_datum next_key;
1143 struct entry *e;
1144 char *name;
1146 done = !pa_database_next(u->database, &key, &next_key, NULL);
1148 name = pa_xstrndup(key.data, key.size);
1149 pa_datum_free(&key);
1151 if ((e = entry_read(u, name))) {
1152 uint32_t idx;
1153 char *device_name;
1154 uint32_t found_index = PA_INVALID_INDEX;
1156 if ((device_name = get_name(name, "sink:"))) {
1157 pa_sink* s;
1158 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1159 if (strcmp(s->name, device_name) == 0) {
1160 found_index = s->index;
1161 break;
1164 pa_xfree(device_name);
1165 } else if ((device_name = get_name(name, "source:"))) {
1166 pa_source* s;
1167 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1168 if (strcmp(s->name, device_name) == 0) {
1169 found_index = s->index;
1170 break;
1173 pa_xfree(device_name);
1176 pa_tagstruct_puts(reply, name);
1177 pa_tagstruct_puts(reply, e->description);
1178 pa_tagstruct_puts(reply, e->icon);
1179 pa_tagstruct_putu32(reply, found_index);
1180 pa_tagstruct_putu32(reply, NUM_ROLES);
1182 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1183 pa_tagstruct_puts(reply, role_names[i]);
1184 pa_tagstruct_putu32(reply, e->priority[i]);
1187 entry_free(e);
1190 pa_xfree(name);
1192 key = next_key;
1195 break;
1198 case SUBCOMMAND_RENAME: {
1200 struct entry *e;
1201 const char *device, *description;
1203 if (pa_tagstruct_gets(t, &device) < 0 ||
1204 pa_tagstruct_gets(t, &description) < 0)
1205 goto fail;
1207 if (!device || !*device || !description || !*description)
1208 goto fail;
1210 if ((e = entry_read(u, device))) {
1211 pa_xfree(e->description);
1212 e->description = pa_xstrdup(description);
1213 e->user_set_description = TRUE;
1215 if (entry_write(u, (char *)device, e)) {
1216 apply_entry(u, device, e);
1218 trigger_save(u);
1220 else
1221 pa_log_warn("Could not save device");
1223 entry_free(e);
1225 else
1226 pa_log_warn("Could not rename device %s, no entry in database", device);
1228 break;
1231 case SUBCOMMAND_DELETE:
1233 while (!pa_tagstruct_eof(t)) {
1234 const char *name;
1235 pa_datum key;
1237 if (pa_tagstruct_gets(t, &name) < 0)
1238 goto fail;
1240 key.data = (char*) name;
1241 key.size = strlen(name);
1243 /** @todo: Reindex the priorities */
1244 pa_database_unset(u->database, &key);
1247 trigger_save(u);
1249 break;
1251 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1253 pa_bool_t enable;
1255 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1256 goto fail;
1258 if ((u->do_routing = enable)) {
1259 /* Update our caches */
1260 update_highest_priority_device_indexes(u, "sink:", NULL);
1261 update_highest_priority_device_indexes(u, "source:", NULL);
1264 break;
1267 case SUBCOMMAND_REORDER: {
1269 const char *role;
1270 struct entry *e;
1271 uint32_t role_index, n_devices;
1272 pa_datum key;
1273 pa_bool_t done, sink_mode = TRUE;
1274 struct device_t { uint32_t prio; char *device; };
1275 struct device_t *device;
1276 struct device_t **devices;
1277 uint32_t i, idx, offset;
1278 pa_hashmap *h;
1279 /*void *state;*/
1280 pa_bool_t first;
1282 if (pa_tagstruct_gets(t, &role) < 0 ||
1283 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1284 n_devices < 1)
1285 goto fail;
1287 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1288 goto fail;
1290 /* Cycle through the devices given and make sure they exist */
1291 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1292 first = TRUE;
1293 idx = 0;
1294 for (i = 0; i < n_devices; ++i) {
1295 const char *s;
1296 if (pa_tagstruct_gets(t, &s) < 0) {
1297 while ((device = pa_hashmap_steal_first(h))) {
1298 pa_xfree(device->device);
1299 pa_xfree(device);
1302 pa_hashmap_free(h, NULL, NULL);
1303 pa_log_error("Protocol error on reorder");
1304 goto fail;
1307 /* Ensure this is a valid entry */
1308 if (!(e = entry_read(u, s))) {
1309 while ((device = pa_hashmap_steal_first(h))) {
1310 pa_xfree(device->device);
1311 pa_xfree(device);
1314 pa_hashmap_free(h, NULL, NULL);
1315 pa_log_error("Client specified an unknown device in it's reorder list.");
1316 goto fail;
1318 entry_free(e);
1320 if (first) {
1321 first = FALSE;
1322 sink_mode = (0 == strncmp("sink:", s, 5));
1323 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1324 while ((device = pa_hashmap_steal_first(h))) {
1325 pa_xfree(device->device);
1326 pa_xfree(device);
1329 pa_hashmap_free(h, NULL, NULL);
1330 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1331 goto fail;
1334 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1335 device = pa_xnew(struct device_t, 1);
1336 device->device = pa_xstrdup(s);
1337 if (pa_hashmap_put(h, device->device, device) == 0) {
1338 device->prio = idx;
1339 idx++;
1340 } else {
1341 pa_xfree(device->device);
1342 pa_xfree(device);
1346 /*pa_log_debug("Hashmap contents (received from client)");
1347 PA_HASHMAP_FOREACH(device, h, state) {
1348 pa_log_debug(" - %s (%d)", device->device, device->prio);
1351 /* Now cycle through our list and add all the devices.
1352 This has the effect of addign in any in our DB,
1353 not specified in the device list (and thus will be
1354 tacked on at the end) */
1355 offset = idx;
1356 done = !pa_database_first(u->database, &key, NULL);
1358 while (!done && idx < 256) {
1359 pa_datum next_key;
1361 done = !pa_database_next(u->database, &key, &next_key, NULL);
1363 device = pa_xnew(struct device_t, 1);
1364 device->device = pa_xstrndup(key.data, key.size);
1365 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1366 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1368 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
1369 if (pa_hashmap_put(h, device->device, device) == 0
1370 && (e = entry_read(u, device->device))) {
1371 /* We add offset on to the existing priorirty so that when we order, the
1372 existing entries are always lower priority than the new ones. */
1373 device->prio = (offset + e->priority[role_index]);
1374 pa_xfree(e);
1376 else {
1377 pa_xfree(device->device);
1378 pa_xfree(device);
1380 } else {
1381 pa_xfree(device->device);
1382 pa_xfree(device);
1385 pa_datum_free(&key);
1387 key = next_key;
1390 /*pa_log_debug("Hashmap contents (combined with database)");
1391 PA_HASHMAP_FOREACH(device, h, state) {
1392 pa_log_debug(" - %s (%d)", device->device, device->prio);
1395 /* Now we put all the entries in a simple list for sorting it. */
1396 n_devices = pa_hashmap_size(h);
1397 devices = pa_xnew(struct device_t *, n_devices);
1398 idx = 0;
1399 while ((device = pa_hashmap_steal_first(h))) {
1400 devices[idx++] = device;
1402 pa_hashmap_free(h, NULL, NULL);
1404 /* Simple bubble sort */
1405 for (i = 0; i < n_devices; ++i) {
1406 for (uint32_t j = i; j < n_devices; ++j) {
1407 if (devices[i]->prio > devices[j]->prio) {
1408 struct device_t *tmp;
1409 tmp = devices[i];
1410 devices[i] = devices[j];
1411 devices[j] = tmp;
1416 /*pa_log_debug("Sorted device list");
1417 for (i = 0; i < n_devices; ++i) {
1418 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1421 /* Go through in order and write the new entry and cleanup our own list */
1422 idx = 1;
1423 first = TRUE;
1424 for (i = 0; i < n_devices; ++i) {
1425 if ((e = entry_read(u, devices[i]->device))) {
1426 if (e->priority[role_index] == idx)
1427 idx++;
1428 else {
1429 e->priority[role_index] = idx;
1431 if (entry_write(u, (char *) devices[i]->device, e)) {
1432 first = FALSE;
1433 idx++;
1437 pa_xfree(e);
1439 pa_xfree(devices[i]->device);
1440 pa_xfree(devices[i]);
1443 if (!first) {
1444 trigger_save(u);
1446 if (sink_mode)
1447 route_sink_inputs(u, NULL);
1448 else
1449 route_source_outputs(u, NULL);
1452 break;
1455 case SUBCOMMAND_SUBSCRIBE: {
1457 pa_bool_t enabled;
1459 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1460 !pa_tagstruct_eof(t))
1461 goto fail;
1463 if (enabled)
1464 pa_idxset_put(u->subscribed, c, NULL);
1465 else
1466 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1468 break;
1471 default:
1472 goto fail;
1475 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1476 return 0;
1478 fail:
1480 if (reply)
1481 pa_tagstruct_free(reply);
1483 return -1;
1486 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1487 pa_assert(p);
1488 pa_assert(c);
1489 pa_assert(u);
1491 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1492 return PA_HOOK_OK;
1495 struct prioritised_indexes {
1496 uint32_t index;
1497 int32_t priority;
1500 int pa__init(pa_module*m) {
1501 pa_modargs *ma = NULL;
1502 struct userdata *u;
1503 char *fname;
1504 pa_sink *sink;
1505 pa_source *source;
1506 uint32_t idx;
1507 pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
1508 uint32_t total_devices;
1510 pa_assert(m);
1512 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1513 pa_log("Failed to parse module arguments");
1514 goto fail;
1517 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1518 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1519 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1520 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1521 goto fail;
1524 m->userdata = u = pa_xnew0(struct userdata, 1);
1525 u->core = m->core;
1526 u->module = m;
1527 u->do_routing = do_routing;
1528 u->on_hotplug = on_hotplug;
1529 u->on_rescue = on_rescue;
1530 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1532 u->protocol = pa_native_protocol_get(m->core);
1533 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1535 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);
1537 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);
1539 /* Used to handle device description management */
1540 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);
1541 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);
1543 /* The following slots are used to deal with routing */
1544 /* A little bit later than module-stream-restore, but before module-intended-roles */
1545 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);
1546 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 if (on_hotplug) {
1549 /* A little bit later than module-stream-restore, but before module-intended-roles */
1550 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);
1551 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 if (on_rescue) {
1555 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1556 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);
1557 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);
1560 if (!(fname = pa_state_path("device-manager", TRUE)))
1561 goto fail;
1563 if (!(u->database = pa_database_open(fname, TRUE))) {
1564 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1565 pa_xfree(fname);
1566 goto fail;
1569 pa_log_info("Successfully opened database file '%s'.", fname);
1570 pa_xfree(fname);
1572 /* Attempt to inject the devices into the list in priority order */
1573 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1574 if (total_devices > 0 && total_devices < 128) {
1575 uint32_t i;
1576 struct prioritised_indexes p_i[128];
1578 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1579 i = 0;
1580 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1581 pa_log_debug("Found sink index %u", sink->index);
1582 p_i[i ].index = sink->index;
1583 p_i[i++].priority = sink->priority;
1585 /* Bubble sort it (only really useful for first time creation) */
1586 if (i > 1)
1587 for (uint32_t j = 0; j < i; ++j)
1588 for (uint32_t k = 0; k < i; ++k)
1589 if (p_i[j].priority > p_i[k].priority) {
1590 struct prioritised_indexes tmp_pi = p_i[k];
1591 p_i[k] = p_i[j];
1592 p_i[j] = tmp_pi;
1594 /* Register it */
1595 for (uint32_t j = 0; j < i; ++j)
1596 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1599 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1600 i = 0;
1601 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1602 p_i[i ].index = source->index;
1603 p_i[i++].priority = source->priority;
1605 /* Bubble sort it (only really useful for first time creation) */
1606 if (i > 1)
1607 for (uint32_t j = 0; j < i; ++j)
1608 for (uint32_t k = 0; k < i; ++k)
1609 if (p_i[j].priority > p_i[k].priority) {
1610 struct prioritised_indexes tmp_pi = p_i[k];
1611 p_i[k] = p_i[j];
1612 p_i[j] = tmp_pi;
1614 /* Register it */
1615 for (uint32_t j = 0; j < i; ++j)
1616 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1618 else if (total_devices > 0) {
1619 /* This user has a *lot* of devices... */
1620 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1621 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1623 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1624 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1627 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1628 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1629 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1632 route_sink_inputs(u, NULL);
1633 route_source_outputs(u, NULL);
1635 #ifdef DUMP_DATABASE
1636 dump_database(u);
1637 #endif
1639 pa_modargs_free(ma);
1640 return 0;
1642 fail:
1643 pa__done(m);
1645 if (ma)
1646 pa_modargs_free(ma);
1648 return -1;
1651 void pa__done(pa_module*m) {
1652 struct userdata* u;
1654 pa_assert(m);
1656 if (!(u = m->userdata))
1657 return;
1659 if (u->subscription)
1660 pa_subscription_free(u->subscription);
1662 if (u->sink_new_hook_slot)
1663 pa_hook_slot_free(u->sink_new_hook_slot);
1664 if (u->source_new_hook_slot)
1665 pa_hook_slot_free(u->source_new_hook_slot);
1667 if (u->sink_input_new_hook_slot)
1668 pa_hook_slot_free(u->sink_input_new_hook_slot);
1669 if (u->source_output_new_hook_slot)
1670 pa_hook_slot_free(u->source_output_new_hook_slot);
1672 if (u->sink_put_hook_slot)
1673 pa_hook_slot_free(u->sink_put_hook_slot);
1674 if (u->source_put_hook_slot)
1675 pa_hook_slot_free(u->source_put_hook_slot);
1677 if (u->sink_unlink_hook_slot)
1678 pa_hook_slot_free(u->sink_unlink_hook_slot);
1679 if (u->source_unlink_hook_slot)
1680 pa_hook_slot_free(u->source_unlink_hook_slot);
1682 if (u->connection_unlink_hook_slot)
1683 pa_hook_slot_free(u->connection_unlink_hook_slot);
1685 if (u->save_time_event)
1686 u->core->mainloop->time_free(u->save_time_event);
1688 if (u->database)
1689 pa_database_close(u->database);
1691 if (u->protocol) {
1692 pa_native_protocol_remove_ext(u->protocol, m);
1693 pa_native_protocol_unref(u->protocol);
1696 if (u->subscribed)
1697 pa_idxset_free(u->subscribed, NULL, NULL);
1699 pa_xfree(u);