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