]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
Remove unnecessary #includes
[pulseaudio] / src / modules / module-device-manager.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006-2008 Lennart Poettering
5 Copyright 2009 Colin Guthrie
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
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
34 #include <pulse/gccmacro.h>
35 #include <pulse/xmalloc.h>
36 #include <pulse/timeval.h>
37 #include <pulse/rtclock.h>
38
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>
53
54 #include "module-device-manager-symdef.h"
55
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?>");
64
65 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
66 #define DUMP_DATABASE
67
68 static const char* const valid_modargs[] = {
69 "do_routing",
70 "on_hotplug",
71 "on_rescue",
72 NULL
73 };
74
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
87 };
88
89 typedef uint32_t role_indexes_t[NUM_ROLES];
90
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",
101 };
102
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;
119
120 pa_native_protocol *protocol;
121 pa_idxset *subscribed;
122
123 pa_bool_t on_hotplug;
124 pa_bool_t on_rescue;
125 pa_bool_t do_routing;
126
127 role_indexes_t preferred_sinks;
128 role_indexes_t preferred_sources;
129 };
130
131 #define ENTRY_VERSION 1
132
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;
139 };
140
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
150 };
151
152
153 /* Forward declarations */
154 #ifdef DUMP_DATABASE
155 static void dump_database(struct userdata *);
156 #endif
157 static void notify_subscribers(struct userdata *);
158
159
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;
162
163 pa_assert(a);
164 pa_assert(e);
165 pa_assert(u);
166
167 pa_assert(e == u->save_time_event);
168 u->core->mainloop->time_free(u->save_time_event);
169 u->save_time_event = NULL;
170
171 pa_database_sync(u->database);
172 pa_log_info("Synced.");
173
174 #ifdef DUMP_DATABASE
175 dump_database(u);
176 #endif
177 }
178
179 static void trigger_save(struct userdata *u) {
180
181 pa_assert(u);
182
183 notify_subscribers(u);
184
185 if (u->save_time_event)
186 return;
187
188 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
189 }
190
191 static struct entry* entry_new(void) {
192 struct entry *r = pa_xnew0(struct entry, 1);
193 r->version = ENTRY_VERSION;
194 return r;
195 }
196
197 static void entry_free(struct entry* e) {
198 pa_assert(e);
199
200 pa_xfree(e->description);
201 pa_xfree(e->icon);
202 }
203
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;
208
209 pa_assert(u);
210 pa_assert(name);
211 pa_assert(e);
212
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]);
220
221 key.data = (char *) name;
222 key.size = strlen(name);
223
224 data.data = (void*)pa_tagstruct_data(t, &data.size);
225
226 r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
227
228 pa_tagstruct_free(t);
229
230 return r;
231 }
232
233 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
234
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;
246
247 pa_assert(u);
248 pa_assert(data);
249
250 if (data->size != sizeof(struct legacy_entry)) {
251 pa_log_debug("Size does not match.");
252 return NULL;
253 }
254
255 le = (struct legacy_entry*)data->data;
256
257 if (le->version != LEGACY_ENTRY_VERSION) {
258 pa_log_debug("Version mismatch.");
259 return NULL;
260 }
261
262 if (!memchr(le->description, 0, sizeof(le->description))) {
263 pa_log_warn("Description has missing NUL byte.");
264 return NULL;
265 }
266
267 if (!memchr(le->icon, 0, sizeof(le->icon))) {
268 pa_log_warn("Icon has missing NUL byte.");
269 return NULL;
270 }
271
272 e = entry_new();
273 e->description = pa_xstrdup(le->description);
274 e->icon = pa_xstrdup(le->icon);
275 return e;
276 }
277 #endif
278
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;
284
285 pa_assert(u);
286 pa_assert(name);
287
288 key.data = (char*) name;
289 key.size = strlen(name);
290
291 pa_zero(data);
292
293 if (!pa_database_get(u->database, &key, &data))
294 goto fail;
295
296 t = pa_tagstruct_new(data.data, data.size);
297 e = entry_new();
298
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) {
304
305 goto fail;
306 }
307
308 e->description = pa_xstrdup(description);
309 e->icon = pa_xstrdup(icon);
310
311 for (uint8_t i=0; i<ROLE_MAX; ++i) {
312 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
313 goto fail;
314 }
315
316 if (!pa_tagstruct_eof(t))
317 goto fail;
318
319 pa_tagstruct_free(t);
320 pa_datum_free(&data);
321
322 return e;
323
324 fail:
325 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
326
327 if (e)
328 entry_free(e);
329 if (t)
330 pa_tagstruct_free(t);
331
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
343
344 pa_datum_free(&data);
345 return NULL;
346 }
347
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);
352
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);
365 }
366 }
367
368 static void dump_database(struct userdata *u) {
369 pa_datum key;
370 pa_bool_t done;
371
372 pa_assert(u);
373
374 done = !pa_database_first(u->database, &key, NULL);
375
376 pa_log_debug("Dumping database");
377 while (!done) {
378 char *name;
379 struct entry *e;
380 pa_datum next_key;
381
382 done = !pa_database_next(u->database, &key, &next_key, NULL);
383
384 name = pa_xstrndup(key.data, key.size);
385
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);
394 }
395
396 pa_xfree(name);
397
398 pa_datum_free(&key);
399 key = next_key;
400 }
401
402 if (u->do_routing) {
403 pa_log_debug(" Highest priority devices per-role:");
404
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);
413 }
414
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);
423 }
424 }
425
426 pa_log_debug("Completed database dump");
427 }
428 #endif
429
430 static void notify_subscribers(struct userdata *u) {
431
432 pa_native_connection *c;
433 uint32_t idx;
434
435 pa_assert(u);
436
437 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
438 pa_tagstruct *t;
439
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);
446
447 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
448 }
449 }
450
451 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
452
453 pa_assert(a);
454 pa_assert(b);
455
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;
460
461 for (int i=0; i < NUM_ROLES; ++i)
462 if (a->priority[i] != b->priority[i])
463 return FALSE;
464
465 return TRUE;
466 }
467
468 static char *get_name(const char *key, const char *prefix) {
469 char *t;
470
471 if (strncmp(key, prefix, strlen(prefix)))
472 return NULL;
473
474 t = pa_xstrdup(key + strlen(prefix));
475 return t;
476 }
477
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;
480
481 pa_assert(u);
482 pa_assert(entry);
483 pa_assert(name);
484 pa_assert(prefix);
485
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;
495
496 pa_zero(max_priority);
497 done = !pa_database_first(u->database, &key, NULL);
498
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;
502
503 done = !pa_database_next(u->database, &key, &next_key, NULL);
504
505 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
506 char *name2;
507 struct entry *e;
508
509 name2 = pa_xstrndup(key.data, key.size);
510
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]);
514 }
515
516 entry_free(e);
517 }
518
519 pa_xfree(name2);
520 }
521 pa_datum_free(&key);
522 key = next_key;
523 }
524
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;
528 }
529 entry->user_set_description = FALSE;
530 }
531
532 return old;
533 }
534
535 static uint32_t get_role_index(const char* role) {
536 pa_assert(role);
537
538 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
539 if (strcmp(role, role_names[i]) == 0)
540 return i;
541
542 return PA_INVALID_INDEX;
543 }
544
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;
549
550 pa_assert(u);
551 pa_assert(prefix);
552
553 sink_mode = (strcmp(prefix, "sink:") == 0);
554
555 if (sink_mode)
556 indexes = &u->preferred_sinks;
557 else
558 indexes = &u->preferred_sources;
559
560 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
561 (*indexes)[i] = PA_INVALID_INDEX;
562 }
563 pa_zero(highest_priority_available);
564
565 done = !pa_database_first(u->database, &key, NULL);
566
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;
570
571 done = !pa_database_next(u->database, &key, &next_key, NULL);
572
573 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
574 char *name, *device_name;
575 struct entry *e;
576
577 name = pa_xstrndup(key.data, key.size);
578 device_name = get_name(name, prefix);
579
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;
587
588 if (sink_mode) {
589 pa_sink *sink;
590
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;
598 }
599 }
600 } else {
601 pa_source *source;
602
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;
610 }
611 }
612 }
613 if (found) {
614 highest_priority_available[i] = e->priority[i];
615 (*indexes)[i] = idx;
616 }
617
618 }
619 }
620
621 entry_free(e);
622 }
623
624 pa_xfree(name);
625 pa_xfree(device_name);
626 }
627
628 pa_datum_free(&key);
629 key = next_key;
630 }
631 }
632
633
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;
638
639 pa_assert(u);
640 pa_assert(u->do_routing);
641
642 if (si->save_sink)
643 return;
644
645 /* Skip this if it is already in the process of being moved anyway */
646 if (!si->sink)
647 return;
648
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;
654
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);
659
660 if (PA_INVALID_INDEX == role_index)
661 return;
662
663 device_index = u->preferred_sinks[role_index];
664 if (PA_INVALID_INDEX == device_index)
665 return;
666
667 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
668 return;
669
670 if (si->sink != sink)
671 pa_sink_input_move_to(si, sink, FALSE);
672 }
673
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;
677
678 pa_assert(u);
679
680 if (!u->do_routing)
681 return PA_HOOK_OK;
682
683 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
684
685 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
686 route_sink_input(u, si);
687 }
688
689 return PA_HOOK_OK;
690 }
691
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;
696
697 pa_assert(u);
698 pa_assert(u->do_routing);
699
700 if (so->save_source)
701 return;
702
703 if (so->direct_on_input)
704 return;
705
706 /* Skip this if it is already in the process of being moved anyway */
707 if (!so->source)
708 return;
709
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;
715
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);
720
721 if (PA_INVALID_INDEX == role_index)
722 return;
723
724 device_index = u->preferred_sources[role_index];
725 if (PA_INVALID_INDEX == device_index)
726 return;
727
728 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
729 return;
730
731 if (so->source != source)
732 pa_source_output_move_to(so, source, FALSE);
733 }
734
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;
738
739 pa_assert(u);
740
741 if (!u->do_routing)
742 return PA_HOOK_OK;
743
744 update_highest_priority_device_indexes(u, "source:", ignore_source);
745
746 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
747 route_source_output(u, so);
748 }
749
750 return PA_HOOK_OK;
751 }
752
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;
757
758 pa_assert(c);
759 pa_assert(u);
760
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) &&
765
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;
771
772 entry = entry_new();
773
774 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
775 pa_sink_input *si;
776
777 if (!u->do_routing)
778 return;
779 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
780 return;
781
782 /* The role may change mid-stream, so we reroute */
783 route_sink_input(u, si);
784
785 return;
786 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
787 pa_source_output *so;
788
789 if (!u->do_routing)
790 return;
791 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
792 return;
793
794 /* The role may change mid-stream, so we reroute */
795 route_source_output(u, so);
796
797 return;
798 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
799 pa_sink *sink;
800
801 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
802 return;
803
804 name = pa_sprintf_malloc("sink:%s", sink->name);
805
806 old = load_or_initialize_entry(u, entry, name, "sink:");
807
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);
817 }
818
819 pa_xfree(entry->icon);
820 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
821
822 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
823 pa_source *source;
824
825 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
826
827 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
828 return;
829
830 if (source->monitor_of)
831 return;
832
833 name = pa_sprintf_malloc("source:%s", source->name);
834
835 old = load_or_initialize_entry(u, entry, name, "source:");
836
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);
846 }
847
848 pa_xfree(entry->icon);
849 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
850 }
851
852 pa_assert(name);
853
854 if (old) {
855
856 if (entries_equal(old, entry)) {
857 entry_free(old);
858 entry_free(entry);
859 pa_xfree(name);
860
861 return;
862 }
863
864 entry_free(old);
865 }
866
867 pa_log_info("Storing device %s.", name);
868
869 if (entry_write(u, name, entry))
870 trigger_save(u);
871 else
872 pa_log_warn("Could not save device");;
873
874 entry_free(entry);
875 pa_xfree(name);
876 }
877
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;
881
882 pa_assert(c);
883 pa_assert(new_data);
884 pa_assert(u);
885
886 name = pa_sprintf_malloc("sink:%s", new_data->name);
887
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);
892 }
893
894 entry_free(e);
895 }
896
897 pa_xfree(name);
898
899 return PA_HOOK_OK;
900 }
901
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;
905
906 pa_assert(c);
907 pa_assert(new_data);
908 pa_assert(u);
909
910 name = pa_sprintf_malloc("source:%s", new_data->name);
911
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);
917 }
918
919 entry_free(e);
920 }
921
922 pa_xfree(name);
923
924 return PA_HOOK_OK;
925 }
926
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;
930
931 pa_assert(c);
932 pa_assert(new_data);
933 pa_assert(u);
934
935 if (!u->do_routing)
936 return PA_HOOK_OK;
937
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);
945
946 if (PA_INVALID_INDEX != role_index) {
947 uint32_t device_index;
948
949 device_index = u->preferred_sinks[role_index];
950 if (PA_INVALID_INDEX != device_index) {
951 pa_sink *sink;
952
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");
956 }
957 }
958 }
959 }
960
961 return PA_HOOK_OK;
962 }
963
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;
967
968 pa_assert(c);
969 pa_assert(new_data);
970 pa_assert(u);
971
972 if (!u->do_routing)
973 return PA_HOOK_OK;
974
975 if (new_data->direct_on_input)
976 return PA_HOOK_OK;
977
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);
985
986 if (PA_INVALID_INDEX != role_index) {
987 uint32_t device_index;
988
989 device_index = u->preferred_sources[role_index];
990 if (PA_INVALID_INDEX != device_index) {
991 pa_source *source;
992
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");
996 }
997 }
998 }
999
1000 return PA_HOOK_OK;
1001 }
1002
1003
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);
1009
1010 notify_subscribers(u);
1011
1012 return route_sink_inputs(u, NULL);
1013 }
1014
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);
1020
1021 notify_subscribers(u);
1022
1023 return route_source_outputs(u, NULL);
1024 }
1025
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);
1032
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;
1036
1037 notify_subscribers(u);
1038
1039 return route_sink_inputs(u, sink);
1040 }
1041
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);
1048
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;
1052
1053 notify_subscribers(u);
1054
1055 return route_source_outputs(u, source);
1056 }
1057
1058
1059 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1060 uint32_t idx;
1061 char *n;
1062
1063 pa_assert(u);
1064 pa_assert(name);
1065 pa_assert(e);
1066
1067 if (!e->user_set_description)
1068 return;
1069
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;
1075 }
1076
1077 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1078 pa_sink_set_description(s, e->description);
1079 }
1080 pa_xfree(n);
1081 }
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;
1087 }
1088
1089 if (s->monitor_of) {
1090 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1091 continue;
1092 }
1093
1094 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1095 pa_source_set_description(s, e->description);
1096 }
1097 pa_xfree(n);
1098 }
1099 }
1100
1101
1102 #define EXT_VERSION 1
1103
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;
1108
1109 pa_assert(p);
1110 pa_assert(m);
1111 pa_assert(c);
1112 pa_assert(t);
1113
1114 u = m->userdata;
1115
1116 if (pa_tagstruct_getu32(t, &command) < 0)
1117 goto fail;
1118
1119 reply = pa_tagstruct_new(NULL, 0);
1120 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1121 pa_tagstruct_putu32(reply, tag);
1122
1123 switch (command) {
1124 case SUBCOMMAND_TEST: {
1125 if (!pa_tagstruct_eof(t))
1126 goto fail;
1127
1128 pa_tagstruct_putu32(reply, EXT_VERSION);
1129 break;
1130 }
1131
1132 case SUBCOMMAND_READ: {
1133 pa_datum key;
1134 pa_bool_t done;
1135
1136 if (!pa_tagstruct_eof(t))
1137 goto fail;
1138
1139 done = !pa_database_first(u->database, &key, NULL);
1140
1141 while (!done) {
1142 pa_datum next_key;
1143 struct entry *e;
1144 char *name;
1145
1146 done = !pa_database_next(u->database, &key, &next_key, NULL);
1147
1148 name = pa_xstrndup(key.data, key.size);
1149 pa_datum_free(&key);
1150
1151 if ((e = entry_read(u, name))) {
1152 uint32_t idx;
1153 char *device_name;
1154 uint32_t found_index = PA_INVALID_INDEX;
1155
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;
1162 }
1163 }
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;
1171 }
1172 }
1173 pa_xfree(device_name);
1174 }
1175
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);
1181
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]);
1185 }
1186
1187 entry_free(e);
1188 }
1189
1190 pa_xfree(name);
1191
1192 key = next_key;
1193 }
1194
1195 break;
1196 }
1197
1198 case SUBCOMMAND_RENAME: {
1199
1200 struct entry *e;
1201 const char *device, *description;
1202
1203 if (pa_tagstruct_gets(t, &device) < 0 ||
1204 pa_tagstruct_gets(t, &description) < 0)
1205 goto fail;
1206
1207 if (!device || !*device || !description || !*description)
1208 goto fail;
1209
1210 if ((e = entry_read(u, device))) {
1211 pa_xfree(e->description);
1212 e->description = pa_xstrdup(description);
1213 e->user_set_description = TRUE;
1214
1215 if (entry_write(u, (char *)device, e)) {
1216 apply_entry(u, device, e);
1217
1218 trigger_save(u);
1219 }
1220 else
1221 pa_log_warn("Could not save device");
1222
1223 entry_free(e);
1224 }
1225 else
1226 pa_log_warn("Could not rename device %s, no entry in database", device);
1227
1228 break;
1229 }
1230
1231 case SUBCOMMAND_DELETE:
1232
1233 while (!pa_tagstruct_eof(t)) {
1234 const char *name;
1235 pa_datum key;
1236
1237 if (pa_tagstruct_gets(t, &name) < 0)
1238 goto fail;
1239
1240 key.data = (char*) name;
1241 key.size = strlen(name);
1242
1243 /** @todo: Reindex the priorities */
1244 pa_database_unset(u->database, &key);
1245 }
1246
1247 trigger_save(u);
1248
1249 break;
1250
1251 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1252
1253 pa_bool_t enable;
1254
1255 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1256 goto fail;
1257
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);
1262 }
1263
1264 break;
1265 }
1266
1267 case SUBCOMMAND_REORDER: {
1268
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;
1281
1282 if (pa_tagstruct_gets(t, &role) < 0 ||
1283 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1284 n_devices < 1)
1285 goto fail;
1286
1287 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1288 goto fail;
1289
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);
1300 }
1301
1302 pa_hashmap_free(h, NULL, NULL);
1303 pa_log_error("Protocol error on reorder");
1304 goto fail;
1305 }
1306
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);
1312 }
1313
1314 pa_hashmap_free(h, NULL, NULL);
1315 pa_log_error("Client specified an unknown device in it's reorder list.");
1316 goto fail;
1317 }
1318 entry_free(e);
1319
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);
1327 }
1328
1329 pa_hashmap_free(h, NULL, NULL);
1330 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1331 goto fail;
1332 }
1333
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);
1343 }
1344 }
1345
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);
1349 }*/
1350
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);
1357
1358 while (!done && idx < 256) {
1359 pa_datum next_key;
1360
1361 done = !pa_database_next(u->database, &key, &next_key, NULL);
1362
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))) {
1367
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);
1375 }
1376 else {
1377 pa_xfree(device->device);
1378 pa_xfree(device);
1379 }
1380 } else {
1381 pa_xfree(device->device);
1382 pa_xfree(device);
1383 }
1384
1385 pa_datum_free(&key);
1386
1387 key = next_key;
1388 }
1389
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);
1393 }*/
1394
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;
1401 }
1402 pa_hashmap_free(h, NULL, NULL);
1403
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;
1412 }
1413 }
1414 }
1415
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);
1419 }*/
1420
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;
1430
1431 if (entry_write(u, (char *) devices[i]->device, e)) {
1432 first = FALSE;
1433 idx++;
1434 }
1435 }
1436
1437 pa_xfree(e);
1438 }
1439 pa_xfree(devices[i]->device);
1440 pa_xfree(devices[i]);
1441 }
1442
1443 if (!first) {
1444 trigger_save(u);
1445
1446 if (sink_mode)
1447 route_sink_inputs(u, NULL);
1448 else
1449 route_source_outputs(u, NULL);
1450 }
1451
1452 break;
1453 }
1454
1455 case SUBCOMMAND_SUBSCRIBE: {
1456
1457 pa_bool_t enabled;
1458
1459 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1460 !pa_tagstruct_eof(t))
1461 goto fail;
1462
1463 if (enabled)
1464 pa_idxset_put(u->subscribed, c, NULL);
1465 else
1466 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1467
1468 break;
1469 }
1470
1471 default:
1472 goto fail;
1473 }
1474
1475 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1476 return 0;
1477
1478 fail:
1479
1480 if (reply)
1481 pa_tagstruct_free(reply);
1482
1483 return -1;
1484 }
1485
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);
1490
1491 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1492 return PA_HOOK_OK;
1493 }
1494
1495 struct prioritised_indexes {
1496 uint32_t index;
1497 int32_t priority;
1498 };
1499
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;
1509
1510 pa_assert(m);
1511
1512 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1513 pa_log("Failed to parse module arguments");
1514 goto fail;
1515 }
1516
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;
1522 }
1523
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);
1531
1532 u->protocol = pa_native_protocol_get(m->core);
1533 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1534
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);
1536
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);
1538
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);
1542
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);
1547
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);
1552 }
1553
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);
1558 }
1559
1560 if (!(fname = pa_state_path("device-manager", TRUE)))
1561 goto fail;
1562
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;
1567 }
1568
1569 pa_log_info("Successfully opened database file '%s'.", fname);
1570 pa_xfree(fname);
1571
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];
1577
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;
1584 }
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;
1593 }
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);
1597
1598
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;
1604 }
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;
1613 }
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);
1617 }
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);
1622
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);
1625 }
1626
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;
1630 }
1631
1632 route_sink_inputs(u, NULL);
1633 route_source_outputs(u, NULL);
1634
1635 #ifdef DUMP_DATABASE
1636 dump_database(u);
1637 #endif
1638
1639 pa_modargs_free(ma);
1640 return 0;
1641
1642 fail:
1643 pa__done(m);
1644
1645 if (ma)
1646 pa_modargs_free(ma);
1647
1648 return -1;
1649 }
1650
1651 void pa__done(pa_module*m) {
1652 struct userdata* u;
1653
1654 pa_assert(m);
1655
1656 if (!(u = m->userdata))
1657 return;
1658
1659 if (u->subscription)
1660 pa_subscription_free(u->subscription);
1661
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);
1666
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);
1671
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);
1676
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);
1681
1682 if (u->connection_unlink_hook_slot)
1683 pa_hook_slot_free(u->connection_unlink_hook_slot);
1684
1685 if (u->save_time_event)
1686 u->core->mainloop->time_free(u->save_time_event);
1687
1688 if (u->database)
1689 pa_database_close(u->database);
1690
1691 if (u->protocol) {
1692 pa_native_protocol_remove_ext(u->protocol, m);
1693 pa_native_protocol_unref(u->protocol);
1694 }
1695
1696 if (u->subscribed)
1697 pa_idxset_free(u->subscribed, NULL, NULL);
1698
1699 pa_xfree(u);
1700 }