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