153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2006-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2009 Colin Guthrie 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <unistd.h> 2653a5a1b3Sopenharmony_ci#include <string.h> 2753a5a1b3Sopenharmony_ci#include <errno.h> 2853a5a1b3Sopenharmony_ci#include <sys/types.h> 2953a5a1b3Sopenharmony_ci#include <stdio.h> 3053a5a1b3Sopenharmony_ci#include <stdlib.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3453a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3553a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 3653a5a1b3Sopenharmony_ci 3753a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/source-output.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/protocol-native.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/pstream.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/pstream-util.h> 4953a5a1b3Sopenharmony_ci#include <pulsecore/database.h> 5053a5a1b3Sopenharmony_ci#include <pulsecore/tagstruct.h> 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Colin Guthrie"); 5353a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role"); 5453a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 5553a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(true); 5653a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 5753a5a1b3Sopenharmony_ci "do_routing=<Automatically route streams based on a priority list (unique per-role)?> " 5853a5a1b3Sopenharmony_ci "on_hotplug=<When new device becomes available, recheck streams?> " 5953a5a1b3Sopenharmony_ci "on_rescue=<When device becomes unavailable, recheck streams?>"); 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_ci#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) 6253a5a1b3Sopenharmony_ci#define DUMP_DATABASE 6353a5a1b3Sopenharmony_ci 6453a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 6553a5a1b3Sopenharmony_ci "do_routing", 6653a5a1b3Sopenharmony_ci "on_hotplug", 6753a5a1b3Sopenharmony_ci "on_rescue", 6853a5a1b3Sopenharmony_ci NULL 6953a5a1b3Sopenharmony_ci}; 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci#define NUM_ROLES 9 7253a5a1b3Sopenharmony_cienum { 7353a5a1b3Sopenharmony_ci ROLE_NONE, 7453a5a1b3Sopenharmony_ci ROLE_VIDEO, 7553a5a1b3Sopenharmony_ci ROLE_MUSIC, 7653a5a1b3Sopenharmony_ci ROLE_GAME, 7753a5a1b3Sopenharmony_ci ROLE_EVENT, 7853a5a1b3Sopenharmony_ci ROLE_PHONE, 7953a5a1b3Sopenharmony_ci ROLE_ANIMATION, 8053a5a1b3Sopenharmony_ci ROLE_PRODUCTION, 8153a5a1b3Sopenharmony_ci ROLE_A11Y, 8253a5a1b3Sopenharmony_ci ROLE_MAX 8353a5a1b3Sopenharmony_ci}; 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_citypedef uint32_t role_indexes_t[NUM_ROLES]; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_cistatic const char* role_names[NUM_ROLES] = { 8853a5a1b3Sopenharmony_ci "none", 8953a5a1b3Sopenharmony_ci "video", 9053a5a1b3Sopenharmony_ci "music", 9153a5a1b3Sopenharmony_ci "game", 9253a5a1b3Sopenharmony_ci "event", 9353a5a1b3Sopenharmony_ci "phone", 9453a5a1b3Sopenharmony_ci "animation", 9553a5a1b3Sopenharmony_ci "production", 9653a5a1b3Sopenharmony_ci "a11y", 9753a5a1b3Sopenharmony_ci}; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_cistruct userdata { 10053a5a1b3Sopenharmony_ci pa_core *core; 10153a5a1b3Sopenharmony_ci pa_module *module; 10253a5a1b3Sopenharmony_ci pa_subscription *subscription; 10353a5a1b3Sopenharmony_ci pa_hook_slot 10453a5a1b3Sopenharmony_ci *sink_new_hook_slot, 10553a5a1b3Sopenharmony_ci *source_new_hook_slot, 10653a5a1b3Sopenharmony_ci *sink_input_new_hook_slot, 10753a5a1b3Sopenharmony_ci *source_output_new_hook_slot, 10853a5a1b3Sopenharmony_ci *sink_put_hook_slot, 10953a5a1b3Sopenharmony_ci *source_put_hook_slot, 11053a5a1b3Sopenharmony_ci *sink_unlink_hook_slot, 11153a5a1b3Sopenharmony_ci *source_unlink_hook_slot, 11253a5a1b3Sopenharmony_ci *connection_unlink_hook_slot; 11353a5a1b3Sopenharmony_ci pa_time_event *save_time_event; 11453a5a1b3Sopenharmony_ci pa_database *database; 11553a5a1b3Sopenharmony_ci 11653a5a1b3Sopenharmony_ci pa_native_protocol *protocol; 11753a5a1b3Sopenharmony_ci pa_idxset *subscribed; 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci bool on_hotplug; 12053a5a1b3Sopenharmony_ci bool on_rescue; 12153a5a1b3Sopenharmony_ci bool do_routing; 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci role_indexes_t preferred_sinks; 12453a5a1b3Sopenharmony_ci role_indexes_t preferred_sources; 12553a5a1b3Sopenharmony_ci}; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci#define ENTRY_VERSION 1 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_cistruct entry { 13053a5a1b3Sopenharmony_ci uint8_t version; 13153a5a1b3Sopenharmony_ci char *description; 13253a5a1b3Sopenharmony_ci bool user_set_description; 13353a5a1b3Sopenharmony_ci char *icon; 13453a5a1b3Sopenharmony_ci role_indexes_t priority; 13553a5a1b3Sopenharmony_ci}; 13653a5a1b3Sopenharmony_ci 13753a5a1b3Sopenharmony_cienum { 13853a5a1b3Sopenharmony_ci SUBCOMMAND_TEST, 13953a5a1b3Sopenharmony_ci SUBCOMMAND_READ, 14053a5a1b3Sopenharmony_ci SUBCOMMAND_RENAME, 14153a5a1b3Sopenharmony_ci SUBCOMMAND_DELETE, 14253a5a1b3Sopenharmony_ci SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING, 14353a5a1b3Sopenharmony_ci SUBCOMMAND_REORDER, 14453a5a1b3Sopenharmony_ci SUBCOMMAND_SUBSCRIBE, 14553a5a1b3Sopenharmony_ci SUBCOMMAND_EVENT 14653a5a1b3Sopenharmony_ci}; 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci/* Forward declarations */ 14953a5a1b3Sopenharmony_ci#ifdef DUMP_DATABASE 15053a5a1b3Sopenharmony_cistatic void dump_database(struct userdata *); 15153a5a1b3Sopenharmony_ci#endif 15253a5a1b3Sopenharmony_cistatic void notify_subscribers(struct userdata *); 15353a5a1b3Sopenharmony_ci 15453a5a1b3Sopenharmony_cistatic void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { 15553a5a1b3Sopenharmony_ci struct userdata *u = userdata; 15653a5a1b3Sopenharmony_ci 15753a5a1b3Sopenharmony_ci pa_assert(a); 15853a5a1b3Sopenharmony_ci pa_assert(e); 15953a5a1b3Sopenharmony_ci pa_assert(u); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci pa_assert(e == u->save_time_event); 16253a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->save_time_event); 16353a5a1b3Sopenharmony_ci u->save_time_event = NULL; 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci pa_database_sync(u->database); 16653a5a1b3Sopenharmony_ci pa_log_info("Synced."); 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci#ifdef DUMP_DATABASE 16953a5a1b3Sopenharmony_ci dump_database(u); 17053a5a1b3Sopenharmony_ci#endif 17153a5a1b3Sopenharmony_ci} 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_cistatic void trigger_save(struct userdata *u) { 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci pa_assert(u); 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci notify_subscribers(u); 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci if (u->save_time_event) 18053a5a1b3Sopenharmony_ci return; 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u); 18353a5a1b3Sopenharmony_ci} 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_cistatic struct entry* entry_new(void) { 18653a5a1b3Sopenharmony_ci struct entry *r = pa_xnew0(struct entry, 1); 18753a5a1b3Sopenharmony_ci r->version = ENTRY_VERSION; 18853a5a1b3Sopenharmony_ci return r; 18953a5a1b3Sopenharmony_ci} 19053a5a1b3Sopenharmony_ci 19153a5a1b3Sopenharmony_cistatic void entry_free(struct entry* e) { 19253a5a1b3Sopenharmony_ci pa_assert(e); 19353a5a1b3Sopenharmony_ci 19453a5a1b3Sopenharmony_ci pa_xfree(e->description); 19553a5a1b3Sopenharmony_ci pa_xfree(e->icon); 19653a5a1b3Sopenharmony_ci pa_xfree(e); 19753a5a1b3Sopenharmony_ci} 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_cistatic bool entry_write(struct userdata *u, const char *name, const struct entry *e) { 20053a5a1b3Sopenharmony_ci pa_tagstruct *t; 20153a5a1b3Sopenharmony_ci pa_datum key, data; 20253a5a1b3Sopenharmony_ci bool r; 20353a5a1b3Sopenharmony_ci 20453a5a1b3Sopenharmony_ci pa_assert(u); 20553a5a1b3Sopenharmony_ci pa_assert(name); 20653a5a1b3Sopenharmony_ci pa_assert(e); 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci t = pa_tagstruct_new(); 20953a5a1b3Sopenharmony_ci pa_tagstruct_putu8(t, e->version); 21053a5a1b3Sopenharmony_ci pa_tagstruct_puts(t, e->description); 21153a5a1b3Sopenharmony_ci pa_tagstruct_put_boolean(t, e->user_set_description); 21253a5a1b3Sopenharmony_ci pa_tagstruct_puts(t, e->icon); 21353a5a1b3Sopenharmony_ci for (uint8_t i=0; i<ROLE_MAX; ++i) 21453a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, e->priority[i]); 21553a5a1b3Sopenharmony_ci 21653a5a1b3Sopenharmony_ci key.data = (char *) name; 21753a5a1b3Sopenharmony_ci key.size = strlen(name); 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci data.data = (void*)pa_tagstruct_data(t, &data.size); 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci r = (pa_database_set(u->database, &key, &data, true) == 0); 22253a5a1b3Sopenharmony_ci 22353a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci return r; 22653a5a1b3Sopenharmony_ci} 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci#define LEGACY_ENTRY_VERSION 1 23153a5a1b3Sopenharmony_cistatic struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) { 23253a5a1b3Sopenharmony_ci struct legacy_entry { 23353a5a1b3Sopenharmony_ci uint8_t version; 23453a5a1b3Sopenharmony_ci char description[PA_NAME_MAX]; 23553a5a1b3Sopenharmony_ci bool user_set_description; 23653a5a1b3Sopenharmony_ci char icon[PA_NAME_MAX]; 23753a5a1b3Sopenharmony_ci role_indexes_t priority; 23853a5a1b3Sopenharmony_ci } PA_GCC_PACKED; 23953a5a1b3Sopenharmony_ci struct legacy_entry *le; 24053a5a1b3Sopenharmony_ci struct entry *e; 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci pa_assert(u); 24353a5a1b3Sopenharmony_ci pa_assert(data); 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci if (data->size != sizeof(struct legacy_entry)) { 24653a5a1b3Sopenharmony_ci pa_log_debug("Size does not match."); 24753a5a1b3Sopenharmony_ci return NULL; 24853a5a1b3Sopenharmony_ci } 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci le = (struct legacy_entry*)data->data; 25153a5a1b3Sopenharmony_ci 25253a5a1b3Sopenharmony_ci if (le->version != LEGACY_ENTRY_VERSION) { 25353a5a1b3Sopenharmony_ci pa_log_debug("Version mismatch."); 25453a5a1b3Sopenharmony_ci return NULL; 25553a5a1b3Sopenharmony_ci } 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci if (!memchr(le->description, 0, sizeof(le->description))) { 25853a5a1b3Sopenharmony_ci pa_log_warn("Description has missing NUL byte."); 25953a5a1b3Sopenharmony_ci return NULL; 26053a5a1b3Sopenharmony_ci } 26153a5a1b3Sopenharmony_ci 26253a5a1b3Sopenharmony_ci if (!le->description[0]) { 26353a5a1b3Sopenharmony_ci pa_log_warn("Description is empty."); 26453a5a1b3Sopenharmony_ci return NULL; 26553a5a1b3Sopenharmony_ci } 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_ci if (!memchr(le->icon, 0, sizeof(le->icon))) { 26853a5a1b3Sopenharmony_ci pa_log_warn("Icon has missing NUL byte."); 26953a5a1b3Sopenharmony_ci return NULL; 27053a5a1b3Sopenharmony_ci } 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_ci e = entry_new(); 27353a5a1b3Sopenharmony_ci e->description = pa_xstrdup(le->description); 27453a5a1b3Sopenharmony_ci e->icon = pa_xstrdup(le->icon); 27553a5a1b3Sopenharmony_ci return e; 27653a5a1b3Sopenharmony_ci} 27753a5a1b3Sopenharmony_ci#endif 27853a5a1b3Sopenharmony_ci 27953a5a1b3Sopenharmony_cistatic struct entry* entry_read(struct userdata *u, const char *name) { 28053a5a1b3Sopenharmony_ci pa_datum key, data; 28153a5a1b3Sopenharmony_ci struct entry *e = NULL; 28253a5a1b3Sopenharmony_ci pa_tagstruct *t = NULL; 28353a5a1b3Sopenharmony_ci const char *description, *icon; 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci pa_assert(u); 28653a5a1b3Sopenharmony_ci pa_assert(name); 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci key.data = (char*) name; 28953a5a1b3Sopenharmony_ci key.size = strlen(name); 29053a5a1b3Sopenharmony_ci 29153a5a1b3Sopenharmony_ci pa_zero(data); 29253a5a1b3Sopenharmony_ci 29353a5a1b3Sopenharmony_ci if (!pa_database_get(u->database, &key, &data)) { 29453a5a1b3Sopenharmony_ci pa_log_debug("Database contains no data for key: %s", name); 29553a5a1b3Sopenharmony_ci return NULL; 29653a5a1b3Sopenharmony_ci } 29753a5a1b3Sopenharmony_ci 29853a5a1b3Sopenharmony_ci t = pa_tagstruct_new_fixed(data.data, data.size); 29953a5a1b3Sopenharmony_ci e = entry_new(); 30053a5a1b3Sopenharmony_ci 30153a5a1b3Sopenharmony_ci if (pa_tagstruct_getu8(t, &e->version) < 0 || 30253a5a1b3Sopenharmony_ci e->version > ENTRY_VERSION || 30353a5a1b3Sopenharmony_ci pa_tagstruct_gets(t, &description) < 0 || 30453a5a1b3Sopenharmony_ci pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 || 30553a5a1b3Sopenharmony_ci pa_tagstruct_gets(t, &icon) < 0) { 30653a5a1b3Sopenharmony_ci 30753a5a1b3Sopenharmony_ci goto fail; 30853a5a1b3Sopenharmony_ci } 30953a5a1b3Sopenharmony_ci 31053a5a1b3Sopenharmony_ci if (e->user_set_description && !description) { 31153a5a1b3Sopenharmony_ci pa_log("Entry has user_set_description set, but the description is NULL."); 31253a5a1b3Sopenharmony_ci goto fail; 31353a5a1b3Sopenharmony_ci } 31453a5a1b3Sopenharmony_ci 31553a5a1b3Sopenharmony_ci if (e->user_set_description && !*description) { 31653a5a1b3Sopenharmony_ci pa_log("Entry has user_set_description set, but the description is empty."); 31753a5a1b3Sopenharmony_ci goto fail; 31853a5a1b3Sopenharmony_ci } 31953a5a1b3Sopenharmony_ci 32053a5a1b3Sopenharmony_ci e->description = pa_xstrdup(description); 32153a5a1b3Sopenharmony_ci e->icon = pa_xstrdup(icon); 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci for (uint8_t i=0; i<ROLE_MAX; ++i) { 32453a5a1b3Sopenharmony_ci if (pa_tagstruct_getu32(t, &e->priority[i]) < 0) 32553a5a1b3Sopenharmony_ci goto fail; 32653a5a1b3Sopenharmony_ci } 32753a5a1b3Sopenharmony_ci 32853a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 32953a5a1b3Sopenharmony_ci goto fail; 33053a5a1b3Sopenharmony_ci 33153a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 33253a5a1b3Sopenharmony_ci pa_datum_free(&data); 33353a5a1b3Sopenharmony_ci 33453a5a1b3Sopenharmony_ci return e; 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_cifail: 33753a5a1b3Sopenharmony_ci pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name); 33853a5a1b3Sopenharmony_ci 33953a5a1b3Sopenharmony_ci if (e) 34053a5a1b3Sopenharmony_ci entry_free(e); 34153a5a1b3Sopenharmony_ci if (t) 34253a5a1b3Sopenharmony_ci pa_tagstruct_free(t); 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT 34553a5a1b3Sopenharmony_ci pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name); 34653a5a1b3Sopenharmony_ci if ((e = legacy_entry_read(u, &data))) { 34753a5a1b3Sopenharmony_ci pa_log_debug("Success. Saving new format for key: %s", name); 34853a5a1b3Sopenharmony_ci if (entry_write(u, name, e)) 34953a5a1b3Sopenharmony_ci trigger_save(u); 35053a5a1b3Sopenharmony_ci pa_datum_free(&data); 35153a5a1b3Sopenharmony_ci return e; 35253a5a1b3Sopenharmony_ci } else 35353a5a1b3Sopenharmony_ci pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name); 35453a5a1b3Sopenharmony_ci#endif 35553a5a1b3Sopenharmony_ci 35653a5a1b3Sopenharmony_ci pa_datum_free(&data); 35753a5a1b3Sopenharmony_ci return NULL; 35853a5a1b3Sopenharmony_ci} 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci#ifdef DUMP_DATABASE 36153a5a1b3Sopenharmony_cistatic void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, bool sink_mode) { 36253a5a1b3Sopenharmony_ci pa_assert(u); 36353a5a1b3Sopenharmony_ci pa_assert(human); 36453a5a1b3Sopenharmony_ci 36553a5a1b3Sopenharmony_ci if (sink_mode) { 36653a5a1b3Sopenharmony_ci pa_sink *s; 36753a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index]))) 36853a5a1b3Sopenharmony_ci pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name); 36953a5a1b3Sopenharmony_ci else 37053a5a1b3Sopenharmony_ci pa_log_debug(" %s No sink specified", human); 37153a5a1b3Sopenharmony_ci } else { 37253a5a1b3Sopenharmony_ci pa_source *s; 37353a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index]))) 37453a5a1b3Sopenharmony_ci pa_log_debug(" %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name); 37553a5a1b3Sopenharmony_ci else 37653a5a1b3Sopenharmony_ci pa_log_debug(" %s No source specified", human); 37753a5a1b3Sopenharmony_ci } 37853a5a1b3Sopenharmony_ci} 37953a5a1b3Sopenharmony_ci 38053a5a1b3Sopenharmony_cistatic void dump_database(struct userdata *u) { 38153a5a1b3Sopenharmony_ci pa_datum key; 38253a5a1b3Sopenharmony_ci bool done; 38353a5a1b3Sopenharmony_ci 38453a5a1b3Sopenharmony_ci pa_assert(u); 38553a5a1b3Sopenharmony_ci 38653a5a1b3Sopenharmony_ci done = !pa_database_first(u->database, &key, NULL); 38753a5a1b3Sopenharmony_ci 38853a5a1b3Sopenharmony_ci pa_log_debug("Dumping database"); 38953a5a1b3Sopenharmony_ci while (!done) { 39053a5a1b3Sopenharmony_ci char *name; 39153a5a1b3Sopenharmony_ci struct entry *e; 39253a5a1b3Sopenharmony_ci pa_datum next_key; 39353a5a1b3Sopenharmony_ci 39453a5a1b3Sopenharmony_ci done = !pa_database_next(u->database, &key, &next_key, NULL); 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci name = pa_xstrndup(key.data, key.size); 39753a5a1b3Sopenharmony_ci 39853a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 39953a5a1b3Sopenharmony_ci pa_log_debug(" Got entry: %s", name); 40053a5a1b3Sopenharmony_ci pa_log_debug(" Description: %s", e->description); 40153a5a1b3Sopenharmony_ci pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u", 40253a5a1b3Sopenharmony_ci e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]); 40353a5a1b3Sopenharmony_ci pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u", 40453a5a1b3Sopenharmony_ci e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]); 40553a5a1b3Sopenharmony_ci entry_free(e); 40653a5a1b3Sopenharmony_ci } 40753a5a1b3Sopenharmony_ci 40853a5a1b3Sopenharmony_ci pa_xfree(name); 40953a5a1b3Sopenharmony_ci 41053a5a1b3Sopenharmony_ci pa_datum_free(&key); 41153a5a1b3Sopenharmony_ci key = next_key; 41253a5a1b3Sopenharmony_ci } 41353a5a1b3Sopenharmony_ci 41453a5a1b3Sopenharmony_ci if (u->do_routing) { 41553a5a1b3Sopenharmony_ci pa_log_debug(" Highest priority devices per-role:"); 41653a5a1b3Sopenharmony_ci 41753a5a1b3Sopenharmony_ci pa_log_debug(" Sinks:"); 41853a5a1b3Sopenharmony_ci for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { 41953a5a1b3Sopenharmony_ci char name[13]; 42053a5a1b3Sopenharmony_ci uint32_t len = PA_MIN(12u, strlen(role_names[role])); 42153a5a1b3Sopenharmony_ci strncpy(name, role_names[role], len); 42253a5a1b3Sopenharmony_ci for (int i = len+1; i < 12; ++i) name[i] = ' '; 42353a5a1b3Sopenharmony_ci name[len] = ':'; name[0] -= 32; name[12] = '\0'; 42453a5a1b3Sopenharmony_ci dump_database_helper(u, role, name, true); 42553a5a1b3Sopenharmony_ci } 42653a5a1b3Sopenharmony_ci 42753a5a1b3Sopenharmony_ci pa_log_debug(" Sources:"); 42853a5a1b3Sopenharmony_ci for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) { 42953a5a1b3Sopenharmony_ci char name[13]; 43053a5a1b3Sopenharmony_ci uint32_t len = PA_MIN(12u, strlen(role_names[role])); 43153a5a1b3Sopenharmony_ci strncpy(name, role_names[role], len); 43253a5a1b3Sopenharmony_ci for (int i = len+1; i < 12; ++i) name[i] = ' '; 43353a5a1b3Sopenharmony_ci name[len] = ':'; name[0] -= 32; name[12] = '\0'; 43453a5a1b3Sopenharmony_ci dump_database_helper(u, role, name, false); 43553a5a1b3Sopenharmony_ci } 43653a5a1b3Sopenharmony_ci } 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci pa_log_debug("Completed database dump"); 43953a5a1b3Sopenharmony_ci} 44053a5a1b3Sopenharmony_ci#endif 44153a5a1b3Sopenharmony_ci 44253a5a1b3Sopenharmony_cistatic void notify_subscribers(struct userdata *u) { 44353a5a1b3Sopenharmony_ci 44453a5a1b3Sopenharmony_ci pa_native_connection *c; 44553a5a1b3Sopenharmony_ci uint32_t idx; 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci pa_assert(u); 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(c, u->subscribed, idx) { 45053a5a1b3Sopenharmony_ci pa_tagstruct *t; 45153a5a1b3Sopenharmony_ci 45253a5a1b3Sopenharmony_ci t = pa_tagstruct_new(); 45353a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION); 45453a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, 0); 45553a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, u->module->index); 45653a5a1b3Sopenharmony_ci pa_tagstruct_puts(t, u->module->name); 45753a5a1b3Sopenharmony_ci pa_tagstruct_putu32(t, SUBCOMMAND_EVENT); 45853a5a1b3Sopenharmony_ci 45953a5a1b3Sopenharmony_ci pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t); 46053a5a1b3Sopenharmony_ci } 46153a5a1b3Sopenharmony_ci} 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_cistatic bool entries_equal(const struct entry *a, const struct entry *b) { 46453a5a1b3Sopenharmony_ci 46553a5a1b3Sopenharmony_ci pa_assert(a); 46653a5a1b3Sopenharmony_ci pa_assert(b); 46753a5a1b3Sopenharmony_ci 46853a5a1b3Sopenharmony_ci if (!pa_streq(a->description, b->description) 46953a5a1b3Sopenharmony_ci || a->user_set_description != b->user_set_description 47053a5a1b3Sopenharmony_ci || !pa_streq(a->icon, b->icon)) 47153a5a1b3Sopenharmony_ci return false; 47253a5a1b3Sopenharmony_ci 47353a5a1b3Sopenharmony_ci for (int i=0; i < NUM_ROLES; ++i) 47453a5a1b3Sopenharmony_ci if (a->priority[i] != b->priority[i]) 47553a5a1b3Sopenharmony_ci return false; 47653a5a1b3Sopenharmony_ci 47753a5a1b3Sopenharmony_ci return true; 47853a5a1b3Sopenharmony_ci} 47953a5a1b3Sopenharmony_ci 48053a5a1b3Sopenharmony_cistatic char *get_name(const char *key, const char *prefix) { 48153a5a1b3Sopenharmony_ci char *t; 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci if (strncmp(key, prefix, strlen(prefix))) 48453a5a1b3Sopenharmony_ci return NULL; 48553a5a1b3Sopenharmony_ci 48653a5a1b3Sopenharmony_ci t = pa_xstrdup(key + strlen(prefix)); 48753a5a1b3Sopenharmony_ci return t; 48853a5a1b3Sopenharmony_ci} 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_cistatic inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) { 49153a5a1b3Sopenharmony_ci struct entry *old; 49253a5a1b3Sopenharmony_ci 49353a5a1b3Sopenharmony_ci pa_assert(u); 49453a5a1b3Sopenharmony_ci pa_assert(entry); 49553a5a1b3Sopenharmony_ci pa_assert(name); 49653a5a1b3Sopenharmony_ci pa_assert(prefix); 49753a5a1b3Sopenharmony_ci 49853a5a1b3Sopenharmony_ci if ((old = entry_read(u, name))) { 49953a5a1b3Sopenharmony_ci *entry = *old; 50053a5a1b3Sopenharmony_ci entry->description = pa_xstrdup(old->description); 50153a5a1b3Sopenharmony_ci entry->icon = pa_xstrdup(old->icon); 50253a5a1b3Sopenharmony_ci } else { 50353a5a1b3Sopenharmony_ci /* This is a new device, so make sure we write its priority list correctly */ 50453a5a1b3Sopenharmony_ci role_indexes_t max_priority; 50553a5a1b3Sopenharmony_ci pa_datum key; 50653a5a1b3Sopenharmony_ci bool done; 50753a5a1b3Sopenharmony_ci 50853a5a1b3Sopenharmony_ci pa_zero(max_priority); 50953a5a1b3Sopenharmony_ci done = !pa_database_first(u->database, &key, NULL); 51053a5a1b3Sopenharmony_ci 51153a5a1b3Sopenharmony_ci /* Find all existing devices with the same prefix so we calculate the current max priority for each role */ 51253a5a1b3Sopenharmony_ci while (!done) { 51353a5a1b3Sopenharmony_ci pa_datum next_key; 51453a5a1b3Sopenharmony_ci 51553a5a1b3Sopenharmony_ci done = !pa_database_next(u->database, &key, &next_key, NULL); 51653a5a1b3Sopenharmony_ci 51753a5a1b3Sopenharmony_ci if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { 51853a5a1b3Sopenharmony_ci char *name2; 51953a5a1b3Sopenharmony_ci struct entry *e; 52053a5a1b3Sopenharmony_ci 52153a5a1b3Sopenharmony_ci name2 = pa_xstrndup(key.data, key.size); 52253a5a1b3Sopenharmony_ci 52353a5a1b3Sopenharmony_ci if ((e = entry_read(u, name2))) { 52453a5a1b3Sopenharmony_ci for (uint32_t i = 0; i < NUM_ROLES; ++i) { 52553a5a1b3Sopenharmony_ci max_priority[i] = PA_MAX(max_priority[i], e->priority[i]); 52653a5a1b3Sopenharmony_ci } 52753a5a1b3Sopenharmony_ci 52853a5a1b3Sopenharmony_ci entry_free(e); 52953a5a1b3Sopenharmony_ci } 53053a5a1b3Sopenharmony_ci 53153a5a1b3Sopenharmony_ci pa_xfree(name2); 53253a5a1b3Sopenharmony_ci } 53353a5a1b3Sopenharmony_ci pa_datum_free(&key); 53453a5a1b3Sopenharmony_ci key = next_key; 53553a5a1b3Sopenharmony_ci } 53653a5a1b3Sopenharmony_ci 53753a5a1b3Sopenharmony_ci /* Actually initialise our entry now we've calculated it */ 53853a5a1b3Sopenharmony_ci for (uint32_t i = 0; i < NUM_ROLES; ++i) { 53953a5a1b3Sopenharmony_ci entry->priority[i] = max_priority[i] + 1; 54053a5a1b3Sopenharmony_ci } 54153a5a1b3Sopenharmony_ci entry->user_set_description = false; 54253a5a1b3Sopenharmony_ci } 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_ci return old; 54553a5a1b3Sopenharmony_ci} 54653a5a1b3Sopenharmony_ci 54753a5a1b3Sopenharmony_cistatic uint32_t get_role_index(const char* role) { 54853a5a1b3Sopenharmony_ci pa_assert(role); 54953a5a1b3Sopenharmony_ci 55053a5a1b3Sopenharmony_ci for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) 55153a5a1b3Sopenharmony_ci if (pa_streq(role, role_names[i])) 55253a5a1b3Sopenharmony_ci return i; 55353a5a1b3Sopenharmony_ci 55453a5a1b3Sopenharmony_ci return PA_INVALID_INDEX; 55553a5a1b3Sopenharmony_ci} 55653a5a1b3Sopenharmony_ci 55753a5a1b3Sopenharmony_cistatic void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) { 55853a5a1b3Sopenharmony_ci role_indexes_t *indexes, highest_priority_available; 55953a5a1b3Sopenharmony_ci pa_datum key; 56053a5a1b3Sopenharmony_ci bool done, sink_mode; 56153a5a1b3Sopenharmony_ci 56253a5a1b3Sopenharmony_ci pa_assert(u); 56353a5a1b3Sopenharmony_ci pa_assert(prefix); 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_ci sink_mode = pa_streq(prefix, "sink:"); 56653a5a1b3Sopenharmony_ci 56753a5a1b3Sopenharmony_ci if (sink_mode) 56853a5a1b3Sopenharmony_ci indexes = &u->preferred_sinks; 56953a5a1b3Sopenharmony_ci else 57053a5a1b3Sopenharmony_ci indexes = &u->preferred_sources; 57153a5a1b3Sopenharmony_ci 57253a5a1b3Sopenharmony_ci for (uint32_t i = 0; i < NUM_ROLES; ++i) { 57353a5a1b3Sopenharmony_ci (*indexes)[i] = PA_INVALID_INDEX; 57453a5a1b3Sopenharmony_ci } 57553a5a1b3Sopenharmony_ci pa_zero(highest_priority_available); 57653a5a1b3Sopenharmony_ci 57753a5a1b3Sopenharmony_ci done = !pa_database_first(u->database, &key, NULL); 57853a5a1b3Sopenharmony_ci 57953a5a1b3Sopenharmony_ci /* Find all existing devices with the same prefix so we find the highest priority device for each role */ 58053a5a1b3Sopenharmony_ci while (!done) { 58153a5a1b3Sopenharmony_ci pa_datum next_key; 58253a5a1b3Sopenharmony_ci 58353a5a1b3Sopenharmony_ci done = !pa_database_next(u->database, &key, &next_key, NULL); 58453a5a1b3Sopenharmony_ci 58553a5a1b3Sopenharmony_ci if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) { 58653a5a1b3Sopenharmony_ci char *name, *device_name; 58753a5a1b3Sopenharmony_ci struct entry *e; 58853a5a1b3Sopenharmony_ci 58953a5a1b3Sopenharmony_ci name = pa_xstrndup(key.data, key.size); 59053a5a1b3Sopenharmony_ci pa_assert_se(device_name = get_name(name, prefix)); 59153a5a1b3Sopenharmony_ci 59253a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 59353a5a1b3Sopenharmony_ci for (uint32_t i = 0; i < NUM_ROLES; ++i) { 59453a5a1b3Sopenharmony_ci if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) { 59553a5a1b3Sopenharmony_ci /* We've found a device with a higher priority than that we've currently got, 59653a5a1b3Sopenharmony_ci so see if it is currently available or not and update our list */ 59753a5a1b3Sopenharmony_ci uint32_t idx; 59853a5a1b3Sopenharmony_ci bool found = false; 59953a5a1b3Sopenharmony_ci 60053a5a1b3Sopenharmony_ci if (sink_mode) { 60153a5a1b3Sopenharmony_ci pa_sink *sink; 60253a5a1b3Sopenharmony_ci 60353a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, u->core->sinks, idx) { 60453a5a1b3Sopenharmony_ci if ((pa_sink*) ignore_device == sink) 60553a5a1b3Sopenharmony_ci continue; 60653a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(sink->state)) 60753a5a1b3Sopenharmony_ci continue; 60853a5a1b3Sopenharmony_ci if (pa_streq(sink->name, device_name)) { 60953a5a1b3Sopenharmony_ci found = true; 61053a5a1b3Sopenharmony_ci idx = sink->index; /* Is this needed? */ 61153a5a1b3Sopenharmony_ci break; 61253a5a1b3Sopenharmony_ci } 61353a5a1b3Sopenharmony_ci } 61453a5a1b3Sopenharmony_ci } else { 61553a5a1b3Sopenharmony_ci pa_source *source; 61653a5a1b3Sopenharmony_ci 61753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, u->core->sources, idx) { 61853a5a1b3Sopenharmony_ci if ((pa_source*) ignore_device == source) 61953a5a1b3Sopenharmony_ci continue; 62053a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(source->state)) 62153a5a1b3Sopenharmony_ci continue; 62253a5a1b3Sopenharmony_ci if (pa_streq(source->name, device_name)) { 62353a5a1b3Sopenharmony_ci found = true; 62453a5a1b3Sopenharmony_ci idx = source->index; /* Is this needed? */ 62553a5a1b3Sopenharmony_ci break; 62653a5a1b3Sopenharmony_ci } 62753a5a1b3Sopenharmony_ci } 62853a5a1b3Sopenharmony_ci } 62953a5a1b3Sopenharmony_ci if (found) { 63053a5a1b3Sopenharmony_ci highest_priority_available[i] = e->priority[i]; 63153a5a1b3Sopenharmony_ci (*indexes)[i] = idx; 63253a5a1b3Sopenharmony_ci } 63353a5a1b3Sopenharmony_ci 63453a5a1b3Sopenharmony_ci } 63553a5a1b3Sopenharmony_ci } 63653a5a1b3Sopenharmony_ci 63753a5a1b3Sopenharmony_ci entry_free(e); 63853a5a1b3Sopenharmony_ci } 63953a5a1b3Sopenharmony_ci 64053a5a1b3Sopenharmony_ci pa_xfree(name); 64153a5a1b3Sopenharmony_ci pa_xfree(device_name); 64253a5a1b3Sopenharmony_ci } 64353a5a1b3Sopenharmony_ci 64453a5a1b3Sopenharmony_ci pa_datum_free(&key); 64553a5a1b3Sopenharmony_ci key = next_key; 64653a5a1b3Sopenharmony_ci } 64753a5a1b3Sopenharmony_ci} 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_cistatic void route_sink_input(struct userdata *u, pa_sink_input *si) { 65053a5a1b3Sopenharmony_ci const char *auto_filtered_prop; 65153a5a1b3Sopenharmony_ci const char *role; 65253a5a1b3Sopenharmony_ci uint32_t role_index, device_index; 65353a5a1b3Sopenharmony_ci bool auto_filtered = false; 65453a5a1b3Sopenharmony_ci pa_sink *sink; 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci pa_assert(u); 65753a5a1b3Sopenharmony_ci pa_assert(u->do_routing); 65853a5a1b3Sopenharmony_ci 65953a5a1b3Sopenharmony_ci /* Skip this if it is already in the process of being moved anyway */ 66053a5a1b3Sopenharmony_ci if (!si->sink) 66153a5a1b3Sopenharmony_ci return; 66253a5a1b3Sopenharmony_ci 66353a5a1b3Sopenharmony_ci /* Don't override user or application routing requests. */ 66453a5a1b3Sopenharmony_ci if (pa_safe_streq(si->sink->name, si->preferred_sink) || si->sink_requested_by_application) 66553a5a1b3Sopenharmony_ci return; 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci auto_filtered_prop = pa_proplist_gets(si->proplist, "module-device-manager.auto_filtered"); 66853a5a1b3Sopenharmony_ci if (auto_filtered_prop) 66953a5a1b3Sopenharmony_ci auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1); 67053a5a1b3Sopenharmony_ci 67153a5a1b3Sopenharmony_ci /* It might happen that a stream and a sink are set up at the 67253a5a1b3Sopenharmony_ci same time, in which case we want to make sure we don't 67353a5a1b3Sopenharmony_ci interfere with that */ 67453a5a1b3Sopenharmony_ci if (!PA_SINK_INPUT_IS_LINKED(si->state)) 67553a5a1b3Sopenharmony_ci return; 67653a5a1b3Sopenharmony_ci 67753a5a1b3Sopenharmony_ci if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE))) 67853a5a1b3Sopenharmony_ci role_index = get_role_index("none"); 67953a5a1b3Sopenharmony_ci else 68053a5a1b3Sopenharmony_ci role_index = get_role_index(role); 68153a5a1b3Sopenharmony_ci 68253a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX == role_index) 68353a5a1b3Sopenharmony_ci return; 68453a5a1b3Sopenharmony_ci 68553a5a1b3Sopenharmony_ci device_index = u->preferred_sinks[role_index]; 68653a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX == device_index) 68753a5a1b3Sopenharmony_ci return; 68853a5a1b3Sopenharmony_ci 68953a5a1b3Sopenharmony_ci if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index))) 69053a5a1b3Sopenharmony_ci return; 69153a5a1b3Sopenharmony_ci 69253a5a1b3Sopenharmony_ci if (auto_filtered) { 69353a5a1b3Sopenharmony_ci /* For streams for which a filter has been loaded by another module, we 69453a5a1b3Sopenharmony_ci * do not try to execute moves within the same filter hierarchy */ 69553a5a1b3Sopenharmony_ci if (pa_sink_get_master(si->sink) == pa_sink_get_master(sink)) 69653a5a1b3Sopenharmony_ci return; 69753a5a1b3Sopenharmony_ci } 69853a5a1b3Sopenharmony_ci 69953a5a1b3Sopenharmony_ci if (si->sink != sink) 70053a5a1b3Sopenharmony_ci pa_sink_input_move_to(si, sink, false); 70153a5a1b3Sopenharmony_ci} 70253a5a1b3Sopenharmony_ci 70353a5a1b3Sopenharmony_cistatic pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) { 70453a5a1b3Sopenharmony_ci pa_sink_input *si; 70553a5a1b3Sopenharmony_ci uint32_t idx; 70653a5a1b3Sopenharmony_ci 70753a5a1b3Sopenharmony_ci pa_assert(u); 70853a5a1b3Sopenharmony_ci 70953a5a1b3Sopenharmony_ci if (!u->do_routing) 71053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 71153a5a1b3Sopenharmony_ci 71253a5a1b3Sopenharmony_ci update_highest_priority_device_indexes(u, "sink:", ignore_sink); 71353a5a1b3Sopenharmony_ci 71453a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { 71553a5a1b3Sopenharmony_ci route_sink_input(u, si); 71653a5a1b3Sopenharmony_ci } 71753a5a1b3Sopenharmony_ci 71853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 71953a5a1b3Sopenharmony_ci} 72053a5a1b3Sopenharmony_ci 72153a5a1b3Sopenharmony_cistatic void route_source_output(struct userdata *u, pa_source_output *so) { 72253a5a1b3Sopenharmony_ci const char *auto_filtered_prop; 72353a5a1b3Sopenharmony_ci const char *role; 72453a5a1b3Sopenharmony_ci uint32_t role_index, device_index; 72553a5a1b3Sopenharmony_ci bool auto_filtered = false; 72653a5a1b3Sopenharmony_ci pa_source *source; 72753a5a1b3Sopenharmony_ci 72853a5a1b3Sopenharmony_ci pa_assert(u); 72953a5a1b3Sopenharmony_ci pa_assert(u->do_routing); 73053a5a1b3Sopenharmony_ci 73153a5a1b3Sopenharmony_ci if (so->direct_on_input) 73253a5a1b3Sopenharmony_ci return; 73353a5a1b3Sopenharmony_ci 73453a5a1b3Sopenharmony_ci /* Skip this if it is already in the process of being moved anyway */ 73553a5a1b3Sopenharmony_ci if (!so->source) 73653a5a1b3Sopenharmony_ci return; 73753a5a1b3Sopenharmony_ci 73853a5a1b3Sopenharmony_ci /* Don't override user or application routing requests. */ 73953a5a1b3Sopenharmony_ci if (pa_safe_streq(so->source->name, so->preferred_source) || so->source_requested_by_application) 74053a5a1b3Sopenharmony_ci return; 74153a5a1b3Sopenharmony_ci 74253a5a1b3Sopenharmony_ci auto_filtered_prop = pa_proplist_gets(so->proplist, "module-device-manager.auto_filtered"); 74353a5a1b3Sopenharmony_ci if (auto_filtered_prop) 74453a5a1b3Sopenharmony_ci auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1); 74553a5a1b3Sopenharmony_ci 74653a5a1b3Sopenharmony_ci /* It might happen that a stream and a source are set up at the 74753a5a1b3Sopenharmony_ci same time, in which case we want to make sure we don't 74853a5a1b3Sopenharmony_ci interfere with that */ 74953a5a1b3Sopenharmony_ci if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state)) 75053a5a1b3Sopenharmony_ci return; 75153a5a1b3Sopenharmony_ci 75253a5a1b3Sopenharmony_ci if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE))) 75353a5a1b3Sopenharmony_ci role_index = get_role_index("none"); 75453a5a1b3Sopenharmony_ci else 75553a5a1b3Sopenharmony_ci role_index = get_role_index(role); 75653a5a1b3Sopenharmony_ci 75753a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX == role_index) 75853a5a1b3Sopenharmony_ci return; 75953a5a1b3Sopenharmony_ci 76053a5a1b3Sopenharmony_ci device_index = u->preferred_sources[role_index]; 76153a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX == device_index) 76253a5a1b3Sopenharmony_ci return; 76353a5a1b3Sopenharmony_ci 76453a5a1b3Sopenharmony_ci if (!(source = pa_idxset_get_by_index(u->core->sources, device_index))) 76553a5a1b3Sopenharmony_ci return; 76653a5a1b3Sopenharmony_ci 76753a5a1b3Sopenharmony_ci if (auto_filtered) { 76853a5a1b3Sopenharmony_ci /* For streams for which a filter has been loaded by another module, we 76953a5a1b3Sopenharmony_ci * do not try to execute moves within the same filter hierarchy */ 77053a5a1b3Sopenharmony_ci if (pa_source_get_master(so->source) == pa_source_get_master(source)) 77153a5a1b3Sopenharmony_ci return; 77253a5a1b3Sopenharmony_ci } 77353a5a1b3Sopenharmony_ci 77453a5a1b3Sopenharmony_ci if (so->source != source) 77553a5a1b3Sopenharmony_ci pa_source_output_move_to(so, source, false); 77653a5a1b3Sopenharmony_ci} 77753a5a1b3Sopenharmony_ci 77853a5a1b3Sopenharmony_cistatic pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) { 77953a5a1b3Sopenharmony_ci pa_source_output *so; 78053a5a1b3Sopenharmony_ci uint32_t idx; 78153a5a1b3Sopenharmony_ci 78253a5a1b3Sopenharmony_ci pa_assert(u); 78353a5a1b3Sopenharmony_ci 78453a5a1b3Sopenharmony_ci if (!u->do_routing) 78553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 78653a5a1b3Sopenharmony_ci 78753a5a1b3Sopenharmony_ci update_highest_priority_device_indexes(u, "source:", ignore_source); 78853a5a1b3Sopenharmony_ci 78953a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) { 79053a5a1b3Sopenharmony_ci route_source_output(u, so); 79153a5a1b3Sopenharmony_ci } 79253a5a1b3Sopenharmony_ci 79353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 79453a5a1b3Sopenharmony_ci} 79553a5a1b3Sopenharmony_ci 79653a5a1b3Sopenharmony_cistatic void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { 79753a5a1b3Sopenharmony_ci struct userdata *u = userdata; 79853a5a1b3Sopenharmony_ci struct entry *entry, *old = NULL; 79953a5a1b3Sopenharmony_ci char *name = NULL; 80053a5a1b3Sopenharmony_ci 80153a5a1b3Sopenharmony_ci pa_assert(c); 80253a5a1b3Sopenharmony_ci pa_assert(u); 80353a5a1b3Sopenharmony_ci 80453a5a1b3Sopenharmony_ci if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && 80553a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && 80653a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && 80753a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) && 80853a5a1b3Sopenharmony_ci 80953a5a1b3Sopenharmony_ci /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/ 81053a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) && 81153a5a1b3Sopenharmony_ci /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/ 81253a5a1b3Sopenharmony_ci t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE)) 81353a5a1b3Sopenharmony_ci return; 81453a5a1b3Sopenharmony_ci 81553a5a1b3Sopenharmony_ci if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { 81653a5a1b3Sopenharmony_ci pa_sink_input *si; 81753a5a1b3Sopenharmony_ci 81853a5a1b3Sopenharmony_ci if (!u->do_routing) 81953a5a1b3Sopenharmony_ci return; 82053a5a1b3Sopenharmony_ci if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) 82153a5a1b3Sopenharmony_ci return; 82253a5a1b3Sopenharmony_ci 82353a5a1b3Sopenharmony_ci /* The role may change mid-stream, so we reroute */ 82453a5a1b3Sopenharmony_ci route_sink_input(u, si); 82553a5a1b3Sopenharmony_ci 82653a5a1b3Sopenharmony_ci return; 82753a5a1b3Sopenharmony_ci } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) { 82853a5a1b3Sopenharmony_ci pa_source_output *so; 82953a5a1b3Sopenharmony_ci 83053a5a1b3Sopenharmony_ci if (!u->do_routing) 83153a5a1b3Sopenharmony_ci return; 83253a5a1b3Sopenharmony_ci if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) 83353a5a1b3Sopenharmony_ci return; 83453a5a1b3Sopenharmony_ci 83553a5a1b3Sopenharmony_ci /* The role may change mid-stream, so we reroute */ 83653a5a1b3Sopenharmony_ci route_source_output(u, so); 83753a5a1b3Sopenharmony_ci 83853a5a1b3Sopenharmony_ci return; 83953a5a1b3Sopenharmony_ci } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { 84053a5a1b3Sopenharmony_ci pa_sink *sink; 84153a5a1b3Sopenharmony_ci 84253a5a1b3Sopenharmony_ci if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) 84353a5a1b3Sopenharmony_ci return; 84453a5a1b3Sopenharmony_ci 84553a5a1b3Sopenharmony_ci entry = entry_new(); 84653a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", sink->name); 84753a5a1b3Sopenharmony_ci 84853a5a1b3Sopenharmony_ci old = load_or_initialize_entry(u, entry, name, "sink:"); 84953a5a1b3Sopenharmony_ci 85053a5a1b3Sopenharmony_ci if (!entry->user_set_description) { 85153a5a1b3Sopenharmony_ci pa_xfree(entry->description); 85253a5a1b3Sopenharmony_ci entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); 85353a5a1b3Sopenharmony_ci } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) { 85453a5a1b3Sopenharmony_ci /* Warning: If two modules fight over the description, this could cause an infinite loop. 85553a5a1b3Sopenharmony_ci by changing the description here, we retrigger this subscription callback. The only thing stopping us from 85653a5a1b3Sopenharmony_ci looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage 85753a5a1b3Sopenharmony_ci the description, this will fail... */ 85853a5a1b3Sopenharmony_ci pa_sink_set_description(sink, entry->description); 85953a5a1b3Sopenharmony_ci } 86053a5a1b3Sopenharmony_ci 86153a5a1b3Sopenharmony_ci pa_xfree(entry->icon); 86253a5a1b3Sopenharmony_ci entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)); 86353a5a1b3Sopenharmony_ci 86453a5a1b3Sopenharmony_ci } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { 86553a5a1b3Sopenharmony_ci pa_source *source; 86653a5a1b3Sopenharmony_ci 86753a5a1b3Sopenharmony_ci pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); 86853a5a1b3Sopenharmony_ci 86953a5a1b3Sopenharmony_ci if (!(source = pa_idxset_get_by_index(c->sources, idx))) 87053a5a1b3Sopenharmony_ci return; 87153a5a1b3Sopenharmony_ci 87253a5a1b3Sopenharmony_ci if (source->monitor_of) 87353a5a1b3Sopenharmony_ci return; 87453a5a1b3Sopenharmony_ci 87553a5a1b3Sopenharmony_ci entry = entry_new(); 87653a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", source->name); 87753a5a1b3Sopenharmony_ci 87853a5a1b3Sopenharmony_ci old = load_or_initialize_entry(u, entry, name, "source:"); 87953a5a1b3Sopenharmony_ci 88053a5a1b3Sopenharmony_ci if (!entry->user_set_description) { 88153a5a1b3Sopenharmony_ci pa_xfree(entry->description); 88253a5a1b3Sopenharmony_ci entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)); 88353a5a1b3Sopenharmony_ci } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) { 88453a5a1b3Sopenharmony_ci /* Warning: If two modules fight over the description, this could cause an infinite loop. 88553a5a1b3Sopenharmony_ci by changing the description here, we retrigger this subscription callback. The only thing stopping us from 88653a5a1b3Sopenharmony_ci looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage 88753a5a1b3Sopenharmony_ci the description, this will fail... */ 88853a5a1b3Sopenharmony_ci pa_source_set_description(source, entry->description); 88953a5a1b3Sopenharmony_ci } 89053a5a1b3Sopenharmony_ci 89153a5a1b3Sopenharmony_ci pa_xfree(entry->icon); 89253a5a1b3Sopenharmony_ci entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)); 89353a5a1b3Sopenharmony_ci } else { 89453a5a1b3Sopenharmony_ci pa_assert_not_reached(); 89553a5a1b3Sopenharmony_ci } 89653a5a1b3Sopenharmony_ci 89753a5a1b3Sopenharmony_ci pa_assert(name); 89853a5a1b3Sopenharmony_ci 89953a5a1b3Sopenharmony_ci if (old) { 90053a5a1b3Sopenharmony_ci 90153a5a1b3Sopenharmony_ci if (entries_equal(old, entry)) { 90253a5a1b3Sopenharmony_ci entry_free(old); 90353a5a1b3Sopenharmony_ci entry_free(entry); 90453a5a1b3Sopenharmony_ci pa_xfree(name); 90553a5a1b3Sopenharmony_ci 90653a5a1b3Sopenharmony_ci return; 90753a5a1b3Sopenharmony_ci } 90853a5a1b3Sopenharmony_ci 90953a5a1b3Sopenharmony_ci entry_free(old); 91053a5a1b3Sopenharmony_ci } 91153a5a1b3Sopenharmony_ci 91253a5a1b3Sopenharmony_ci pa_log_info("Storing device %s.", name); 91353a5a1b3Sopenharmony_ci 91453a5a1b3Sopenharmony_ci if (entry_write(u, name, entry)) 91553a5a1b3Sopenharmony_ci trigger_save(u); 91653a5a1b3Sopenharmony_ci else 91753a5a1b3Sopenharmony_ci pa_log_warn("Could not save device");; 91853a5a1b3Sopenharmony_ci 91953a5a1b3Sopenharmony_ci entry_free(entry); 92053a5a1b3Sopenharmony_ci pa_xfree(name); 92153a5a1b3Sopenharmony_ci} 92253a5a1b3Sopenharmony_ci 92353a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { 92453a5a1b3Sopenharmony_ci char *name; 92553a5a1b3Sopenharmony_ci struct entry *e; 92653a5a1b3Sopenharmony_ci 92753a5a1b3Sopenharmony_ci pa_assert(c); 92853a5a1b3Sopenharmony_ci pa_assert(new_data); 92953a5a1b3Sopenharmony_ci pa_assert(u); 93053a5a1b3Sopenharmony_ci 93153a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("sink:%s", new_data->name); 93253a5a1b3Sopenharmony_ci 93353a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 93453a5a1b3Sopenharmony_ci if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) { 93553a5a1b3Sopenharmony_ci pa_log_info("Restoring description for sink %s.", new_data->name); 93653a5a1b3Sopenharmony_ci pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); 93753a5a1b3Sopenharmony_ci } 93853a5a1b3Sopenharmony_ci 93953a5a1b3Sopenharmony_ci entry_free(e); 94053a5a1b3Sopenharmony_ci } 94153a5a1b3Sopenharmony_ci 94253a5a1b3Sopenharmony_ci pa_xfree(name); 94353a5a1b3Sopenharmony_ci 94453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 94553a5a1b3Sopenharmony_ci} 94653a5a1b3Sopenharmony_ci 94753a5a1b3Sopenharmony_cistatic pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { 94853a5a1b3Sopenharmony_ci char *name; 94953a5a1b3Sopenharmony_ci struct entry *e; 95053a5a1b3Sopenharmony_ci 95153a5a1b3Sopenharmony_ci pa_assert(c); 95253a5a1b3Sopenharmony_ci pa_assert(new_data); 95353a5a1b3Sopenharmony_ci pa_assert(u); 95453a5a1b3Sopenharmony_ci 95553a5a1b3Sopenharmony_ci name = pa_sprintf_malloc("source:%s", new_data->name); 95653a5a1b3Sopenharmony_ci 95753a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 95853a5a1b3Sopenharmony_ci if (e->user_set_description && !pa_safe_streq(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION))) { 95953a5a1b3Sopenharmony_ci /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */ 96053a5a1b3Sopenharmony_ci pa_log_info("Restoring description for source %s.", new_data->name); 96153a5a1b3Sopenharmony_ci pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description); 96253a5a1b3Sopenharmony_ci } 96353a5a1b3Sopenharmony_ci 96453a5a1b3Sopenharmony_ci entry_free(e); 96553a5a1b3Sopenharmony_ci } 96653a5a1b3Sopenharmony_ci 96753a5a1b3Sopenharmony_ci pa_xfree(name); 96853a5a1b3Sopenharmony_ci 96953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 97053a5a1b3Sopenharmony_ci} 97153a5a1b3Sopenharmony_ci 97253a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { 97353a5a1b3Sopenharmony_ci const char *role; 97453a5a1b3Sopenharmony_ci uint32_t role_index; 97553a5a1b3Sopenharmony_ci 97653a5a1b3Sopenharmony_ci pa_assert(c); 97753a5a1b3Sopenharmony_ci pa_assert(new_data); 97853a5a1b3Sopenharmony_ci pa_assert(u); 97953a5a1b3Sopenharmony_ci 98053a5a1b3Sopenharmony_ci if (!u->do_routing) 98153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 98253a5a1b3Sopenharmony_ci 98353a5a1b3Sopenharmony_ci if (new_data->sink) 98453a5a1b3Sopenharmony_ci pa_log_debug("Not restoring device for stream because already set."); 98553a5a1b3Sopenharmony_ci else { 98653a5a1b3Sopenharmony_ci if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) 98753a5a1b3Sopenharmony_ci role_index = get_role_index("none"); 98853a5a1b3Sopenharmony_ci else 98953a5a1b3Sopenharmony_ci role_index = get_role_index(role); 99053a5a1b3Sopenharmony_ci 99153a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != role_index) { 99253a5a1b3Sopenharmony_ci uint32_t device_index; 99353a5a1b3Sopenharmony_ci 99453a5a1b3Sopenharmony_ci device_index = u->preferred_sinks[role_index]; 99553a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != device_index) { 99653a5a1b3Sopenharmony_ci pa_sink *sink; 99753a5a1b3Sopenharmony_ci 99853a5a1b3Sopenharmony_ci if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) { 99953a5a1b3Sopenharmony_ci if (!pa_sink_input_new_data_set_sink(new_data, sink, false, false)) 100053a5a1b3Sopenharmony_ci pa_log_debug("Not restoring device for stream because no supported format was found"); 100153a5a1b3Sopenharmony_ci } 100253a5a1b3Sopenharmony_ci } 100353a5a1b3Sopenharmony_ci } 100453a5a1b3Sopenharmony_ci } 100553a5a1b3Sopenharmony_ci 100653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 100753a5a1b3Sopenharmony_ci} 100853a5a1b3Sopenharmony_ci 100953a5a1b3Sopenharmony_cistatic pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { 101053a5a1b3Sopenharmony_ci const char *role; 101153a5a1b3Sopenharmony_ci uint32_t role_index; 101253a5a1b3Sopenharmony_ci 101353a5a1b3Sopenharmony_ci pa_assert(c); 101453a5a1b3Sopenharmony_ci pa_assert(new_data); 101553a5a1b3Sopenharmony_ci pa_assert(u); 101653a5a1b3Sopenharmony_ci 101753a5a1b3Sopenharmony_ci if (!u->do_routing) 101853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 101953a5a1b3Sopenharmony_ci 102053a5a1b3Sopenharmony_ci if (new_data->direct_on_input) 102153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 102253a5a1b3Sopenharmony_ci 102353a5a1b3Sopenharmony_ci if (new_data->source) 102453a5a1b3Sopenharmony_ci pa_log_debug("Not restoring device for stream because already set."); 102553a5a1b3Sopenharmony_ci else { 102653a5a1b3Sopenharmony_ci if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE))) 102753a5a1b3Sopenharmony_ci role_index = get_role_index("none"); 102853a5a1b3Sopenharmony_ci else 102953a5a1b3Sopenharmony_ci role_index = get_role_index(role); 103053a5a1b3Sopenharmony_ci 103153a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != role_index) { 103253a5a1b3Sopenharmony_ci uint32_t device_index; 103353a5a1b3Sopenharmony_ci 103453a5a1b3Sopenharmony_ci device_index = u->preferred_sources[role_index]; 103553a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX != device_index) { 103653a5a1b3Sopenharmony_ci pa_source *source; 103753a5a1b3Sopenharmony_ci 103853a5a1b3Sopenharmony_ci if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) 103953a5a1b3Sopenharmony_ci if (!pa_source_output_new_data_set_source(new_data, source, false, false)) 104053a5a1b3Sopenharmony_ci pa_log_debug("Not restoring device for stream because no supported format was found"); 104153a5a1b3Sopenharmony_ci } 104253a5a1b3Sopenharmony_ci } 104353a5a1b3Sopenharmony_ci } 104453a5a1b3Sopenharmony_ci 104553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 104653a5a1b3Sopenharmony_ci} 104753a5a1b3Sopenharmony_ci 104853a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) { 104953a5a1b3Sopenharmony_ci pa_assert(c); 105053a5a1b3Sopenharmony_ci pa_assert(u); 105153a5a1b3Sopenharmony_ci pa_assert(u->core == c); 105253a5a1b3Sopenharmony_ci pa_assert(u->on_hotplug); 105353a5a1b3Sopenharmony_ci 105453a5a1b3Sopenharmony_ci notify_subscribers(u); 105553a5a1b3Sopenharmony_ci 105653a5a1b3Sopenharmony_ci return route_sink_inputs(u, NULL); 105753a5a1b3Sopenharmony_ci} 105853a5a1b3Sopenharmony_ci 105953a5a1b3Sopenharmony_cistatic pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) { 106053a5a1b3Sopenharmony_ci pa_assert(c); 106153a5a1b3Sopenharmony_ci pa_assert(u); 106253a5a1b3Sopenharmony_ci pa_assert(u->core == c); 106353a5a1b3Sopenharmony_ci pa_assert(u->on_hotplug); 106453a5a1b3Sopenharmony_ci 106553a5a1b3Sopenharmony_ci notify_subscribers(u); 106653a5a1b3Sopenharmony_ci 106753a5a1b3Sopenharmony_ci return route_source_outputs(u, NULL); 106853a5a1b3Sopenharmony_ci} 106953a5a1b3Sopenharmony_ci 107053a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { 107153a5a1b3Sopenharmony_ci pa_assert(c); 107253a5a1b3Sopenharmony_ci pa_assert(sink); 107353a5a1b3Sopenharmony_ci pa_assert(u); 107453a5a1b3Sopenharmony_ci pa_assert(u->core == c); 107553a5a1b3Sopenharmony_ci pa_assert(u->on_rescue); 107653a5a1b3Sopenharmony_ci 107753a5a1b3Sopenharmony_ci /* There's no point in doing anything if the core is shut down anyway */ 107853a5a1b3Sopenharmony_ci if (c->state == PA_CORE_SHUTDOWN) 107953a5a1b3Sopenharmony_ci return PA_HOOK_OK; 108053a5a1b3Sopenharmony_ci 108153a5a1b3Sopenharmony_ci notify_subscribers(u); 108253a5a1b3Sopenharmony_ci 108353a5a1b3Sopenharmony_ci return route_sink_inputs(u, sink); 108453a5a1b3Sopenharmony_ci} 108553a5a1b3Sopenharmony_ci 108653a5a1b3Sopenharmony_cistatic pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { 108753a5a1b3Sopenharmony_ci pa_assert(c); 108853a5a1b3Sopenharmony_ci pa_assert(source); 108953a5a1b3Sopenharmony_ci pa_assert(u); 109053a5a1b3Sopenharmony_ci pa_assert(u->core == c); 109153a5a1b3Sopenharmony_ci pa_assert(u->on_rescue); 109253a5a1b3Sopenharmony_ci 109353a5a1b3Sopenharmony_ci /* There's no point in doing anything if the core is shut down anyway */ 109453a5a1b3Sopenharmony_ci if (c->state == PA_CORE_SHUTDOWN) 109553a5a1b3Sopenharmony_ci return PA_HOOK_OK; 109653a5a1b3Sopenharmony_ci 109753a5a1b3Sopenharmony_ci notify_subscribers(u); 109853a5a1b3Sopenharmony_ci 109953a5a1b3Sopenharmony_ci return route_source_outputs(u, source); 110053a5a1b3Sopenharmony_ci} 110153a5a1b3Sopenharmony_ci 110253a5a1b3Sopenharmony_cistatic void apply_entry(struct userdata *u, const char *name, struct entry *e) { 110353a5a1b3Sopenharmony_ci uint32_t idx; 110453a5a1b3Sopenharmony_ci char *n; 110553a5a1b3Sopenharmony_ci 110653a5a1b3Sopenharmony_ci pa_assert(u); 110753a5a1b3Sopenharmony_ci pa_assert(name); 110853a5a1b3Sopenharmony_ci pa_assert(e); 110953a5a1b3Sopenharmony_ci 111053a5a1b3Sopenharmony_ci if (!e->user_set_description) 111153a5a1b3Sopenharmony_ci return; 111253a5a1b3Sopenharmony_ci 111353a5a1b3Sopenharmony_ci if ((n = get_name(name, "sink:"))) { 111453a5a1b3Sopenharmony_ci pa_sink *s; 111553a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(s, u->core->sinks, idx) { 111653a5a1b3Sopenharmony_ci if (!pa_streq(s->name, n)) { 111753a5a1b3Sopenharmony_ci continue; 111853a5a1b3Sopenharmony_ci } 111953a5a1b3Sopenharmony_ci 112053a5a1b3Sopenharmony_ci pa_log_info("Setting description for sink %s to '%s'", s->name, e->description); 112153a5a1b3Sopenharmony_ci pa_sink_set_description(s, e->description); 112253a5a1b3Sopenharmony_ci } 112353a5a1b3Sopenharmony_ci pa_xfree(n); 112453a5a1b3Sopenharmony_ci } 112553a5a1b3Sopenharmony_ci else if ((n = get_name(name, "source:"))) { 112653a5a1b3Sopenharmony_ci pa_source *s; 112753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(s, u->core->sources, idx) { 112853a5a1b3Sopenharmony_ci if (!pa_streq(s->name, n)) { 112953a5a1b3Sopenharmony_ci continue; 113053a5a1b3Sopenharmony_ci } 113153a5a1b3Sopenharmony_ci 113253a5a1b3Sopenharmony_ci if (s->monitor_of) { 113353a5a1b3Sopenharmony_ci pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name); 113453a5a1b3Sopenharmony_ci continue; 113553a5a1b3Sopenharmony_ci } 113653a5a1b3Sopenharmony_ci 113753a5a1b3Sopenharmony_ci pa_log_info("Setting description for source %s to '%s'", s->name, e->description); 113853a5a1b3Sopenharmony_ci pa_source_set_description(s, e->description); 113953a5a1b3Sopenharmony_ci } 114053a5a1b3Sopenharmony_ci pa_xfree(n); 114153a5a1b3Sopenharmony_ci } 114253a5a1b3Sopenharmony_ci} 114353a5a1b3Sopenharmony_ci 114453a5a1b3Sopenharmony_ci#define EXT_VERSION 1 114553a5a1b3Sopenharmony_ci 114653a5a1b3Sopenharmony_cistatic int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { 114753a5a1b3Sopenharmony_ci struct userdata *u; 114853a5a1b3Sopenharmony_ci uint32_t command; 114953a5a1b3Sopenharmony_ci pa_tagstruct *reply = NULL; 115053a5a1b3Sopenharmony_ci 115153a5a1b3Sopenharmony_ci pa_assert(p); 115253a5a1b3Sopenharmony_ci pa_assert(m); 115353a5a1b3Sopenharmony_ci pa_assert(c); 115453a5a1b3Sopenharmony_ci pa_assert(t); 115553a5a1b3Sopenharmony_ci 115653a5a1b3Sopenharmony_ci u = m->userdata; 115753a5a1b3Sopenharmony_ci 115853a5a1b3Sopenharmony_ci if (pa_tagstruct_getu32(t, &command) < 0) 115953a5a1b3Sopenharmony_ci goto fail; 116053a5a1b3Sopenharmony_ci 116153a5a1b3Sopenharmony_ci reply = pa_tagstruct_new(); 116253a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, PA_COMMAND_REPLY); 116353a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, tag); 116453a5a1b3Sopenharmony_ci 116553a5a1b3Sopenharmony_ci switch (command) { 116653a5a1b3Sopenharmony_ci case SUBCOMMAND_TEST: { 116753a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 116853a5a1b3Sopenharmony_ci goto fail; 116953a5a1b3Sopenharmony_ci 117053a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, EXT_VERSION); 117153a5a1b3Sopenharmony_ci break; 117253a5a1b3Sopenharmony_ci } 117353a5a1b3Sopenharmony_ci 117453a5a1b3Sopenharmony_ci case SUBCOMMAND_READ: { 117553a5a1b3Sopenharmony_ci pa_datum key; 117653a5a1b3Sopenharmony_ci bool done; 117753a5a1b3Sopenharmony_ci 117853a5a1b3Sopenharmony_ci if (!pa_tagstruct_eof(t)) 117953a5a1b3Sopenharmony_ci goto fail; 118053a5a1b3Sopenharmony_ci 118153a5a1b3Sopenharmony_ci done = !pa_database_first(u->database, &key, NULL); 118253a5a1b3Sopenharmony_ci 118353a5a1b3Sopenharmony_ci while (!done) { 118453a5a1b3Sopenharmony_ci pa_datum next_key; 118553a5a1b3Sopenharmony_ci struct entry *e; 118653a5a1b3Sopenharmony_ci char *name; 118753a5a1b3Sopenharmony_ci 118853a5a1b3Sopenharmony_ci done = !pa_database_next(u->database, &key, &next_key, NULL); 118953a5a1b3Sopenharmony_ci 119053a5a1b3Sopenharmony_ci name = pa_xstrndup(key.data, key.size); 119153a5a1b3Sopenharmony_ci pa_datum_free(&key); 119253a5a1b3Sopenharmony_ci 119353a5a1b3Sopenharmony_ci if ((e = entry_read(u, name))) { 119453a5a1b3Sopenharmony_ci uint32_t idx; 119553a5a1b3Sopenharmony_ci char *device_name; 119653a5a1b3Sopenharmony_ci uint32_t found_index = PA_INVALID_INDEX; 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci if ((device_name = get_name(name, "sink:"))) { 119953a5a1b3Sopenharmony_ci pa_sink* s; 120053a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(s, u->core->sinks, idx) { 120153a5a1b3Sopenharmony_ci if (pa_streq(s->name, device_name)) { 120253a5a1b3Sopenharmony_ci found_index = s->index; 120353a5a1b3Sopenharmony_ci break; 120453a5a1b3Sopenharmony_ci } 120553a5a1b3Sopenharmony_ci } 120653a5a1b3Sopenharmony_ci pa_xfree(device_name); 120753a5a1b3Sopenharmony_ci } else if ((device_name = get_name(name, "source:"))) { 120853a5a1b3Sopenharmony_ci pa_source* s; 120953a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(s, u->core->sources, idx) { 121053a5a1b3Sopenharmony_ci if (pa_streq(s->name, device_name)) { 121153a5a1b3Sopenharmony_ci found_index = s->index; 121253a5a1b3Sopenharmony_ci break; 121353a5a1b3Sopenharmony_ci } 121453a5a1b3Sopenharmony_ci } 121553a5a1b3Sopenharmony_ci pa_xfree(device_name); 121653a5a1b3Sopenharmony_ci } 121753a5a1b3Sopenharmony_ci 121853a5a1b3Sopenharmony_ci pa_tagstruct_puts(reply, name); 121953a5a1b3Sopenharmony_ci pa_tagstruct_puts(reply, e->description); 122053a5a1b3Sopenharmony_ci pa_tagstruct_puts(reply, e->icon); 122153a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, found_index); 122253a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, NUM_ROLES); 122353a5a1b3Sopenharmony_ci 122453a5a1b3Sopenharmony_ci for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) { 122553a5a1b3Sopenharmony_ci pa_tagstruct_puts(reply, role_names[i]); 122653a5a1b3Sopenharmony_ci pa_tagstruct_putu32(reply, e->priority[i]); 122753a5a1b3Sopenharmony_ci } 122853a5a1b3Sopenharmony_ci 122953a5a1b3Sopenharmony_ci entry_free(e); 123053a5a1b3Sopenharmony_ci } 123153a5a1b3Sopenharmony_ci 123253a5a1b3Sopenharmony_ci pa_xfree(name); 123353a5a1b3Sopenharmony_ci 123453a5a1b3Sopenharmony_ci key = next_key; 123553a5a1b3Sopenharmony_ci } 123653a5a1b3Sopenharmony_ci 123753a5a1b3Sopenharmony_ci break; 123853a5a1b3Sopenharmony_ci } 123953a5a1b3Sopenharmony_ci 124053a5a1b3Sopenharmony_ci case SUBCOMMAND_RENAME: { 124153a5a1b3Sopenharmony_ci 124253a5a1b3Sopenharmony_ci struct entry *e; 124353a5a1b3Sopenharmony_ci const char *device, *description; 124453a5a1b3Sopenharmony_ci 124553a5a1b3Sopenharmony_ci if (pa_tagstruct_gets(t, &device) < 0 || 124653a5a1b3Sopenharmony_ci pa_tagstruct_gets(t, &description) < 0) 124753a5a1b3Sopenharmony_ci goto fail; 124853a5a1b3Sopenharmony_ci 124953a5a1b3Sopenharmony_ci if (!device || !*device || !description || !*description) 125053a5a1b3Sopenharmony_ci goto fail; 125153a5a1b3Sopenharmony_ci 125253a5a1b3Sopenharmony_ci if ((e = entry_read(u, device))) { 125353a5a1b3Sopenharmony_ci pa_xfree(e->description); 125453a5a1b3Sopenharmony_ci e->description = pa_xstrdup(description); 125553a5a1b3Sopenharmony_ci e->user_set_description = true; 125653a5a1b3Sopenharmony_ci 125753a5a1b3Sopenharmony_ci if (entry_write(u, (char *)device, e)) { 125853a5a1b3Sopenharmony_ci apply_entry(u, device, e); 125953a5a1b3Sopenharmony_ci 126053a5a1b3Sopenharmony_ci trigger_save(u); 126153a5a1b3Sopenharmony_ci } 126253a5a1b3Sopenharmony_ci else 126353a5a1b3Sopenharmony_ci pa_log_warn("Could not save device"); 126453a5a1b3Sopenharmony_ci 126553a5a1b3Sopenharmony_ci entry_free(e); 126653a5a1b3Sopenharmony_ci } 126753a5a1b3Sopenharmony_ci else 126853a5a1b3Sopenharmony_ci pa_log_warn("Could not rename device %s, no entry in database", device); 126953a5a1b3Sopenharmony_ci 127053a5a1b3Sopenharmony_ci break; 127153a5a1b3Sopenharmony_ci } 127253a5a1b3Sopenharmony_ci 127353a5a1b3Sopenharmony_ci case SUBCOMMAND_DELETE: 127453a5a1b3Sopenharmony_ci 127553a5a1b3Sopenharmony_ci while (!pa_tagstruct_eof(t)) { 127653a5a1b3Sopenharmony_ci const char *name; 127753a5a1b3Sopenharmony_ci pa_datum key; 127853a5a1b3Sopenharmony_ci 127953a5a1b3Sopenharmony_ci if (pa_tagstruct_gets(t, &name) < 0) 128053a5a1b3Sopenharmony_ci goto fail; 128153a5a1b3Sopenharmony_ci 128253a5a1b3Sopenharmony_ci key.data = (char*) name; 128353a5a1b3Sopenharmony_ci key.size = strlen(name); 128453a5a1b3Sopenharmony_ci 128553a5a1b3Sopenharmony_ci /** @todo: Reindex the priorities */ 128653a5a1b3Sopenharmony_ci pa_database_unset(u->database, &key); 128753a5a1b3Sopenharmony_ci } 128853a5a1b3Sopenharmony_ci 128953a5a1b3Sopenharmony_ci trigger_save(u); 129053a5a1b3Sopenharmony_ci 129153a5a1b3Sopenharmony_ci break; 129253a5a1b3Sopenharmony_ci 129353a5a1b3Sopenharmony_ci case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: { 129453a5a1b3Sopenharmony_ci 129553a5a1b3Sopenharmony_ci bool enable; 129653a5a1b3Sopenharmony_ci 129753a5a1b3Sopenharmony_ci if (pa_tagstruct_get_boolean(t, &enable) < 0) 129853a5a1b3Sopenharmony_ci goto fail; 129953a5a1b3Sopenharmony_ci 130053a5a1b3Sopenharmony_ci if ((u->do_routing = enable)) { 130153a5a1b3Sopenharmony_ci /* Update our caches */ 130253a5a1b3Sopenharmony_ci update_highest_priority_device_indexes(u, "sink:", NULL); 130353a5a1b3Sopenharmony_ci update_highest_priority_device_indexes(u, "source:", NULL); 130453a5a1b3Sopenharmony_ci } 130553a5a1b3Sopenharmony_ci 130653a5a1b3Sopenharmony_ci break; 130753a5a1b3Sopenharmony_ci } 130853a5a1b3Sopenharmony_ci 130953a5a1b3Sopenharmony_ci case SUBCOMMAND_REORDER: { 131053a5a1b3Sopenharmony_ci 131153a5a1b3Sopenharmony_ci const char *role; 131253a5a1b3Sopenharmony_ci struct entry *e; 131353a5a1b3Sopenharmony_ci uint32_t role_index, n_devices; 131453a5a1b3Sopenharmony_ci pa_datum key; 131553a5a1b3Sopenharmony_ci bool done, sink_mode = true; 131653a5a1b3Sopenharmony_ci struct device_t { uint32_t prio; char *device; }; 131753a5a1b3Sopenharmony_ci struct device_t *device; 131853a5a1b3Sopenharmony_ci struct device_t **devices; 131953a5a1b3Sopenharmony_ci uint32_t i, idx, offset; 132053a5a1b3Sopenharmony_ci pa_hashmap *h; 132153a5a1b3Sopenharmony_ci /*void *state;*/ 132253a5a1b3Sopenharmony_ci bool first; 132353a5a1b3Sopenharmony_ci 132453a5a1b3Sopenharmony_ci if (pa_tagstruct_gets(t, &role) < 0 || 132553a5a1b3Sopenharmony_ci pa_tagstruct_getu32(t, &n_devices) < 0 || 132653a5a1b3Sopenharmony_ci n_devices < 1) 132753a5a1b3Sopenharmony_ci goto fail; 132853a5a1b3Sopenharmony_ci 132953a5a1b3Sopenharmony_ci if (PA_INVALID_INDEX == (role_index = get_role_index(role))) 133053a5a1b3Sopenharmony_ci goto fail; 133153a5a1b3Sopenharmony_ci 133253a5a1b3Sopenharmony_ci /* Cycle through the devices given and make sure they exist */ 133353a5a1b3Sopenharmony_ci h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 133453a5a1b3Sopenharmony_ci first = true; 133553a5a1b3Sopenharmony_ci idx = 0; 133653a5a1b3Sopenharmony_ci for (i = 0; i < n_devices; ++i) { 133753a5a1b3Sopenharmony_ci const char *s; 133853a5a1b3Sopenharmony_ci if (pa_tagstruct_gets(t, &s) < 0) { 133953a5a1b3Sopenharmony_ci while ((device = pa_hashmap_steal_first(h))) { 134053a5a1b3Sopenharmony_ci pa_xfree(device->device); 134153a5a1b3Sopenharmony_ci pa_xfree(device); 134253a5a1b3Sopenharmony_ci } 134353a5a1b3Sopenharmony_ci 134453a5a1b3Sopenharmony_ci pa_hashmap_free(h); 134553a5a1b3Sopenharmony_ci pa_log_error("Protocol error on reorder"); 134653a5a1b3Sopenharmony_ci goto fail; 134753a5a1b3Sopenharmony_ci } 134853a5a1b3Sopenharmony_ci 134953a5a1b3Sopenharmony_ci /* Ensure this is a valid entry */ 135053a5a1b3Sopenharmony_ci if (!(e = entry_read(u, s))) { 135153a5a1b3Sopenharmony_ci while ((device = pa_hashmap_steal_first(h))) { 135253a5a1b3Sopenharmony_ci pa_xfree(device->device); 135353a5a1b3Sopenharmony_ci pa_xfree(device); 135453a5a1b3Sopenharmony_ci } 135553a5a1b3Sopenharmony_ci 135653a5a1b3Sopenharmony_ci pa_hashmap_free(h); 135753a5a1b3Sopenharmony_ci pa_log_error("Client specified an unknown device in its reorder list."); 135853a5a1b3Sopenharmony_ci goto fail; 135953a5a1b3Sopenharmony_ci } 136053a5a1b3Sopenharmony_ci entry_free(e); 136153a5a1b3Sopenharmony_ci 136253a5a1b3Sopenharmony_ci if (first) { 136353a5a1b3Sopenharmony_ci first = false; 136453a5a1b3Sopenharmony_ci sink_mode = (0 == strncmp("sink:", s, 5)); 136553a5a1b3Sopenharmony_ci } else if ((sink_mode && 0 != strncmp("sink:", s, 5)) || (!sink_mode && 0 != strncmp("source:", s, 7))) { 136653a5a1b3Sopenharmony_ci while ((device = pa_hashmap_steal_first(h))) { 136753a5a1b3Sopenharmony_ci pa_xfree(device->device); 136853a5a1b3Sopenharmony_ci pa_xfree(device); 136953a5a1b3Sopenharmony_ci } 137053a5a1b3Sopenharmony_ci 137153a5a1b3Sopenharmony_ci pa_hashmap_free(h); 137253a5a1b3Sopenharmony_ci pa_log_error("Attempted to reorder mixed devices (sinks and sources)"); 137353a5a1b3Sopenharmony_ci goto fail; 137453a5a1b3Sopenharmony_ci } 137553a5a1b3Sopenharmony_ci 137653a5a1b3Sopenharmony_ci /* Add the device to our hashmap. If it's already in it, free it now and carry on */ 137753a5a1b3Sopenharmony_ci device = pa_xnew(struct device_t, 1); 137853a5a1b3Sopenharmony_ci device->device = pa_xstrdup(s); 137953a5a1b3Sopenharmony_ci if (pa_hashmap_put(h, device->device, device) == 0) { 138053a5a1b3Sopenharmony_ci device->prio = idx; 138153a5a1b3Sopenharmony_ci idx++; 138253a5a1b3Sopenharmony_ci } else { 138353a5a1b3Sopenharmony_ci pa_xfree(device->device); 138453a5a1b3Sopenharmony_ci pa_xfree(device); 138553a5a1b3Sopenharmony_ci } 138653a5a1b3Sopenharmony_ci } 138753a5a1b3Sopenharmony_ci 138853a5a1b3Sopenharmony_ci /*pa_log_debug("Hashmap contents (received from client)"); 138953a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(device, h, state) { 139053a5a1b3Sopenharmony_ci pa_log_debug(" - %s (%d)", device->device, device->prio); 139153a5a1b3Sopenharmony_ci }*/ 139253a5a1b3Sopenharmony_ci 139353a5a1b3Sopenharmony_ci /* Now cycle through our list and add all the devices. 139453a5a1b3Sopenharmony_ci This has the effect of adding in any in our DB, 139553a5a1b3Sopenharmony_ci not specified in the device list (and thus will be 139653a5a1b3Sopenharmony_ci tacked on at the end) */ 139753a5a1b3Sopenharmony_ci offset = idx; 139853a5a1b3Sopenharmony_ci done = !pa_database_first(u->database, &key, NULL); 139953a5a1b3Sopenharmony_ci 140053a5a1b3Sopenharmony_ci while (!done && idx < 256) { 140153a5a1b3Sopenharmony_ci pa_datum next_key; 140253a5a1b3Sopenharmony_ci 140353a5a1b3Sopenharmony_ci done = !pa_database_next(u->database, &key, &next_key, NULL); 140453a5a1b3Sopenharmony_ci 140553a5a1b3Sopenharmony_ci device = pa_xnew(struct device_t, 1); 140653a5a1b3Sopenharmony_ci device->device = pa_xstrndup(key.data, key.size); 140753a5a1b3Sopenharmony_ci if ((sink_mode && 0 == strncmp("sink:", device->device, 5)) 140853a5a1b3Sopenharmony_ci || (!sink_mode && 0 == strncmp("source:", device->device, 7))) { 140953a5a1b3Sopenharmony_ci 141053a5a1b3Sopenharmony_ci /* Add the device to our hashmap. If it's already in it, free it now and carry on */ 141153a5a1b3Sopenharmony_ci if (pa_hashmap_put(h, device->device, device) == 0 141253a5a1b3Sopenharmony_ci && (e = entry_read(u, device->device))) { 141353a5a1b3Sopenharmony_ci /* We add offset on to the existing priority so that when we order, the 141453a5a1b3Sopenharmony_ci existing entries are always lower priority than the new ones. */ 141553a5a1b3Sopenharmony_ci device->prio = (offset + e->priority[role_index]); 141653a5a1b3Sopenharmony_ci pa_xfree(e); 141753a5a1b3Sopenharmony_ci } 141853a5a1b3Sopenharmony_ci else { 141953a5a1b3Sopenharmony_ci pa_xfree(device->device); 142053a5a1b3Sopenharmony_ci pa_xfree(device); 142153a5a1b3Sopenharmony_ci } 142253a5a1b3Sopenharmony_ci } else { 142353a5a1b3Sopenharmony_ci pa_xfree(device->device); 142453a5a1b3Sopenharmony_ci pa_xfree(device); 142553a5a1b3Sopenharmony_ci } 142653a5a1b3Sopenharmony_ci 142753a5a1b3Sopenharmony_ci pa_datum_free(&key); 142853a5a1b3Sopenharmony_ci 142953a5a1b3Sopenharmony_ci key = next_key; 143053a5a1b3Sopenharmony_ci } 143153a5a1b3Sopenharmony_ci 143253a5a1b3Sopenharmony_ci /*pa_log_debug("Hashmap contents (combined with database)"); 143353a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(device, h, state) { 143453a5a1b3Sopenharmony_ci pa_log_debug(" - %s (%d)", device->device, device->prio); 143553a5a1b3Sopenharmony_ci }*/ 143653a5a1b3Sopenharmony_ci 143753a5a1b3Sopenharmony_ci /* Now we put all the entries in a simple list for sorting it. */ 143853a5a1b3Sopenharmony_ci n_devices = pa_hashmap_size(h); 143953a5a1b3Sopenharmony_ci devices = pa_xnew(struct device_t *, n_devices); 144053a5a1b3Sopenharmony_ci idx = 0; 144153a5a1b3Sopenharmony_ci while ((device = pa_hashmap_steal_first(h))) { 144253a5a1b3Sopenharmony_ci devices[idx++] = device; 144353a5a1b3Sopenharmony_ci } 144453a5a1b3Sopenharmony_ci pa_hashmap_free(h); 144553a5a1b3Sopenharmony_ci 144653a5a1b3Sopenharmony_ci /* Simple bubble sort */ 144753a5a1b3Sopenharmony_ci for (i = 0; i < n_devices; ++i) { 144853a5a1b3Sopenharmony_ci for (uint32_t j = i; j < n_devices; ++j) { 144953a5a1b3Sopenharmony_ci if (devices[i]->prio > devices[j]->prio) { 145053a5a1b3Sopenharmony_ci struct device_t *tmp; 145153a5a1b3Sopenharmony_ci tmp = devices[i]; 145253a5a1b3Sopenharmony_ci devices[i] = devices[j]; 145353a5a1b3Sopenharmony_ci devices[j] = tmp; 145453a5a1b3Sopenharmony_ci } 145553a5a1b3Sopenharmony_ci } 145653a5a1b3Sopenharmony_ci } 145753a5a1b3Sopenharmony_ci 145853a5a1b3Sopenharmony_ci /*pa_log_debug("Sorted device list"); 145953a5a1b3Sopenharmony_ci for (i = 0; i < n_devices; ++i) { 146053a5a1b3Sopenharmony_ci pa_log_debug(" - %s (%d)", devices[i]->device, devices[i]->prio); 146153a5a1b3Sopenharmony_ci }*/ 146253a5a1b3Sopenharmony_ci 146353a5a1b3Sopenharmony_ci /* Go through in order and write the new entry and cleanup our own list */ 146453a5a1b3Sopenharmony_ci idx = 1; 146553a5a1b3Sopenharmony_ci first = true; 146653a5a1b3Sopenharmony_ci for (i = 0; i < n_devices; ++i) { 146753a5a1b3Sopenharmony_ci if ((e = entry_read(u, devices[i]->device))) { 146853a5a1b3Sopenharmony_ci if (e->priority[role_index] == idx) 146953a5a1b3Sopenharmony_ci idx++; 147053a5a1b3Sopenharmony_ci else { 147153a5a1b3Sopenharmony_ci e->priority[role_index] = idx; 147253a5a1b3Sopenharmony_ci 147353a5a1b3Sopenharmony_ci if (entry_write(u, (char *) devices[i]->device, e)) { 147453a5a1b3Sopenharmony_ci first = false; 147553a5a1b3Sopenharmony_ci idx++; 147653a5a1b3Sopenharmony_ci } 147753a5a1b3Sopenharmony_ci } 147853a5a1b3Sopenharmony_ci 147953a5a1b3Sopenharmony_ci pa_xfree(e); 148053a5a1b3Sopenharmony_ci } 148153a5a1b3Sopenharmony_ci pa_xfree(devices[i]->device); 148253a5a1b3Sopenharmony_ci pa_xfree(devices[i]); 148353a5a1b3Sopenharmony_ci } 148453a5a1b3Sopenharmony_ci 148553a5a1b3Sopenharmony_ci pa_xfree(devices); 148653a5a1b3Sopenharmony_ci 148753a5a1b3Sopenharmony_ci if (!first) { 148853a5a1b3Sopenharmony_ci trigger_save(u); 148953a5a1b3Sopenharmony_ci 149053a5a1b3Sopenharmony_ci if (sink_mode) 149153a5a1b3Sopenharmony_ci route_sink_inputs(u, NULL); 149253a5a1b3Sopenharmony_ci else 149353a5a1b3Sopenharmony_ci route_source_outputs(u, NULL); 149453a5a1b3Sopenharmony_ci } 149553a5a1b3Sopenharmony_ci 149653a5a1b3Sopenharmony_ci break; 149753a5a1b3Sopenharmony_ci } 149853a5a1b3Sopenharmony_ci 149953a5a1b3Sopenharmony_ci case SUBCOMMAND_SUBSCRIBE: { 150053a5a1b3Sopenharmony_ci 150153a5a1b3Sopenharmony_ci bool enabled; 150253a5a1b3Sopenharmony_ci 150353a5a1b3Sopenharmony_ci if (pa_tagstruct_get_boolean(t, &enabled) < 0 || 150453a5a1b3Sopenharmony_ci !pa_tagstruct_eof(t)) 150553a5a1b3Sopenharmony_ci goto fail; 150653a5a1b3Sopenharmony_ci 150753a5a1b3Sopenharmony_ci if (enabled) 150853a5a1b3Sopenharmony_ci pa_idxset_put(u->subscribed, c, NULL); 150953a5a1b3Sopenharmony_ci else 151053a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(u->subscribed, c, NULL); 151153a5a1b3Sopenharmony_ci 151253a5a1b3Sopenharmony_ci break; 151353a5a1b3Sopenharmony_ci } 151453a5a1b3Sopenharmony_ci 151553a5a1b3Sopenharmony_ci default: 151653a5a1b3Sopenharmony_ci goto fail; 151753a5a1b3Sopenharmony_ci } 151853a5a1b3Sopenharmony_ci 151953a5a1b3Sopenharmony_ci pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply); 152053a5a1b3Sopenharmony_ci return 0; 152153a5a1b3Sopenharmony_ci 152253a5a1b3Sopenharmony_ci fail: 152353a5a1b3Sopenharmony_ci 152453a5a1b3Sopenharmony_ci if (reply) 152553a5a1b3Sopenharmony_ci pa_tagstruct_free(reply); 152653a5a1b3Sopenharmony_ci 152753a5a1b3Sopenharmony_ci return -1; 152853a5a1b3Sopenharmony_ci} 152953a5a1b3Sopenharmony_ci 153053a5a1b3Sopenharmony_cistatic pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) { 153153a5a1b3Sopenharmony_ci pa_assert(p); 153253a5a1b3Sopenharmony_ci pa_assert(c); 153353a5a1b3Sopenharmony_ci pa_assert(u); 153453a5a1b3Sopenharmony_ci 153553a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(u->subscribed, c, NULL); 153653a5a1b3Sopenharmony_ci return PA_HOOK_OK; 153753a5a1b3Sopenharmony_ci} 153853a5a1b3Sopenharmony_ci 153953a5a1b3Sopenharmony_cistruct prioritised_indexes { 154053a5a1b3Sopenharmony_ci uint32_t index; 154153a5a1b3Sopenharmony_ci int32_t priority; 154253a5a1b3Sopenharmony_ci}; 154353a5a1b3Sopenharmony_ci 154453a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 154553a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 154653a5a1b3Sopenharmony_ci struct userdata *u; 154753a5a1b3Sopenharmony_ci char *state_path; 154853a5a1b3Sopenharmony_ci pa_sink *sink; 154953a5a1b3Sopenharmony_ci pa_source *source; 155053a5a1b3Sopenharmony_ci uint32_t idx; 155153a5a1b3Sopenharmony_ci bool do_routing = false, on_hotplug = true, on_rescue = true; 155253a5a1b3Sopenharmony_ci uint32_t total_devices; 155353a5a1b3Sopenharmony_ci 155453a5a1b3Sopenharmony_ci pa_assert(m); 155553a5a1b3Sopenharmony_ci 155653a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 155753a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments"); 155853a5a1b3Sopenharmony_ci goto fail; 155953a5a1b3Sopenharmony_ci } 156053a5a1b3Sopenharmony_ci 156153a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 || 156253a5a1b3Sopenharmony_ci pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 || 156353a5a1b3Sopenharmony_ci pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) { 156453a5a1b3Sopenharmony_ci pa_log("on_hotplug= and on_rescue= expect boolean arguments"); 156553a5a1b3Sopenharmony_ci goto fail; 156653a5a1b3Sopenharmony_ci } 156753a5a1b3Sopenharmony_ci 156853a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 156953a5a1b3Sopenharmony_ci u->core = m->core; 157053a5a1b3Sopenharmony_ci u->module = m; 157153a5a1b3Sopenharmony_ci u->do_routing = do_routing; 157253a5a1b3Sopenharmony_ci u->on_hotplug = on_hotplug; 157353a5a1b3Sopenharmony_ci u->on_rescue = on_rescue; 157453a5a1b3Sopenharmony_ci u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 157553a5a1b3Sopenharmony_ci 157653a5a1b3Sopenharmony_ci u->protocol = pa_native_protocol_get(m->core); 157753a5a1b3Sopenharmony_ci pa_native_protocol_install_ext(u->protocol, m, extension_cb); 157853a5a1b3Sopenharmony_ci 157953a5a1b3Sopenharmony_ci 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); 158053a5a1b3Sopenharmony_ci 158153a5a1b3Sopenharmony_ci 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); 158253a5a1b3Sopenharmony_ci 158353a5a1b3Sopenharmony_ci /* Used to handle device description management */ 158453a5a1b3Sopenharmony_ci 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); 158553a5a1b3Sopenharmony_ci 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); 158653a5a1b3Sopenharmony_ci 158753a5a1b3Sopenharmony_ci /* The following slots are used to deal with routing */ 158853a5a1b3Sopenharmony_ci /* A little bit later than module-stream-restore, but before module-intended-roles */ 158953a5a1b3Sopenharmony_ci 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); 159053a5a1b3Sopenharmony_ci 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); 159153a5a1b3Sopenharmony_ci 159253a5a1b3Sopenharmony_ci if (on_hotplug) { 159353a5a1b3Sopenharmony_ci /* A little bit later than module-stream-restore, but before module-intended-roles */ 159453a5a1b3Sopenharmony_ci 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); 159553a5a1b3Sopenharmony_ci 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); 159653a5a1b3Sopenharmony_ci } 159753a5a1b3Sopenharmony_ci 159853a5a1b3Sopenharmony_ci if (on_rescue) { 159953a5a1b3Sopenharmony_ci /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, ... */ 160053a5a1b3Sopenharmony_ci 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); 160153a5a1b3Sopenharmony_ci 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); 160253a5a1b3Sopenharmony_ci } 160353a5a1b3Sopenharmony_ci 160453a5a1b3Sopenharmony_ci if (!(state_path = pa_state_path(NULL, true))) 160553a5a1b3Sopenharmony_ci goto fail; 160653a5a1b3Sopenharmony_ci 160753a5a1b3Sopenharmony_ci if (!(u->database = pa_database_open(state_path, "device-manager", true, true))) { 160853a5a1b3Sopenharmony_ci pa_xfree(state_path); 160953a5a1b3Sopenharmony_ci goto fail; 161053a5a1b3Sopenharmony_ci } 161153a5a1b3Sopenharmony_ci 161253a5a1b3Sopenharmony_ci pa_xfree(state_path); 161353a5a1b3Sopenharmony_ci 161453a5a1b3Sopenharmony_ci /* Attempt to inject the devices into the list in priority order */ 161553a5a1b3Sopenharmony_ci total_devices = PA_MAX(pa_idxset_size(m->core->sinks), pa_idxset_size(m->core->sources)); 161653a5a1b3Sopenharmony_ci if (total_devices > 0 && total_devices < 128) { 161753a5a1b3Sopenharmony_ci uint32_t i; 161853a5a1b3Sopenharmony_ci struct prioritised_indexes p_i[128]; 161953a5a1b3Sopenharmony_ci 162053a5a1b3Sopenharmony_ci /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */ 162153a5a1b3Sopenharmony_ci i = 0; 162253a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, m->core->sinks, idx) { 162353a5a1b3Sopenharmony_ci pa_log_debug("Found sink index %u", sink->index); 162453a5a1b3Sopenharmony_ci p_i[i ].index = sink->index; 162553a5a1b3Sopenharmony_ci p_i[i++].priority = sink->priority; 162653a5a1b3Sopenharmony_ci } 162753a5a1b3Sopenharmony_ci /* Bubble sort it (only really useful for first time creation) */ 162853a5a1b3Sopenharmony_ci if (i > 1) 162953a5a1b3Sopenharmony_ci for (uint32_t j = 0; j < i; ++j) 163053a5a1b3Sopenharmony_ci for (uint32_t k = 0; k < i; ++k) 163153a5a1b3Sopenharmony_ci if (p_i[j].priority > p_i[k].priority) { 163253a5a1b3Sopenharmony_ci struct prioritised_indexes tmp_pi = p_i[k]; 163353a5a1b3Sopenharmony_ci p_i[k] = p_i[j]; 163453a5a1b3Sopenharmony_ci p_i[j] = tmp_pi; 163553a5a1b3Sopenharmony_ci } 163653a5a1b3Sopenharmony_ci /* Register it */ 163753a5a1b3Sopenharmony_ci for (uint32_t j = 0; j < i; ++j) 163853a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u); 163953a5a1b3Sopenharmony_ci 164053a5a1b3Sopenharmony_ci /* We cycle over all the available sources so that they are added to our database if they are not in it yet */ 164153a5a1b3Sopenharmony_ci i = 0; 164253a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, m->core->sources, idx) { 164353a5a1b3Sopenharmony_ci p_i[i ].index = source->index; 164453a5a1b3Sopenharmony_ci p_i[i++].priority = source->priority; 164553a5a1b3Sopenharmony_ci } 164653a5a1b3Sopenharmony_ci /* Bubble sort it (only really useful for first time creation) */ 164753a5a1b3Sopenharmony_ci if (i > 1) 164853a5a1b3Sopenharmony_ci for (uint32_t j = 0; j < i; ++j) 164953a5a1b3Sopenharmony_ci for (uint32_t k = 0; k < i; ++k) 165053a5a1b3Sopenharmony_ci if (p_i[j].priority > p_i[k].priority) { 165153a5a1b3Sopenharmony_ci struct prioritised_indexes tmp_pi = p_i[k]; 165253a5a1b3Sopenharmony_ci p_i[k] = p_i[j]; 165353a5a1b3Sopenharmony_ci p_i[j] = tmp_pi; 165453a5a1b3Sopenharmony_ci } 165553a5a1b3Sopenharmony_ci /* Register it */ 165653a5a1b3Sopenharmony_ci for (uint32_t j = 0; j < i; ++j) 165753a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, p_i[j].index, u); 165853a5a1b3Sopenharmony_ci } 165953a5a1b3Sopenharmony_ci else if (total_devices > 0) { 166053a5a1b3Sopenharmony_ci /* This user has a *lot* of devices... */ 166153a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(sink, m->core->sinks, idx) 166253a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); 166353a5a1b3Sopenharmony_ci 166453a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(source, m->core->sources, idx) 166553a5a1b3Sopenharmony_ci subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); 166653a5a1b3Sopenharmony_ci } 166753a5a1b3Sopenharmony_ci 166853a5a1b3Sopenharmony_ci /* Perform the routing (if it's enabled) which will update our priority list cache too */ 166953a5a1b3Sopenharmony_ci for (uint32_t i = 0; i < NUM_ROLES; ++i) { 167053a5a1b3Sopenharmony_ci u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX; 167153a5a1b3Sopenharmony_ci } 167253a5a1b3Sopenharmony_ci 167353a5a1b3Sopenharmony_ci route_sink_inputs(u, NULL); 167453a5a1b3Sopenharmony_ci route_source_outputs(u, NULL); 167553a5a1b3Sopenharmony_ci 167653a5a1b3Sopenharmony_ci#ifdef DUMP_DATABASE 167753a5a1b3Sopenharmony_ci dump_database(u); 167853a5a1b3Sopenharmony_ci#endif 167953a5a1b3Sopenharmony_ci 168053a5a1b3Sopenharmony_ci pa_modargs_free(ma); 168153a5a1b3Sopenharmony_ci return 0; 168253a5a1b3Sopenharmony_ci 168353a5a1b3Sopenharmony_cifail: 168453a5a1b3Sopenharmony_ci pa__done(m); 168553a5a1b3Sopenharmony_ci 168653a5a1b3Sopenharmony_ci if (ma) 168753a5a1b3Sopenharmony_ci pa_modargs_free(ma); 168853a5a1b3Sopenharmony_ci 168953a5a1b3Sopenharmony_ci return -1; 169053a5a1b3Sopenharmony_ci} 169153a5a1b3Sopenharmony_ci 169253a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 169353a5a1b3Sopenharmony_ci struct userdata* u; 169453a5a1b3Sopenharmony_ci 169553a5a1b3Sopenharmony_ci pa_assert(m); 169653a5a1b3Sopenharmony_ci 169753a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 169853a5a1b3Sopenharmony_ci return; 169953a5a1b3Sopenharmony_ci 170053a5a1b3Sopenharmony_ci if (u->subscription) 170153a5a1b3Sopenharmony_ci pa_subscription_free(u->subscription); 170253a5a1b3Sopenharmony_ci 170353a5a1b3Sopenharmony_ci if (u->sink_new_hook_slot) 170453a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_new_hook_slot); 170553a5a1b3Sopenharmony_ci if (u->source_new_hook_slot) 170653a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_new_hook_slot); 170753a5a1b3Sopenharmony_ci 170853a5a1b3Sopenharmony_ci if (u->sink_input_new_hook_slot) 170953a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_input_new_hook_slot); 171053a5a1b3Sopenharmony_ci if (u->source_output_new_hook_slot) 171153a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_output_new_hook_slot); 171253a5a1b3Sopenharmony_ci 171353a5a1b3Sopenharmony_ci if (u->sink_put_hook_slot) 171453a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_put_hook_slot); 171553a5a1b3Sopenharmony_ci if (u->source_put_hook_slot) 171653a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_put_hook_slot); 171753a5a1b3Sopenharmony_ci 171853a5a1b3Sopenharmony_ci if (u->sink_unlink_hook_slot) 171953a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_unlink_hook_slot); 172053a5a1b3Sopenharmony_ci if (u->source_unlink_hook_slot) 172153a5a1b3Sopenharmony_ci pa_hook_slot_free(u->source_unlink_hook_slot); 172253a5a1b3Sopenharmony_ci 172353a5a1b3Sopenharmony_ci if (u->connection_unlink_hook_slot) 172453a5a1b3Sopenharmony_ci pa_hook_slot_free(u->connection_unlink_hook_slot); 172553a5a1b3Sopenharmony_ci 172653a5a1b3Sopenharmony_ci if (u->save_time_event) 172753a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->save_time_event); 172853a5a1b3Sopenharmony_ci 172953a5a1b3Sopenharmony_ci if (u->database) 173053a5a1b3Sopenharmony_ci pa_database_close(u->database); 173153a5a1b3Sopenharmony_ci 173253a5a1b3Sopenharmony_ci if (u->protocol) { 173353a5a1b3Sopenharmony_ci pa_native_protocol_remove_ext(u->protocol, m); 173453a5a1b3Sopenharmony_ci pa_native_protocol_unref(u->protocol); 173553a5a1b3Sopenharmony_ci } 173653a5a1b3Sopenharmony_ci 173753a5a1b3Sopenharmony_ci if (u->subscribed) 173853a5a1b3Sopenharmony_ci pa_idxset_free(u->subscribed, NULL); 173953a5a1b3Sopenharmony_ci 174053a5a1b3Sopenharmony_ci pa_xfree(u); 174153a5a1b3Sopenharmony_ci} 1742