]> code.delx.au - pulseaudio/blob - src/modules/module-device-manager.c
9df3d8e2e55159792b43ef5368e2bf535bc6a78e
[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 bool on_hotplug;
124 bool on_rescue;
125 bool 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 bool 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 /* Forward declarations */
153 #ifdef DUMP_DATABASE
154 static void dump_database(struct userdata *);
155 #endif
156 static void notify_subscribers(struct userdata *);
157
158 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
159 struct userdata *u = userdata;
160
161 pa_assert(a);
162 pa_assert(e);
163 pa_assert(u);
164
165 pa_assert(e == u->save_time_event);
166 u->core->mainloop->time_free(u->save_time_event);
167 u->save_time_event = NULL;
168
169 pa_database_sync(u->database);
170 pa_log_info("Synced.");
171
172 #ifdef DUMP_DATABASE
173 dump_database(u);
174 #endif
175 }
176
177 static void trigger_save(struct userdata *u) {
178
179 pa_assert(u);
180
181 notify_subscribers(u);
182
183 if (u->save_time_event)
184 return;
185
186 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
187 }
188
189 static struct entry* entry_new(void) {
190 struct entry *r = pa_xnew0(struct entry, 1);
191 r->version = ENTRY_VERSION;
192 return r;
193 }
194
195 static void entry_free(struct entry* e) {
196 pa_assert(e);
197
198 pa_xfree(e->description);
199 pa_xfree(e->icon);
200 pa_xfree(e);
201 }
202
203 static bool entry_write(struct userdata *u, const char *name, const struct entry *e) {
204 pa_tagstruct *t;
205 pa_datum key, data;
206 bool r;
207
208 pa_assert(u);
209 pa_assert(name);
210 pa_assert(e);
211
212 t = pa_tagstruct_new(NULL, 0);
213 pa_tagstruct_putu8(t, e->version);
214 pa_tagstruct_puts(t, e->description);
215 pa_tagstruct_put_boolean(t, e->user_set_description);
216 pa_tagstruct_puts(t, e->icon);
217 for (uint8_t i=0; i<ROLE_MAX; ++i)
218 pa_tagstruct_putu32(t, e->priority[i]);
219
220 key.data = (char *) name;
221 key.size = strlen(name);
222
223 data.data = (void*)pa_tagstruct_data(t, &data.size);
224
225 r = (pa_database_set(u->database, &key, &data, true) == 0);
226
227 pa_tagstruct_free(t);
228
229 return r;
230 }
231
232 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
233
234 #define LEGACY_ENTRY_VERSION 1
235 static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
236 struct legacy_entry {
237 uint8_t version;
238 char description[PA_NAME_MAX];
239 bool user_set_description;
240 char icon[PA_NAME_MAX];
241 role_indexes_t priority;
242 } PA_GCC_PACKED;
243 struct legacy_entry *le;
244 struct entry *e;
245
246 pa_assert(u);
247 pa_assert(data);
248
249 if (data->size != sizeof(struct legacy_entry)) {
250 pa_log_debug("Size does not match.");
251 return NULL;
252 }
253
254 le = (struct legacy_entry*)data->data;
255
256 if (le->version != LEGACY_ENTRY_VERSION) {
257 pa_log_debug("Version mismatch.");
258 return NULL;
259 }
260
261 if (!memchr(le->description, 0, sizeof(le->description))) {
262 pa_log_warn("Description has missing NUL byte.");
263 return NULL;
264 }
265
266 if (!le->description[0]) {
267 pa_log_warn("Description is empty.");
268 return NULL;
269 }
270
271 if (!memchr(le->icon, 0, sizeof(le->icon))) {
272 pa_log_warn("Icon has missing NUL byte.");
273 return NULL;
274 }
275
276 e = entry_new();
277 e->description = pa_xstrdup(le->description);
278 e->icon = pa_xstrdup(le->icon);
279 return e;
280 }
281 #endif
282
283 static struct entry* entry_read(struct userdata *u, const char *name) {
284 pa_datum key, data;
285 struct entry *e = NULL;
286 pa_tagstruct *t = NULL;
287 const char *description, *icon;
288
289 pa_assert(u);
290 pa_assert(name);
291
292 key.data = (char*) name;
293 key.size = strlen(name);
294
295 pa_zero(data);
296
297 if (!pa_database_get(u->database, &key, &data))
298 goto fail;
299
300 t = pa_tagstruct_new(data.data, data.size);
301 e = entry_new();
302
303 if (pa_tagstruct_getu8(t, &e->version) < 0 ||
304 e->version > ENTRY_VERSION ||
305 pa_tagstruct_gets(t, &description) < 0 ||
306 pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
307 pa_tagstruct_gets(t, &icon) < 0) {
308
309 goto fail;
310 }
311
312 if (e->user_set_description && !description) {
313 pa_log("Entry has user_set_description set, but the description is NULL.");
314 goto fail;
315 }
316
317 if (e->user_set_description && !*description) {
318 pa_log("Entry has user_set_description set, but the description is empty.");
319 goto fail;
320 }
321
322 e->description = pa_xstrdup(description);
323 e->icon = pa_xstrdup(icon);
324
325 for (uint8_t i=0; i<ROLE_MAX; ++i) {
326 if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
327 goto fail;
328 }
329
330 if (!pa_tagstruct_eof(t))
331 goto fail;
332
333 pa_tagstruct_free(t);
334 pa_datum_free(&data);
335
336 return e;
337
338 fail:
339 pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
340
341 if (e)
342 entry_free(e);
343 if (t)
344 pa_tagstruct_free(t);
345
346 #ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
347 pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
348 if ((e = legacy_entry_read(u, &data))) {
349 pa_log_debug("Success. Saving new format for key: %s", name);
350 if (entry_write(u, name, e))
351 trigger_save(u);
352 pa_datum_free(&data);
353 return e;
354 } else
355 pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
356 #endif
357
358 pa_datum_free(&data);
359 return NULL;
360 }
361
362 #ifdef DUMP_DATABASE
363 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) {
364 pa_assert(u);
365 pa_assert(human);
366
367 if (sink_mode) {
368 pa_sink *s;
369 if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
370 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
371 else
372 pa_log_debug(" %s No sink specified", human);
373 } else {
374 pa_source *s;
375 if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
376 pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
377 else
378 pa_log_debug(" %s No source specified", human);
379 }
380 }
381
382 static void dump_database(struct userdata *u) {
383 pa_datum key;
384 bool done;
385
386 pa_assert(u);
387
388 done = !pa_database_first(u->database, &key, NULL);
389
390 pa_log_debug("Dumping database");
391 while (!done) {
392 char *name;
393 struct entry *e;
394 pa_datum next_key;
395
396 done = !pa_database_next(u->database, &key, &next_key, NULL);
397
398 name = pa_xstrndup(key.data, key.size);
399
400 if ((e = entry_read(u, name))) {
401 pa_log_debug(" Got entry: %s", name);
402 pa_log_debug(" Description: %s", e->description);
403 pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
404 e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
405 pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
406 e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
407 entry_free(e);
408 }
409
410 pa_xfree(name);
411
412 pa_datum_free(&key);
413 key = next_key;
414 }
415
416 if (u->do_routing) {
417 pa_log_debug(" Highest priority devices per-role:");
418
419 pa_log_debug(" Sinks:");
420 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
421 char name[13];
422 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
423 strncpy(name, role_names[role], len);
424 for (int i = len+1; i < 12; ++i) name[i] = ' ';
425 name[len] = ':'; name[0] -= 32; name[12] = '\0';
426 dump_database_helper(u, role, name, true);
427 }
428
429 pa_log_debug(" Sources:");
430 for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
431 char name[13];
432 uint32_t len = PA_MIN(12u, strlen(role_names[role]));
433 strncpy(name, role_names[role], len);
434 for (int i = len+1; i < 12; ++i) name[i] = ' ';
435 name[len] = ':'; name[0] -= 32; name[12] = '\0';
436 dump_database_helper(u, role, name, false);
437 }
438 }
439
440 pa_log_debug("Completed database dump");
441 }
442 #endif
443
444 static void notify_subscribers(struct userdata *u) {
445
446 pa_native_connection *c;
447 uint32_t idx;
448
449 pa_assert(u);
450
451 PA_IDXSET_FOREACH(c, u->subscribed, idx) {
452 pa_tagstruct *t;
453
454 t = pa_tagstruct_new(NULL, 0);
455 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
456 pa_tagstruct_putu32(t, 0);
457 pa_tagstruct_putu32(t, u->module->index);
458 pa_tagstruct_puts(t, u->module->name);
459 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
460
461 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
462 }
463 }
464
465 static bool entries_equal(const struct entry *a, const struct entry *b) {
466
467 pa_assert(a);
468 pa_assert(b);
469
470 if (!pa_streq(a->description, b->description)
471 || a->user_set_description != b->user_set_description
472 || !pa_streq(a->icon, b->icon))
473 return false;
474
475 for (int i=0; i < NUM_ROLES; ++i)
476 if (a->priority[i] != b->priority[i])
477 return false;
478
479 return true;
480 }
481
482 static char *get_name(const char *key, const char *prefix) {
483 char *t;
484
485 if (strncmp(key, prefix, strlen(prefix)))
486 return NULL;
487
488 t = pa_xstrdup(key + strlen(prefix));
489 return t;
490 }
491
492 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
493 struct entry *old;
494
495 pa_assert(u);
496 pa_assert(entry);
497 pa_assert(name);
498 pa_assert(prefix);
499
500 if ((old = entry_read(u, name))) {
501 *entry = *old;
502 entry->description = pa_xstrdup(old->description);
503 entry->icon = pa_xstrdup(old->icon);
504 } else {
505 /* This is a new device, so make sure we write it's priority list correctly */
506 role_indexes_t max_priority;
507 pa_datum key;
508 bool done;
509
510 pa_zero(max_priority);
511 done = !pa_database_first(u->database, &key, NULL);
512
513 /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
514 while (!done) {
515 pa_datum next_key;
516
517 done = !pa_database_next(u->database, &key, &next_key, NULL);
518
519 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
520 char *name2;
521 struct entry *e;
522
523 name2 = pa_xstrndup(key.data, key.size);
524
525 if ((e = entry_read(u, name2))) {
526 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
527 max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
528 }
529
530 entry_free(e);
531 }
532
533 pa_xfree(name2);
534 }
535 pa_datum_free(&key);
536 key = next_key;
537 }
538
539 /* Actually initialise our entry now we've calculated it */
540 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
541 entry->priority[i] = max_priority[i] + 1;
542 }
543 entry->user_set_description = false;
544 }
545
546 return old;
547 }
548
549 static uint32_t get_role_index(const char* role) {
550 pa_assert(role);
551
552 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
553 if (pa_streq(role, role_names[i]))
554 return i;
555
556 return PA_INVALID_INDEX;
557 }
558
559 static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
560 role_indexes_t *indexes, highest_priority_available;
561 pa_datum key;
562 bool done, sink_mode;
563
564 pa_assert(u);
565 pa_assert(prefix);
566
567 sink_mode = pa_streq(prefix, "sink:");
568
569 if (sink_mode)
570 indexes = &u->preferred_sinks;
571 else
572 indexes = &u->preferred_sources;
573
574 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
575 (*indexes)[i] = PA_INVALID_INDEX;
576 }
577 pa_zero(highest_priority_available);
578
579 done = !pa_database_first(u->database, &key, NULL);
580
581 /* Find all existing devices with the same prefix so we find the highest priority device for each role */
582 while (!done) {
583 pa_datum next_key;
584
585 done = !pa_database_next(u->database, &key, &next_key, NULL);
586
587 if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
588 char *name, *device_name;
589 struct entry *e;
590
591 name = pa_xstrndup(key.data, key.size);
592 pa_assert_se(device_name = get_name(name, prefix));
593
594 if ((e = entry_read(u, name))) {
595 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
596 if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
597 /* We've found a device with a higher priority than that we've currently got,
598 so see if it is currently available or not and update our list */
599 uint32_t idx;
600 bool found = false;
601
602 if (sink_mode) {
603 pa_sink *sink;
604
605 PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
606 if ((pa_sink*) ignore_device == sink)
607 continue;
608 if (pa_streq(sink->name, device_name)) {
609 found = true;
610 idx = sink->index; /* Is this needed? */
611 break;
612 }
613 }
614 } else {
615 pa_source *source;
616
617 PA_IDXSET_FOREACH(source, u->core->sources, idx) {
618 if ((pa_source*) ignore_device == source)
619 continue;
620 if (pa_streq(source->name, device_name)) {
621 found = true;
622 idx = source->index; /* Is this needed? */
623 break;
624 }
625 }
626 }
627 if (found) {
628 highest_priority_available[i] = e->priority[i];
629 (*indexes)[i] = idx;
630 }
631
632 }
633 }
634
635 entry_free(e);
636 }
637
638 pa_xfree(name);
639 pa_xfree(device_name);
640 }
641
642 pa_datum_free(&key);
643 key = next_key;
644 }
645 }
646
647 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
648 const char *role;
649 uint32_t role_index, device_index;
650 pa_sink *sink;
651
652 pa_assert(u);
653 pa_assert(u->do_routing);
654
655 if (si->save_sink)
656 return;
657
658 /* Skip this if it is already in the process of being moved anyway */
659 if (!si->sink)
660 return;
661
662 /* It might happen that a stream and a sink are set up at the
663 same time, in which case we want to make sure we don't
664 interfere with that */
665 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
666 return;
667
668 if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
669 role_index = get_role_index("none");
670 else
671 role_index = get_role_index(role);
672
673 if (PA_INVALID_INDEX == role_index)
674 return;
675
676 device_index = u->preferred_sinks[role_index];
677 if (PA_INVALID_INDEX == device_index)
678 return;
679
680 if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
681 return;
682
683 if (si->sink != sink)
684 pa_sink_input_move_to(si, sink, false);
685 }
686
687 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
688 pa_sink_input *si;
689 uint32_t idx;
690
691 pa_assert(u);
692
693 if (!u->do_routing)
694 return PA_HOOK_OK;
695
696 update_highest_priority_device_indexes(u, "sink:", ignore_sink);
697
698 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
699 route_sink_input(u, si);
700 }
701
702 return PA_HOOK_OK;
703 }
704
705 static void route_source_output(struct userdata *u, pa_source_output *so) {
706 const char *role;
707 uint32_t role_index, device_index;
708 pa_source *source;
709
710 pa_assert(u);
711 pa_assert(u->do_routing);
712
713 if (so->save_source)
714 return;
715
716 if (so->direct_on_input)
717 return;
718
719 /* Skip this if it is already in the process of being moved anyway */
720 if (!so->source)
721 return;
722
723 /* It might happen that a stream and a source are set up at the
724 same time, in which case we want to make sure we don't
725 interfere with that */
726 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
727 return;
728
729 if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
730 role_index = get_role_index("none");
731 else
732 role_index = get_role_index(role);
733
734 if (PA_INVALID_INDEX == role_index)
735 return;
736
737 device_index = u->preferred_sources[role_index];
738 if (PA_INVALID_INDEX == device_index)
739 return;
740
741 if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
742 return;
743
744 if (so->source != source)
745 pa_source_output_move_to(so, source, false);
746 }
747
748 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
749 pa_source_output *so;
750 uint32_t idx;
751
752 pa_assert(u);
753
754 if (!u->do_routing)
755 return PA_HOOK_OK;
756
757 update_highest_priority_device_indexes(u, "source:", ignore_source);
758
759 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
760 route_source_output(u, so);
761 }
762
763 return PA_HOOK_OK;
764 }
765
766 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
767 struct userdata *u = userdata;
768 struct entry *entry, *old = NULL;
769 char *name = NULL;
770
771 pa_assert(c);
772 pa_assert(u);
773
774 if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
775 t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
776 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
777 t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
778
779 /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
780 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
781 /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
782 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
783 return;
784
785 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
786 pa_sink_input *si;
787
788 if (!u->do_routing)
789 return;
790 if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
791 return;
792
793 /* The role may change mid-stream, so we reroute */
794 route_sink_input(u, si);
795
796 return;
797 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
798 pa_source_output *so;
799
800 if (!u->do_routing)
801 return;
802 if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
803 return;
804
805 /* The role may change mid-stream, so we reroute */
806 route_source_output(u, so);
807
808 return;
809 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
810 pa_sink *sink;
811
812 if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
813 return;
814
815 entry = entry_new();
816 name = pa_sprintf_malloc("sink:%s", sink->name);
817
818 old = load_or_initialize_entry(u, entry, name, "sink:");
819
820 if (!entry->user_set_description) {
821 pa_xfree(entry->description);
822 entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
823 } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
824 /* Warning: If two modules fight over the description, this could cause an infinite loop.
825 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
826 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
827 the description, this will fail... */
828 pa_sink_set_description(sink, entry->description);
829 }
830
831 pa_xfree(entry->icon);
832 entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
833
834 } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
835 pa_source *source;
836
837 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
838
839 if (!(source = pa_idxset_get_by_index(c->sources, idx)))
840 return;
841
842 if (source->monitor_of)
843 return;
844
845 entry = entry_new();
846 name = pa_sprintf_malloc("source:%s", source->name);
847
848 old = load_or_initialize_entry(u, entry, name, "source:");
849
850 if (!entry->user_set_description) {
851 pa_xfree(entry->description);
852 entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
853 } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
854 /* Warning: If two modules fight over the description, this could cause an infinite loop.
855 by changing the description here, we retrigger this subscription callback. The only thing stopping us from
856 looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
857 the description, this will fail... */
858 pa_source_set_description(source, entry->description);
859 }
860
861 pa_xfree(entry->icon);
862 entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
863 } else {
864 pa_assert_not_reached();
865 }
866
867 pa_assert(name);
868
869 if (old) {
870
871 if (entries_equal(old, entry)) {
872 entry_free(old);
873 entry_free(entry);
874 pa_xfree(name);
875
876 return;
877 }
878
879 entry_free(old);
880 }
881
882 pa_log_info("Storing device %s.", name);
883
884 if (entry_write(u, name, entry))
885 trigger_save(u);
886 else
887 pa_log_warn("Could not save device");;
888
889 entry_free(entry);
890 pa_xfree(name);
891 }
892
893 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
894 char *name;
895 struct entry *e;
896
897 pa_assert(c);
898 pa_assert(new_data);
899 pa_assert(u);
900
901 name = pa_sprintf_malloc("sink:%s", new_data->name);
902
903 if ((e = entry_read(u, name))) {
904 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
905 pa_log_info("Restoring description for sink %s.", new_data->name);
906 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
907 }
908
909 entry_free(e);
910 }
911
912 pa_xfree(name);
913
914 return PA_HOOK_OK;
915 }
916
917 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
918 char *name;
919 struct entry *e;
920
921 pa_assert(c);
922 pa_assert(new_data);
923 pa_assert(u);
924
925 name = pa_sprintf_malloc("source:%s", new_data->name);
926
927 if ((e = entry_read(u, name))) {
928 if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
929 /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
930 pa_log_info("Restoring description for source %s.", new_data->name);
931 pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
932 }
933
934 entry_free(e);
935 }
936
937 pa_xfree(name);
938
939 return PA_HOOK_OK;
940 }
941
942 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
943 const char *role;
944 uint32_t role_index;
945
946 pa_assert(c);
947 pa_assert(new_data);
948 pa_assert(u);
949
950 if (!u->do_routing)
951 return PA_HOOK_OK;
952
953 if (new_data->sink)
954 pa_log_debug("Not restoring device for stream because already set.");
955 else {
956 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
957 role_index = get_role_index("none");
958 else
959 role_index = get_role_index(role);
960
961 if (PA_INVALID_INDEX != role_index) {
962 uint32_t device_index;
963
964 device_index = u->preferred_sinks[role_index];
965 if (PA_INVALID_INDEX != device_index) {
966 pa_sink *sink;
967
968 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
969 if (!pa_sink_input_new_data_set_sink(new_data, sink, false))
970 pa_log_debug("Not restoring device for stream because no supported format was found");
971 }
972 }
973 }
974 }
975
976 return PA_HOOK_OK;
977 }
978
979 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
980 const char *role;
981 uint32_t role_index;
982
983 pa_assert(c);
984 pa_assert(new_data);
985 pa_assert(u);
986
987 if (!u->do_routing)
988 return PA_HOOK_OK;
989
990 if (new_data->direct_on_input)
991 return PA_HOOK_OK;
992
993 if (new_data->source)
994 pa_log_debug("Not restoring device for stream because already set.");
995 else {
996 if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
997 role_index = get_role_index("none");
998 else
999 role_index = get_role_index(role);
1000
1001 if (PA_INVALID_INDEX != role_index) {
1002 uint32_t device_index;
1003
1004 device_index = u->preferred_sources[role_index];
1005 if (PA_INVALID_INDEX != device_index) {
1006 pa_source *source;
1007
1008 if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
1009 if (!pa_source_output_new_data_set_source(new_data, source, false))
1010 pa_log_debug("Not restoring device for stream because no supported format was found");
1011 }
1012 }
1013 }
1014
1015 return PA_HOOK_OK;
1016 }
1017
1018 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
1019 pa_assert(c);
1020 pa_assert(u);
1021 pa_assert(u->core == c);
1022 pa_assert(u->on_hotplug);
1023
1024 notify_subscribers(u);
1025
1026 return route_sink_inputs(u, NULL);
1027 }
1028
1029 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
1030 pa_assert(c);
1031 pa_assert(u);
1032 pa_assert(u->core == c);
1033 pa_assert(u->on_hotplug);
1034
1035 notify_subscribers(u);
1036
1037 return route_source_outputs(u, NULL);
1038 }
1039
1040 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
1041 pa_assert(c);
1042 pa_assert(sink);
1043 pa_assert(u);
1044 pa_assert(u->core == c);
1045 pa_assert(u->on_rescue);
1046
1047 /* There's no point in doing anything if the core is shut down anyway */
1048 if (c->state == PA_CORE_SHUTDOWN)
1049 return PA_HOOK_OK;
1050
1051 notify_subscribers(u);
1052
1053 return route_sink_inputs(u, sink);
1054 }
1055
1056 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
1057 pa_assert(c);
1058 pa_assert(source);
1059 pa_assert(u);
1060 pa_assert(u->core == c);
1061 pa_assert(u->on_rescue);
1062
1063 /* There's no point in doing anything if the core is shut down anyway */
1064 if (c->state == PA_CORE_SHUTDOWN)
1065 return PA_HOOK_OK;
1066
1067 notify_subscribers(u);
1068
1069 return route_source_outputs(u, source);
1070 }
1071
1072 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
1073 uint32_t idx;
1074 char *n;
1075
1076 pa_assert(u);
1077 pa_assert(name);
1078 pa_assert(e);
1079
1080 if (!e->user_set_description)
1081 return;
1082
1083 if ((n = get_name(name, "sink:"))) {
1084 pa_sink *s;
1085 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1086 if (!pa_streq(s->name, n)) {
1087 continue;
1088 }
1089
1090 pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
1091 pa_sink_set_description(s, e->description);
1092 }
1093 pa_xfree(n);
1094 }
1095 else if ((n = get_name(name, "source:"))) {
1096 pa_source *s;
1097 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1098 if (!pa_streq(s->name, n)) {
1099 continue;
1100 }
1101
1102 if (s->monitor_of) {
1103 pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
1104 continue;
1105 }
1106
1107 pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
1108 pa_source_set_description(s, e->description);
1109 }
1110 pa_xfree(n);
1111 }
1112 }
1113
1114 #define EXT_VERSION 1
1115
1116 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
1117 struct userdata *u;
1118 uint32_t command;
1119 pa_tagstruct *reply = NULL;
1120
1121 pa_assert(p);
1122 pa_assert(m);
1123 pa_assert(c);
1124 pa_assert(t);
1125
1126 u = m->userdata;
1127
1128 if (pa_tagstruct_getu32(t, &command) < 0)
1129 goto fail;
1130
1131 reply = pa_tagstruct_new(NULL, 0);
1132 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
1133 pa_tagstruct_putu32(reply, tag);
1134
1135 switch (command) {
1136 case SUBCOMMAND_TEST: {
1137 if (!pa_tagstruct_eof(t))
1138 goto fail;
1139
1140 pa_tagstruct_putu32(reply, EXT_VERSION);
1141 break;
1142 }
1143
1144 case SUBCOMMAND_READ: {
1145 pa_datum key;
1146 bool done;
1147
1148 if (!pa_tagstruct_eof(t))
1149 goto fail;
1150
1151 done = !pa_database_first(u->database, &key, NULL);
1152
1153 while (!done) {
1154 pa_datum next_key;
1155 struct entry *e;
1156 char *name;
1157
1158 done = !pa_database_next(u->database, &key, &next_key, NULL);
1159
1160 name = pa_xstrndup(key.data, key.size);
1161 pa_datum_free(&key);
1162
1163 if ((e = entry_read(u, name))) {
1164 uint32_t idx;
1165 char *device_name;
1166 uint32_t found_index = PA_INVALID_INDEX;
1167
1168 if ((device_name = get_name(name, "sink:"))) {
1169 pa_sink* s;
1170 PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
1171 if (pa_streq(s->name, device_name)) {
1172 found_index = s->index;
1173 break;
1174 }
1175 }
1176 pa_xfree(device_name);
1177 } else if ((device_name = get_name(name, "source:"))) {
1178 pa_source* s;
1179 PA_IDXSET_FOREACH(s, u->core->sources, idx) {
1180 if (pa_streq(s->name, device_name)) {
1181 found_index = s->index;
1182 break;
1183 }
1184 }
1185 pa_xfree(device_name);
1186 }
1187
1188 pa_tagstruct_puts(reply, name);
1189 pa_tagstruct_puts(reply, e->description);
1190 pa_tagstruct_puts(reply, e->icon);
1191 pa_tagstruct_putu32(reply, found_index);
1192 pa_tagstruct_putu32(reply, NUM_ROLES);
1193
1194 for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
1195 pa_tagstruct_puts(reply, role_names[i]);
1196 pa_tagstruct_putu32(reply, e->priority[i]);
1197 }
1198
1199 entry_free(e);
1200 }
1201
1202 pa_xfree(name);
1203
1204 key = next_key;
1205 }
1206
1207 break;
1208 }
1209
1210 case SUBCOMMAND_RENAME: {
1211
1212 struct entry *e;
1213 const char *device, *description;
1214
1215 if (pa_tagstruct_gets(t, &device) < 0 ||
1216 pa_tagstruct_gets(t, &description) < 0)
1217 goto fail;
1218
1219 if (!device || !*device || !description || !*description)
1220 goto fail;
1221
1222 if ((e = entry_read(u, device))) {
1223 pa_xfree(e->description);
1224 e->description = pa_xstrdup(description);
1225 e->user_set_description = true;
1226
1227 if (entry_write(u, (char *)device, e)) {
1228 apply_entry(u, device, e);
1229
1230 trigger_save(u);
1231 }
1232 else
1233 pa_log_warn("Could not save device");
1234
1235 entry_free(e);
1236 }
1237 else
1238 pa_log_warn("Could not rename device %s, no entry in database", device);
1239
1240 break;
1241 }
1242
1243 case SUBCOMMAND_DELETE:
1244
1245 while (!pa_tagstruct_eof(t)) {
1246 const char *name;
1247 pa_datum key;
1248
1249 if (pa_tagstruct_gets(t, &name) < 0)
1250 goto fail;
1251
1252 key.data = (char*) name;
1253 key.size = strlen(name);
1254
1255 /** @todo: Reindex the priorities */
1256 pa_database_unset(u->database, &key);
1257 }
1258
1259 trigger_save(u);
1260
1261 break;
1262
1263 case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
1264
1265 bool enable;
1266
1267 if (pa_tagstruct_get_boolean(t, &enable) < 0)
1268 goto fail;
1269
1270 if ((u->do_routing = enable)) {
1271 /* Update our caches */
1272 update_highest_priority_device_indexes(u, "sink:", NULL);
1273 update_highest_priority_device_indexes(u, "source:", NULL);
1274 }
1275
1276 break;
1277 }
1278
1279 case SUBCOMMAND_REORDER: {
1280
1281 const char *role;
1282 struct entry *e;
1283 uint32_t role_index, n_devices;
1284 pa_datum key;
1285 bool done, sink_mode = true;
1286 struct device_t { uint32_t prio; char *device; };
1287 struct device_t *device;
1288 struct device_t **devices;
1289 uint32_t i, idx, offset;
1290 pa_hashmap *h;
1291 /*void *state;*/
1292 bool first;
1293
1294 if (pa_tagstruct_gets(t, &role) < 0 ||
1295 pa_tagstruct_getu32(t, &n_devices) < 0 ||
1296 n_devices < 1)
1297 goto fail;
1298
1299 if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
1300 goto fail;
1301
1302 /* Cycle through the devices given and make sure they exist */
1303 h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1304 first = true;
1305 idx = 0;
1306 for (i = 0; i < n_devices; ++i) {
1307 const char *s;
1308 if (pa_tagstruct_gets(t, &s) < 0) {
1309 while ((device = pa_hashmap_steal_first(h))) {
1310 pa_xfree(device->device);
1311 pa_xfree(device);
1312 }
1313
1314 pa_hashmap_free(h);
1315 pa_log_error("Protocol error on reorder");
1316 goto fail;
1317 }
1318
1319 /* Ensure this is a valid entry */
1320 if (!(e = entry_read(u, s))) {
1321 while ((device = pa_hashmap_steal_first(h))) {
1322 pa_xfree(device->device);
1323 pa_xfree(device);
1324 }
1325
1326 pa_hashmap_free(h);
1327 pa_log_error("Client specified an unknown device in it's reorder list.");
1328 goto fail;
1329 }
1330 entry_free(e);
1331
1332 if (first) {
1333 first = false;
1334 sink_mode = (0 == strncmp("sink:", s, 5));
1335 } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) {
1336 while ((device = pa_hashmap_steal_first(h))) {
1337 pa_xfree(device->device);
1338 pa_xfree(device);
1339 }
1340
1341 pa_hashmap_free(h);
1342 pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
1343 goto fail;
1344 }
1345
1346 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1347 device = pa_xnew(struct device_t, 1);
1348 device->device = pa_xstrdup(s);
1349 if (pa_hashmap_put(h, device->device, device) == 0) {
1350 device->prio = idx;
1351 idx++;
1352 } else {
1353 pa_xfree(device->device);
1354 pa_xfree(device);
1355 }
1356 }
1357
1358 /*pa_log_debug("Hashmap contents (received from client)");
1359 PA_HASHMAP_FOREACH(device, h, state) {
1360 pa_log_debug(" - %s (%d)", device->device, device->prio);
1361 }*/
1362
1363 /* Now cycle through our list and add all the devices.
1364 This has the effect of adding in any in our DB,
1365 not specified in the device list (and thus will be
1366 tacked on at the end) */
1367 offset = idx;
1368 done = !pa_database_first(u->database, &key, NULL);
1369
1370 while (!done && idx < 256) {
1371 pa_datum next_key;
1372
1373 done = !pa_database_next(u->database, &key, &next_key, NULL);
1374
1375 device = pa_xnew(struct device_t, 1);
1376 device->device = pa_xstrndup(key.data, key.size);
1377 if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
1378 || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
1379
1380 /* Add the device to our hashmap. If it's already in it, free it now and carry on */
1381 if (pa_hashmap_put(h, device->device, device) == 0
1382 && (e = entry_read(u, device->device))) {
1383 /* We add offset on to the existing priority so that when we order, the
1384 existing entries are always lower priority than the new ones. */
1385 device->prio = (offset + e->priority[role_index]);
1386 pa_xfree(e);
1387 }
1388 else {
1389 pa_xfree(device->device);
1390 pa_xfree(device);
1391 }
1392 } else {
1393 pa_xfree(device->device);
1394 pa_xfree(device);
1395 }
1396
1397 pa_datum_free(&key);
1398
1399 key = next_key;
1400 }
1401
1402 /*pa_log_debug("Hashmap contents (combined with database)");
1403 PA_HASHMAP_FOREACH(device, h, state) {
1404 pa_log_debug(" - %s (%d)", device->device, device->prio);
1405 }*/
1406
1407 /* Now we put all the entries in a simple list for sorting it. */
1408 n_devices = pa_hashmap_size(h);
1409 devices = pa_xnew(struct device_t *, n_devices);
1410 idx = 0;
1411 while ((device = pa_hashmap_steal_first(h))) {
1412 devices[idx++] = device;
1413 }
1414 pa_hashmap_free(h);
1415
1416 /* Simple bubble sort */
1417 for (i = 0; i < n_devices; ++i) {
1418 for (uint32_t j = i; j < n_devices; ++j) {
1419 if (devices[i]->prio > devices[j]->prio) {
1420 struct device_t *tmp;
1421 tmp = devices[i];
1422 devices[i] = devices[j];
1423 devices[j] = tmp;
1424 }
1425 }
1426 }
1427
1428 /*pa_log_debug("Sorted device list");
1429 for (i = 0; i < n_devices; ++i) {
1430 pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio);
1431 }*/
1432
1433 /* Go through in order and write the new entry and cleanup our own list */
1434 idx = 1;
1435 first = true;
1436 for (i = 0; i < n_devices; ++i) {
1437 if ((e = entry_read(u, devices[i]->device))) {
1438 if (e->priority[role_index] == idx)
1439 idx++;
1440 else {
1441 e->priority[role_index] = idx;
1442
1443 if (entry_write(u, (char *) devices[i]->device, e)) {
1444 first = false;
1445 idx++;
1446 }
1447 }
1448
1449 pa_xfree(e);
1450 }
1451 pa_xfree(devices[i]->device);
1452 pa_xfree(devices[i]);
1453 }
1454
1455 pa_xfree(devices);
1456
1457 if (!first) {
1458 trigger_save(u);
1459
1460 if (sink_mode)
1461 route_sink_inputs(u, NULL);
1462 else
1463 route_source_outputs(u, NULL);
1464 }
1465
1466 break;
1467 }
1468
1469 case SUBCOMMAND_SUBSCRIBE: {
1470
1471 bool enabled;
1472
1473 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
1474 !pa_tagstruct_eof(t))
1475 goto fail;
1476
1477 if (enabled)
1478 pa_idxset_put(u->subscribed, c, NULL);
1479 else
1480 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1481
1482 break;
1483 }
1484
1485 default:
1486 goto fail;
1487 }
1488
1489 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
1490 return 0;
1491
1492 fail:
1493
1494 if (reply)
1495 pa_tagstruct_free(reply);
1496
1497 return -1;
1498 }
1499
1500 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
1501 pa_assert(p);
1502 pa_assert(c);
1503 pa_assert(u);
1504
1505 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1506 return PA_HOOK_OK;
1507 }
1508
1509 struct prioritised_indexes {
1510 uint32_t index;
1511 int32_t priority;
1512 };
1513
1514 int pa__init(pa_module*m) {
1515 pa_modargs *ma = NULL;
1516 struct userdata *u;
1517 char *fname;
1518 pa_sink *sink;
1519 pa_source *source;
1520 uint32_t idx;
1521 bool do_routing = false, on_hotplug = true, on_rescue = true;
1522 uint32_t total_devices;
1523
1524 pa_assert(m);
1525
1526 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1527 pa_log("Failed to parse module arguments");
1528 goto fail;
1529 }
1530
1531 if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
1532 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1533 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1534 pa_log("on_hotplug= and on_rescue= expect boolean arguments");
1535 goto fail;
1536 }
1537
1538 m->userdata = u = pa_xnew0(struct userdata, 1);
1539 u->core = m->core;
1540 u->module = m;
1541 u->do_routing = do_routing;
1542 u->on_hotplug = on_hotplug;
1543 u->on_rescue = on_rescue;
1544 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1545
1546 u->protocol = pa_native_protocol_get(m->core);
1547 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1548
1549 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);
1550
1551 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);
1552
1553 /* Used to handle device description management */
1554 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);
1555 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);
1556
1557 /* The following slots are used to deal with routing */
1558 /* A little bit later than module-stream-restore, but before module-intended-roles */
1559 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);
1560 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);
1561
1562 if (on_hotplug) {
1563 /* A little bit later than module-stream-restore, but before module-intended-roles */
1564 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);
1565 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);
1566 }
1567
1568 if (on_rescue) {
1569 /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
1570 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);
1571 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);
1572 }
1573
1574 if (!(fname = pa_state_path("device-manager", true)))
1575 goto fail;
1576
1577 if (!(u->database = pa_database_open(fname, true))) {
1578 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1579 pa_xfree(fname);
1580 goto fail;
1581 }
1582
1583 pa_log_info("Successfully opened database file '%s'.", fname);
1584 pa_xfree(fname);
1585
1586 /* Attempt to inject the devices into the list in priority order */
1587 total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources));
1588 if (total_devices > 0 && total_devices < 128) {
1589 uint32_t i;
1590 struct prioritised_indexes p_i[128];
1591
1592 /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
1593 i = 0;
1594 PA_IDXSET_FOREACH(sink, m->core->sinks, idx) {
1595 pa_log_debug("Found sink index %u", sink->index);
1596 p_i[i ].index = sink->index;
1597 p_i[i++].priority = sink->priority;
1598 }
1599 /* Bubble sort it (only really useful for first time creation) */
1600 if (i > 1)
1601 for (uint32_t j = 0; j < i; ++j)
1602 for (uint32_t k = 0; k < i; ++k)
1603 if (p_i[j].priority > p_i[k].priority) {
1604 struct prioritised_indexes tmp_pi = p_i[k];
1605 p_i[k] = p_i[j];
1606 p_i[j] = tmp_pi;
1607 }
1608 /* Register it */
1609 for (uint32_t j = 0; j < i; ++j)
1610 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1611
1612 /* We cycle over all the available sources so that they are added to our database if they are not in it yet */
1613 i = 0;
1614 PA_IDXSET_FOREACH(source, m->core->sources, idx) {
1615 p_i[i ].index = source->index;
1616 p_i[i++].priority = source->priority;
1617 }
1618 /* Bubble sort it (only really useful for first time creation) */
1619 if (i > 1)
1620 for (uint32_t j = 0; j < i; ++j)
1621 for (uint32_t k = 0; k < i; ++k)
1622 if (p_i[j].priority > p_i[k].priority) {
1623 struct prioritised_indexes tmp_pi = p_i[k];
1624 p_i[k] = p_i[j];
1625 p_i[j] = tmp_pi;
1626 }
1627 /* Register it */
1628 for (uint32_t j = 0; j < i; ++j)
1629 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u);
1630 }
1631 else if (total_devices > 0) {
1632 /* This user has a *lot* of devices... */
1633 PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
1634 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
1635
1636 PA_IDXSET_FOREACH(source, m->core->sources, idx)
1637 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
1638 }
1639
1640 /* Perform the routing (if it's enabled) which will update our priority list cache too */
1641 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
1642 u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
1643 }
1644
1645 route_sink_inputs(u, NULL);
1646 route_source_outputs(u, NULL);
1647
1648 #ifdef DUMP_DATABASE
1649 dump_database(u);
1650 #endif
1651
1652 pa_modargs_free(ma);
1653 return 0;
1654
1655 fail:
1656 pa__done(m);
1657
1658 if (ma)
1659 pa_modargs_free(ma);
1660
1661 return -1;
1662 }
1663
1664 void pa__done(pa_module*m) {
1665 struct userdata* u;
1666
1667 pa_assert(m);
1668
1669 if (!(u = m->userdata))
1670 return;
1671
1672 if (u->subscription)
1673 pa_subscription_free(u->subscription);
1674
1675 if (u->sink_new_hook_slot)
1676 pa_hook_slot_free(u->sink_new_hook_slot);
1677 if (u->source_new_hook_slot)
1678 pa_hook_slot_free(u->source_new_hook_slot);
1679
1680 if (u->sink_input_new_hook_slot)
1681 pa_hook_slot_free(u->sink_input_new_hook_slot);
1682 if (u->source_output_new_hook_slot)
1683 pa_hook_slot_free(u->source_output_new_hook_slot);
1684
1685 if (u->sink_put_hook_slot)
1686 pa_hook_slot_free(u->sink_put_hook_slot);
1687 if (u->source_put_hook_slot)
1688 pa_hook_slot_free(u->source_put_hook_slot);
1689
1690 if (u->sink_unlink_hook_slot)
1691 pa_hook_slot_free(u->sink_unlink_hook_slot);
1692 if (u->source_unlink_hook_slot)
1693 pa_hook_slot_free(u->source_unlink_hook_slot);
1694
1695 if (u->connection_unlink_hook_slot)
1696 pa_hook_slot_free(u->connection_unlink_hook_slot);
1697
1698 if (u->save_time_event)
1699 u->core->mainloop->time_free(u->save_time_event);
1700
1701 if (u->database)
1702 pa_database_close(u->database);
1703
1704 if (u->protocol) {
1705 pa_native_protocol_remove_ext(u->protocol, m);
1706 pa_native_protocol_unref(u->protocol);
1707 }
1708
1709 if (u->subscribed)
1710 pa_idxset_free(u->subscribed, NULL);
1711
1712 pa_xfree(u);
1713 }