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