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