mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
database: Convert our use of database files to save in tagstruct format.
This has the advantage of allowing versioned updates in the future, thus allowing us to be more user friendly going forward (as opposed to just ignoring entries from old versions). The primary motivation for this, however, is to allow variable length storage in each entry which will be needed for upcoming work. At present this commit will ignore any legacy entries but support for reading and subsequently converting legacy entries will be added shortly.
This commit is contained in:
parent
ec4fa4c668
commit
695d536380
4 changed files with 567 additions and 370 deletions
|
|
@ -46,6 +46,7 @@
|
||||||
#include <pulsecore/card.h>
|
#include <pulsecore/card.h>
|
||||||
#include <pulsecore/namereg.h>
|
#include <pulsecore/namereg.h>
|
||||||
#include <pulsecore/database.h>
|
#include <pulsecore/database.h>
|
||||||
|
#include <pulsecore/tagstruct.h>
|
||||||
|
|
||||||
#include "module-card-restore-symdef.h"
|
#include "module-card-restore-symdef.h"
|
||||||
|
|
||||||
|
|
@ -69,12 +70,12 @@ struct userdata {
|
||||||
pa_database *database;
|
pa_database *database;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ENTRY_VERSION 1
|
#define ENTRY_VERSION 2
|
||||||
|
|
||||||
struct entry {
|
struct entry {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
char profile[PA_NAME_MAX];
|
char *profile;
|
||||||
} PA_GCC_PACKED ;
|
};
|
||||||
|
|
||||||
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
|
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
|
|
@ -91,9 +92,24 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
|
||||||
pa_log_info("Synced.");
|
pa_log_info("Synced.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct entry* read_entry(struct userdata *u, const char *name) {
|
static struct entry* entry_new(void) {
|
||||||
|
struct entry *r = pa_xnew0(struct entry, 1);
|
||||||
|
r->version = ENTRY_VERSION;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void entry_free(struct entry* e) {
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
pa_xfree(e->profile);
|
||||||
|
pa_xfree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry* entry_read(struct userdata *u, const char *name) {
|
||||||
pa_datum key, data;
|
pa_datum key, data;
|
||||||
struct entry *e;
|
struct entry *e = NULL;
|
||||||
|
pa_tagstruct *t = NULL;
|
||||||
|
const char* profile;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
|
|
@ -106,31 +122,63 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
|
||||||
if (!pa_database_get(u->database, &key, &data))
|
if (!pa_database_get(u->database, &key, &data))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (data.size != sizeof(struct entry)) {
|
t = pa_tagstruct_new(data.data, data.size);
|
||||||
pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
|
e = entry_new();
|
||||||
|
|
||||||
|
if (pa_tagstruct_getu8(t, &e->version) < 0 ||
|
||||||
|
e->version > ENTRY_VERSION ||
|
||||||
|
pa_tagstruct_gets(t, &profile) < 0) {
|
||||||
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = (struct entry*) data.data;
|
e->profile = pa_xstrdup(profile);
|
||||||
|
|
||||||
if (e->version != ENTRY_VERSION) {
|
if (!pa_tagstruct_eof(t))
|
||||||
pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!memchr(e->profile, 0, sizeof(e->profile))) {
|
pa_tagstruct_free(t);
|
||||||
pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
|
pa_datum_free(&data);
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
|
pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
entry_free(e);
|
||||||
|
if (t)
|
||||||
|
pa_tagstruct_free(t);
|
||||||
pa_datum_free(&data);
|
pa_datum_free(&data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
|
||||||
|
pa_tagstruct *t;
|
||||||
|
pa_datum key, data;
|
||||||
|
pa_bool_t r;
|
||||||
|
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(name);
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
t = pa_tagstruct_new(NULL, 0);
|
||||||
|
pa_tagstruct_putu8(t, e->version);
|
||||||
|
pa_tagstruct_puts(t, e->profile);
|
||||||
|
|
||||||
|
key.data = (char *) name;
|
||||||
|
key.size = strlen(name);
|
||||||
|
|
||||||
|
data.data = (void*)pa_tagstruct_data(t, &data.size);
|
||||||
|
|
||||||
|
r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static void trigger_save(struct userdata *u) {
|
static void trigger_save(struct userdata *u) {
|
||||||
if (u->save_time_event)
|
if (u->save_time_event)
|
||||||
return;
|
return;
|
||||||
|
|
@ -140,8 +188,7 @@ static void trigger_save(struct userdata *u) {
|
||||||
|
|
||||||
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
struct entry entry, *old;
|
struct entry *entry, *old;
|
||||||
pa_datum key, data;
|
|
||||||
pa_card *card;
|
pa_card *card;
|
||||||
|
|
||||||
pa_assert(c);
|
pa_assert(c);
|
||||||
|
|
@ -151,8 +198,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
|
t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_zero(entry);
|
entry = entry_new();
|
||||||
entry.version = ENTRY_VERSION;
|
|
||||||
|
|
||||||
if (!(card = pa_idxset_get_by_index(c->cards, idx)))
|
if (!(card = pa_idxset_get_by_index(c->cards, idx)))
|
||||||
return;
|
return;
|
||||||
|
|
@ -160,29 +206,25 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
if (!card->save_profile)
|
if (!card->save_profile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
|
entry->profile = pa_xstrdup(card->active_profile ? card->active_profile->name : "");
|
||||||
|
|
||||||
if ((old = read_entry(u, card->name))) {
|
if ((old = entry_read(u, card->name))) {
|
||||||
|
|
||||||
if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
|
if (pa_streq(old->profile, entry->profile)) {
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
|
entry_free(entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
key.data = card->name;
|
|
||||||
key.size = strlen(card->name);
|
|
||||||
|
|
||||||
data.data = &entry;
|
|
||||||
data.size = sizeof(entry);
|
|
||||||
|
|
||||||
pa_log_info("Storing profile for card %s.", card->name);
|
pa_log_info("Storing profile for card %s.", card->name);
|
||||||
|
|
||||||
pa_database_set(u->database, &key, &data, TRUE);
|
if (entry_write(u, card->name, entry))
|
||||||
|
trigger_save(u);
|
||||||
|
|
||||||
trigger_save(u);
|
entry_free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
|
static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
|
||||||
|
|
@ -190,7 +232,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
|
||||||
|
|
||||||
pa_assert(new_data);
|
pa_assert(new_data);
|
||||||
|
|
||||||
if ((e = read_entry(u, new_data->name)) && e->profile[0]) {
|
if ((e = entry_read(u, new_data->name)) && e->profile[0]) {
|
||||||
|
|
||||||
if (!new_data->active_profile) {
|
if (!new_data->active_profile) {
|
||||||
pa_log_info("Restoring profile for card %s.", new_data->name);
|
pa_log_info("Restoring profile for card %s.", new_data->name);
|
||||||
|
|
@ -199,7 +241,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
|
||||||
} else
|
} else
|
||||||
pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
|
pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PA_HOOK_OK;
|
return PA_HOOK_OK;
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
#include <pulsecore/pstream.h>
|
#include <pulsecore/pstream.h>
|
||||||
#include <pulsecore/pstream-util.h>
|
#include <pulsecore/pstream-util.h>
|
||||||
#include <pulsecore/database.h>
|
#include <pulsecore/database.h>
|
||||||
|
#include <pulsecore/tagstruct.h>
|
||||||
|
|
||||||
#include "module-device-manager-symdef.h"
|
#include "module-device-manager-symdef.h"
|
||||||
|
|
||||||
|
|
@ -84,6 +85,7 @@ enum {
|
||||||
ROLE_ANIMATION,
|
ROLE_ANIMATION,
|
||||||
ROLE_PRODUCTION,
|
ROLE_PRODUCTION,
|
||||||
ROLE_A11Y,
|
ROLE_A11Y,
|
||||||
|
ROLE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef uint32_t role_indexes_t[NUM_ROLES];
|
typedef uint32_t role_indexes_t[NUM_ROLES];
|
||||||
|
|
@ -128,15 +130,15 @@ struct userdata {
|
||||||
role_indexes_t preferred_sources;
|
role_indexes_t preferred_sources;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ENTRY_VERSION 1
|
#define ENTRY_VERSION 2
|
||||||
|
|
||||||
struct entry {
|
struct entry {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
char description[PA_NAME_MAX];
|
char *description;
|
||||||
pa_bool_t user_set_description;
|
pa_bool_t user_set_description;
|
||||||
char icon[PA_NAME_MAX];
|
char *icon;
|
||||||
role_indexes_t priority;
|
role_indexes_t priority;
|
||||||
} PA_GCC_PACKED;
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SUBCOMMAND_TEST,
|
SUBCOMMAND_TEST,
|
||||||
|
|
@ -150,9 +152,24 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static struct entry* read_entry(struct userdata *u, const char *name) {
|
static struct entry* entry_new(void) {
|
||||||
|
struct entry *r = pa_xnew0(struct entry, 1);
|
||||||
|
r->version = ENTRY_VERSION;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void entry_free(struct entry* e) {
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
pa_xfree(e->description);
|
||||||
|
pa_xfree(e->icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry* entry_read(struct userdata *u, const char *name) {
|
||||||
pa_datum key, data;
|
pa_datum key, data;
|
||||||
struct entry *e;
|
struct entry *e = NULL;
|
||||||
|
pa_tagstruct *t = NULL;
|
||||||
|
const char *description, *icon;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
|
|
@ -165,36 +182,74 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
|
||||||
if (!pa_database_get(u->database, &key, &data))
|
if (!pa_database_get(u->database, &key, &data))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (data.size != sizeof(struct entry)) {
|
t = pa_tagstruct_new(data.data, data.size);
|
||||||
pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
|
e = entry_new();
|
||||||
|
|
||||||
|
if (pa_tagstruct_getu8(t, &e->version) < 0 ||
|
||||||
|
e->version > ENTRY_VERSION ||
|
||||||
|
pa_tagstruct_gets(t, &description) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
|
||||||
|
pa_tagstruct_gets(t, &icon) < 0) {
|
||||||
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = (struct entry*) data.data;
|
e->description = pa_xstrdup(description);
|
||||||
|
e->icon = pa_xstrdup(icon);
|
||||||
|
|
||||||
if (e->version != ENTRY_VERSION) {
|
for (uint8_t i=0; i<ROLE_MAX; ++i) {
|
||||||
pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
|
if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!memchr(e->description, 0, sizeof(e->description))) {
|
if (!pa_tagstruct_eof(t))
|
||||||
pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!memchr(e->icon, 0, sizeof(e->icon))) {
|
pa_tagstruct_free(t);
|
||||||
pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name);
|
pa_datum_free(&data);
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
entry_free(e);
|
||||||
|
if (t)
|
||||||
|
pa_tagstruct_free(t);
|
||||||
pa_datum_free(&data);
|
pa_datum_free(&data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
|
||||||
|
pa_tagstruct *t;
|
||||||
|
pa_datum key, data;
|
||||||
|
pa_bool_t r;
|
||||||
|
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(name);
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
t = pa_tagstruct_new(NULL, 0);
|
||||||
|
pa_tagstruct_putu8(t, e->version);
|
||||||
|
pa_tagstruct_puts(t, e->description);
|
||||||
|
pa_tagstruct_put_boolean(t, e->user_set_description);
|
||||||
|
pa_tagstruct_puts(t, e->icon);
|
||||||
|
for (uint8_t i=0; i<ROLE_MAX; ++i)
|
||||||
|
pa_tagstruct_putu32(t, e->priority[i]);
|
||||||
|
|
||||||
|
key.data = (char *) name;
|
||||||
|
key.size = strlen(name);
|
||||||
|
|
||||||
|
data.data = (void*)pa_tagstruct_data(t, &data.size);
|
||||||
|
|
||||||
|
r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DUMP_DATABASE
|
#ifdef DUMP_DATABASE
|
||||||
static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
|
static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
@ -233,14 +288,14 @@ static void dump_database(struct userdata *u) {
|
||||||
|
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
pa_log_debug(" Got entry: %s", name);
|
pa_log_debug(" Got entry: %s", name);
|
||||||
pa_log_debug(" Description: %s", e->description);
|
pa_log_debug(" Description: %s", e->description);
|
||||||
pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
|
pa_log_debug(" Priorities: None: %3u, Video: %3u, Music: %3u, Game: %3u, Event: %3u",
|
||||||
e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
|
e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
|
||||||
pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
|
pa_log_debug(" Phone: %3u, Anim: %3u, Prodtn: %3u, A11y: %3u",
|
||||||
e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
|
e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -334,9 +389,9 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
|
||||||
pa_assert(a);
|
pa_assert(a);
|
||||||
pa_assert(b);
|
pa_assert(b);
|
||||||
|
|
||||||
if (strncmp(a->description, b->description, sizeof(a->description))
|
if (!pa_streq(a->description, b->description)
|
||||||
|| a->user_set_description != b->user_set_description
|
|| a->user_set_description != b->user_set_description
|
||||||
|| strncmp(a->icon, b->icon, sizeof(a->icon)))
|
|| !pa_streq(a->icon, b->icon))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
for (int i=0; i < NUM_ROLES; ++i)
|
for (int i=0; i < NUM_ROLES; ++i)
|
||||||
|
|
@ -364,9 +419,11 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
pa_assert(prefix);
|
pa_assert(prefix);
|
||||||
|
|
||||||
if ((old = read_entry(u, name)))
|
if ((old = entry_read(u, name))) {
|
||||||
*entry = *old;
|
*entry = *old;
|
||||||
else {
|
entry->description = pa_xstrdup(old->description);
|
||||||
|
entry->icon = pa_xstrdup(old->icon);
|
||||||
|
} else {
|
||||||
/* This is a new device, so make sure we write it's priority list correctly */
|
/* This is a new device, so make sure we write it's priority list correctly */
|
||||||
role_indexes_t max_priority;
|
role_indexes_t max_priority;
|
||||||
pa_datum key;
|
pa_datum key;
|
||||||
|
|
@ -387,12 +444,12 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
|
||||||
|
|
||||||
name2 = pa_xstrndup(key.data, key.size);
|
name2 = pa_xstrndup(key.data, key.size);
|
||||||
|
|
||||||
if ((e = read_entry(u, name2))) {
|
if ((e = entry_read(u, name2))) {
|
||||||
for (uint32_t i = 0; i < NUM_ROLES; ++i) {
|
for (uint32_t i = 0; i < NUM_ROLES; ++i) {
|
||||||
max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
|
max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name2);
|
pa_xfree(name2);
|
||||||
|
|
@ -456,7 +513,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
device_name = get_name(name, prefix);
|
device_name = get_name(name, prefix);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
for (uint32_t i = 0; i < NUM_ROLES; ++i) {
|
for (uint32_t i = 0; i < NUM_ROLES; ++i) {
|
||||||
if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
|
if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
|
||||||
/* We've found a device with a higher priority than that we've currently got,
|
/* We've found a device with a higher priority than that we've currently got,
|
||||||
|
|
@ -497,7 +554,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -631,9 +688,8 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno
|
||||||
|
|
||||||
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
struct entry entry, *old = NULL;
|
struct entry *entry, *old = NULL;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
pa_datum key, data;
|
|
||||||
|
|
||||||
pa_assert(c);
|
pa_assert(c);
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
@ -649,8 +705,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
|
t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_zero(entry);
|
entry = entry_new();
|
||||||
entry.version = ENTRY_VERSION;
|
|
||||||
|
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
||||||
pa_sink_input *si;
|
pa_sink_input *si;
|
||||||
|
|
@ -684,19 +739,21 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
|
|
||||||
name = pa_sprintf_malloc("sink:%s", sink->name);
|
name = pa_sprintf_malloc("sink:%s", sink->name);
|
||||||
|
|
||||||
old = load_or_initialize_entry(u, &entry, name, "sink:");
|
old = load_or_initialize_entry(u, entry, name, "sink:");
|
||||||
|
|
||||||
if (!entry.user_set_description)
|
if (!entry->user_set_description) {
|
||||||
pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
|
pa_xfree(entry->description);
|
||||||
else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
|
entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
|
||||||
|
} else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
|
||||||
/* Warning: If two modules fight over the description, this could cause an infinite loop.
|
/* Warning: If two modules fight over the description, this could cause an infinite loop.
|
||||||
by changing the description here, we retrigger this subscription callback. The only thing stopping us from
|
by changing the description here, we retrigger this subscription callback. The only thing stopping us from
|
||||||
looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
|
looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
|
||||||
the description, this will fail... */
|
the description, this will fail... */
|
||||||
pa_sink_set_description(sink, entry.description);
|
pa_sink_set_description(sink, entry->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
|
pa_xfree(entry->icon);
|
||||||
|
entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
|
||||||
|
|
||||||
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
||||||
pa_source *source;
|
pa_source *source;
|
||||||
|
|
@ -711,48 +768,46 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
|
|
||||||
name = pa_sprintf_malloc("source:%s", source->name);
|
name = pa_sprintf_malloc("source:%s", source->name);
|
||||||
|
|
||||||
old = load_or_initialize_entry(u, &entry, name, "source:");
|
old = load_or_initialize_entry(u, entry, name, "source:");
|
||||||
|
|
||||||
if (!entry.user_set_description)
|
if (!entry->user_set_description) {
|
||||||
pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
|
pa_xfree(entry->description);
|
||||||
else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
|
entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
|
||||||
|
} else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
|
||||||
/* Warning: If two modules fight over the description, this could cause an infinite loop.
|
/* Warning: If two modules fight over the description, this could cause an infinite loop.
|
||||||
by changing the description here, we retrigger this subscription callback. The only thing stopping us from
|
by changing the description here, we retrigger this subscription callback. The only thing stopping us from
|
||||||
looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
|
looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
|
||||||
the description, this will fail... */
|
the description, this will fail... */
|
||||||
pa_source_set_description(source, entry.description);
|
pa_source_set_description(source, entry->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
|
pa_xfree(entry->icon);
|
||||||
|
entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
|
|
||||||
if (old) {
|
if (old) {
|
||||||
|
|
||||||
if (entries_equal(old, &entry)) {
|
if (entries_equal(old, entry)) {
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
key.data = name;
|
|
||||||
key.size = strlen(name);
|
|
||||||
|
|
||||||
data.data = &entry;
|
|
||||||
data.size = sizeof(entry);
|
|
||||||
|
|
||||||
pa_log_info("Storing device %s.", name);
|
pa_log_info("Storing device %s.", name);
|
||||||
|
|
||||||
if (pa_database_set(u->database, &key, &data, TRUE) == 0)
|
if (entry_write(u, name, entry))
|
||||||
trigger_save(u);
|
trigger_save(u);
|
||||||
else
|
else
|
||||||
pa_log_warn("Could not save device");;
|
pa_log_warn("Could not save device");;
|
||||||
|
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -766,13 +821,13 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
|
||||||
|
|
||||||
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
|
if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
|
||||||
pa_log_info("Restoring description for sink %s.", new_data->name);
|
pa_log_info("Restoring description for sink %s.", new_data->name);
|
||||||
pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
|
pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -790,14 +845,14 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
|
||||||
|
|
||||||
name = pa_sprintf_malloc("source:%s", new_data->name);
|
name = pa_sprintf_malloc("source:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
|
if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
|
||||||
/* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
|
/* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
|
||||||
pa_log_info("Restoring description for source %s.", new_data->name);
|
pa_log_info("Restoring description for source %s.", new_data->name);
|
||||||
pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
|
pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1029,7 +1084,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
pa_datum_free(&key);
|
pa_datum_free(&key);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
char *device_name;
|
char *device_name;
|
||||||
uint32_t found_index = PA_INVALID_INDEX;
|
uint32_t found_index = PA_INVALID_INDEX;
|
||||||
|
|
@ -1065,7 +1120,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
pa_tagstruct_putu32(reply, e->priority[i]);
|
pa_tagstruct_putu32(reply, e->priority[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1088,19 +1143,12 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
if (!device || !*device || !description || !*description)
|
if (!device || !*device || !description || !*description)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if ((e = read_entry(u, device))) {
|
if ((e = entry_read(u, device))) {
|
||||||
pa_datum key, data;
|
pa_xfree(e->description);
|
||||||
|
e->description = pa_xstrdup(description);
|
||||||
pa_strlcpy(e->description, description, sizeof(e->description));
|
|
||||||
e->user_set_description = TRUE;
|
e->user_set_description = TRUE;
|
||||||
|
|
||||||
key.data = (char *) device;
|
if (entry_write(u, (char *)device, e)) {
|
||||||
key.size = strlen(device);
|
|
||||||
|
|
||||||
data.data = e;
|
|
||||||
data.size = sizeof(*e);
|
|
||||||
|
|
||||||
if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
|
|
||||||
apply_entry(u, device, e);
|
apply_entry(u, device, e);
|
||||||
|
|
||||||
trigger_save(u);
|
trigger_save(u);
|
||||||
|
|
@ -1108,7 +1156,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
else
|
else
|
||||||
pa_log_warn("Could not save device");
|
pa_log_warn("Could not save device");
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
pa_log_warn("Could not rename device %s, no entry in database", device);
|
pa_log_warn("Could not rename device %s, no entry in database", device);
|
||||||
|
|
@ -1157,7 +1205,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
const char *role;
|
const char *role;
|
||||||
struct entry *e;
|
struct entry *e;
|
||||||
uint32_t role_index, n_devices;
|
uint32_t role_index, n_devices;
|
||||||
pa_datum key, data;
|
pa_datum key;
|
||||||
pa_bool_t done, sink_mode = TRUE;
|
pa_bool_t done, sink_mode = TRUE;
|
||||||
struct device_t { uint32_t prio; char *device; };
|
struct device_t { uint32_t prio; char *device; };
|
||||||
struct device_t *device;
|
struct device_t *device;
|
||||||
|
|
@ -1193,7 +1241,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure this is a valid entry */
|
/* Ensure this is a valid entry */
|
||||||
if (!(e = read_entry(u, s))) {
|
if (!(e = entry_read(u, s))) {
|
||||||
while ((device = pa_hashmap_steal_first(h))) {
|
while ((device = pa_hashmap_steal_first(h))) {
|
||||||
pa_xfree(device->device);
|
pa_xfree(device->device);
|
||||||
pa_xfree(device);
|
pa_xfree(device);
|
||||||
|
|
@ -1203,7 +1251,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
pa_log_error("Client specified an unknown device in it's reorder list.");
|
pa_log_error("Client specified an unknown device in it's reorder list.");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
first = FALSE;
|
first = FALSE;
|
||||||
|
|
@ -1255,7 +1303,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
|
|
||||||
/* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
|
/* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
|
||||||
if (pa_hashmap_put(h, device->device, device) == 0
|
if (pa_hashmap_put(h, device->device, device) == 0
|
||||||
&& (e = read_entry(u, device->device))) {
|
&& (e = entry_read(u, device->device))) {
|
||||||
/* We add offset on to the existing priorirty so that when we order, the
|
/* We add offset on to the existing priorirty so that when we order, the
|
||||||
existing entries are always lower priority than the new ones. */
|
existing entries are always lower priority than the new ones. */
|
||||||
device->prio = (offset + e->priority[role_index]);
|
device->prio = (offset + e->priority[role_index]);
|
||||||
|
|
@ -1310,19 +1358,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
idx = 1;
|
idx = 1;
|
||||||
first = TRUE;
|
first = TRUE;
|
||||||
for (i = 0; i < n_devices; ++i) {
|
for (i = 0; i < n_devices; ++i) {
|
||||||
if ((e = read_entry(u, devices[i]->device))) {
|
if ((e = entry_read(u, devices[i]->device))) {
|
||||||
if (e->priority[role_index] == idx)
|
if (e->priority[role_index] == idx)
|
||||||
idx++;
|
idx++;
|
||||||
else {
|
else {
|
||||||
e->priority[role_index] = idx;
|
e->priority[role_index] = idx;
|
||||||
|
|
||||||
key.data = (char *) devices[i]->device;
|
if (entry_write(u, (char *) devices[i]->device, e)) {
|
||||||
key.size = strlen(devices[i]->device);
|
|
||||||
|
|
||||||
data.data = e;
|
|
||||||
data.size = sizeof(*e);
|
|
||||||
|
|
||||||
if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
|
|
||||||
first = FALSE;
|
first = FALSE;
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
#include <pulsecore/source-output.h>
|
#include <pulsecore/source-output.h>
|
||||||
#include <pulsecore/namereg.h>
|
#include <pulsecore/namereg.h>
|
||||||
#include <pulsecore/database.h>
|
#include <pulsecore/database.h>
|
||||||
|
#include <pulsecore/tagstruct.h>
|
||||||
|
|
||||||
#include "module-device-restore-symdef.h"
|
#include "module-device-restore-symdef.h"
|
||||||
|
|
||||||
|
|
@ -85,16 +86,16 @@ struct userdata {
|
||||||
pa_bool_t restore_port:1;
|
pa_bool_t restore_port:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ENTRY_VERSION 2
|
#define ENTRY_VERSION 3
|
||||||
|
|
||||||
struct entry {
|
struct entry {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
pa_bool_t muted_valid:1, volume_valid:1, port_valid:1;
|
pa_bool_t muted_valid, volume_valid, port_valid;
|
||||||
pa_bool_t muted:1;
|
pa_bool_t muted;
|
||||||
pa_channel_map channel_map;
|
pa_channel_map channel_map;
|
||||||
pa_cvolume volume;
|
pa_cvolume volume;
|
||||||
char port[PA_NAME_MAX];
|
char *port;
|
||||||
} PA_GCC_PACKED;
|
};
|
||||||
|
|
||||||
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
|
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
|
|
@ -111,9 +112,24 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
|
||||||
pa_log_info("Synced.");
|
pa_log_info("Synced.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct entry* read_entry(struct userdata *u, const char *name) {
|
static struct entry* entry_new(void) {
|
||||||
|
struct entry *r = pa_xnew0(struct entry, 1);
|
||||||
|
r->version = ENTRY_VERSION;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void entry_free(struct entry* e) {
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
pa_xfree(e->port);
|
||||||
|
pa_xfree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry* entry_read(struct userdata *u, const char *name) {
|
||||||
pa_datum key, data;
|
pa_datum key, data;
|
||||||
struct entry *e;
|
struct entry *e = NULL;
|
||||||
|
pa_tagstruct *t = NULL;
|
||||||
|
const char* port;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
|
|
@ -126,22 +142,26 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
|
||||||
if (!pa_database_get(u->database, &key, &data))
|
if (!pa_database_get(u->database, &key, &data))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (data.size != sizeof(struct entry)) {
|
t = pa_tagstruct_new(data.data, data.size);
|
||||||
pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
|
e = entry_new();
|
||||||
|
|
||||||
|
if (pa_tagstruct_getu8(t, &e->version) < 0 ||
|
||||||
|
e->version > ENTRY_VERSION ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
|
||||||
|
pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
|
||||||
|
pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
|
||||||
|
pa_tagstruct_gets(t, &port) < 0) {
|
||||||
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = (struct entry*) data.data;
|
e->port = pa_xstrdup(port);
|
||||||
|
|
||||||
if (e->version != ENTRY_VERSION) {
|
if (!pa_tagstruct_eof(t))
|
||||||
pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!memchr(e->port, 0, sizeof(e->port))) {
|
|
||||||
pa_log_warn("Database contains entry for device %s with missing NUL byte in port name", name);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
|
if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
|
||||||
pa_log_warn("Invalid channel map stored in database for device %s", name);
|
pa_log_warn("Invalid channel map stored in database for device %s", name);
|
||||||
|
|
@ -153,14 +173,64 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
pa_datum_free(&data);
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
|
pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
entry_free(e);
|
||||||
|
if (t)
|
||||||
|
pa_tagstruct_free(t);
|
||||||
pa_datum_free(&data);
|
pa_datum_free(&data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
|
||||||
|
pa_tagstruct *t;
|
||||||
|
pa_datum key, data;
|
||||||
|
pa_bool_t r;
|
||||||
|
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(name);
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
t = pa_tagstruct_new(NULL, 0);
|
||||||
|
pa_tagstruct_putu8(t, e->version);
|
||||||
|
pa_tagstruct_put_boolean(t, e->volume_valid);
|
||||||
|
pa_tagstruct_put_channel_map(t, &e->channel_map);
|
||||||
|
pa_tagstruct_put_cvolume(t, &e->volume);
|
||||||
|
pa_tagstruct_put_boolean(t, e->muted_valid);
|
||||||
|
pa_tagstruct_put_boolean(t, e->muted);
|
||||||
|
pa_tagstruct_put_boolean(t, e->port_valid);
|
||||||
|
pa_tagstruct_puts(t, e->port);
|
||||||
|
|
||||||
|
key.data = (char *) name;
|
||||||
|
key.size = strlen(name);
|
||||||
|
|
||||||
|
data.data = (void*)pa_tagstruct_data(t, &data.size);
|
||||||
|
|
||||||
|
r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry* entry_copy(const struct entry *e) {
|
||||||
|
struct entry* r;
|
||||||
|
|
||||||
|
pa_assert(e);
|
||||||
|
r = entry_new();
|
||||||
|
*r = *e;
|
||||||
|
r->port = pa_xstrdup(e->port);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static void trigger_save(struct userdata *u) {
|
static void trigger_save(struct userdata *u) {
|
||||||
if (u->save_time_event)
|
if (u->save_time_event)
|
||||||
return;
|
return;
|
||||||
|
|
@ -172,7 +242,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
|
||||||
pa_cvolume t;
|
pa_cvolume t;
|
||||||
|
|
||||||
if (a->port_valid != b->port_valid ||
|
if (a->port_valid != b->port_valid ||
|
||||||
(a->port_valid && strncmp(a->port, b->port, sizeof(a->port))))
|
(a->port_valid && !pa_streq(a->port, b->port)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (a->muted_valid != b->muted_valid ||
|
if (a->muted_valid != b->muted_valid ||
|
||||||
|
|
@ -189,9 +259,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
|
||||||
|
|
||||||
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
struct entry entry, *old;
|
struct entry *entry, *old;
|
||||||
char *name;
|
char *name;
|
||||||
pa_datum key, data;
|
|
||||||
|
|
||||||
pa_assert(c);
|
pa_assert(c);
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
@ -202,9 +271,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
|
t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_zero(entry);
|
|
||||||
entry.version = ENTRY_VERSION;
|
|
||||||
|
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
pa_sink *sink;
|
pa_sink *sink;
|
||||||
|
|
||||||
|
|
@ -213,23 +279,26 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
|
|
||||||
name = pa_sprintf_malloc("sink:%s", sink->name);
|
name = pa_sprintf_malloc("sink:%s", sink->name);
|
||||||
|
|
||||||
if ((old = read_entry(u, name)))
|
if ((old = entry_read(u, name)))
|
||||||
entry = *old;
|
entry = entry_copy(old);
|
||||||
|
else
|
||||||
|
entry = entry_new();
|
||||||
|
|
||||||
if (sink->save_volume) {
|
if (sink->save_volume) {
|
||||||
entry.channel_map = sink->channel_map;
|
entry->channel_map = sink->channel_map;
|
||||||
entry.volume = *pa_sink_get_volume(sink, FALSE);
|
entry->volume = *pa_sink_get_volume(sink, FALSE);
|
||||||
entry.volume_valid = TRUE;
|
entry->volume_valid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sink->save_muted) {
|
if (sink->save_muted) {
|
||||||
entry.muted = pa_sink_get_mute(sink, FALSE);
|
entry->muted = pa_sink_get_mute(sink, FALSE);
|
||||||
entry.muted_valid = TRUE;
|
entry->muted_valid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sink->save_port) {
|
if (sink->save_port) {
|
||||||
pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port));
|
pa_xfree(entry->port);
|
||||||
entry.port_valid = TRUE;
|
entry->port = pa_xstrdup(sink->active_port ? sink->active_port->name : "");
|
||||||
|
entry->port_valid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -242,50 +311,50 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
|
|
||||||
name = pa_sprintf_malloc("source:%s", source->name);
|
name = pa_sprintf_malloc("source:%s", source->name);
|
||||||
|
|
||||||
if ((old = read_entry(u, name)))
|
if ((old = entry_read(u, name)))
|
||||||
entry = *old;
|
entry = entry_copy(old);
|
||||||
|
else
|
||||||
|
entry = entry_new();
|
||||||
|
|
||||||
if (source->save_volume) {
|
if (source->save_volume) {
|
||||||
entry.channel_map = source->channel_map;
|
entry->channel_map = source->channel_map;
|
||||||
entry.volume = *pa_source_get_volume(source, FALSE);
|
entry->volume = *pa_source_get_volume(source, FALSE);
|
||||||
entry.volume_valid = TRUE;
|
entry->volume_valid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source->save_muted) {
|
if (source->save_muted) {
|
||||||
entry.muted = pa_source_get_mute(source, FALSE);
|
entry->muted = pa_source_get_mute(source, FALSE);
|
||||||
entry.muted_valid = TRUE;
|
entry->muted_valid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source->save_port) {
|
if (source->save_port) {
|
||||||
pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port));
|
pa_xfree(entry->port);
|
||||||
entry.port_valid = TRUE;
|
entry->port = pa_xstrdup(source->active_port ? source->active_port->name : "");
|
||||||
|
entry->port_valid = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_assert(entry);
|
||||||
|
|
||||||
if (old) {
|
if (old) {
|
||||||
|
|
||||||
if (entries_equal(old, &entry)) {
|
if (entries_equal(old, entry)) {
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
key.data = name;
|
|
||||||
key.size = strlen(name);
|
|
||||||
|
|
||||||
data.data = &entry;
|
|
||||||
data.size = sizeof(entry);
|
|
||||||
|
|
||||||
pa_log_info("Storing volume/mute/port for device %s.", name);
|
pa_log_info("Storing volume/mute/port for device %s.", name);
|
||||||
|
|
||||||
pa_database_set(u->database, &key, &data, TRUE);
|
if (entry_write(u, name, entry))
|
||||||
|
trigger_save(u);
|
||||||
|
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
||||||
trigger_save(u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
|
static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
|
||||||
|
|
@ -299,7 +368,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
|
||||||
|
|
||||||
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (e->port_valid) {
|
if (e->port_valid) {
|
||||||
if (!new_data->active_port) {
|
if (!new_data->active_port) {
|
||||||
|
|
@ -310,7 +379,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
|
||||||
pa_log_debug("Not restoring port for sink %s, because already set.", name);
|
pa_log_debug("Not restoring port for sink %s, because already set.", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -329,7 +398,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
|
||||||
|
|
||||||
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
name = pa_sprintf_malloc("sink:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (u->restore_volume && e->volume_valid) {
|
if (u->restore_volume && e->volume_valid) {
|
||||||
|
|
||||||
|
|
@ -357,7 +426,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
|
||||||
pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
|
pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -376,7 +445,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
|
||||||
|
|
||||||
name = pa_sprintf_malloc("source:%s", new_data->name);
|
name = pa_sprintf_malloc("source:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (e->port_valid) {
|
if (e->port_valid) {
|
||||||
if (!new_data->active_port) {
|
if (!new_data->active_port) {
|
||||||
|
|
@ -387,7 +456,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
|
||||||
pa_log_debug("Not restoring port for source %s, because already set.", name);
|
pa_log_debug("Not restoring port for source %s, because already set.", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -406,7 +475,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
|
||||||
|
|
||||||
name = pa_sprintf_malloc("source:%s", new_data->name);
|
name = pa_sprintf_malloc("source:%s", new_data->name);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (u->restore_volume && e->volume_valid) {
|
if (u->restore_volume && e->volume_valid) {
|
||||||
|
|
||||||
|
|
@ -434,7 +503,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
|
||||||
pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
|
pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
#include <pulsecore/pstream.h>
|
#include <pulsecore/pstream.h>
|
||||||
#include <pulsecore/pstream-util.h>
|
#include <pulsecore/pstream-util.h>
|
||||||
#include <pulsecore/database.h>
|
#include <pulsecore/database.h>
|
||||||
|
#include <pulsecore/tagstruct.h>
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
#include <pulsecore/dbus-util.h>
|
#include <pulsecore/dbus-util.h>
|
||||||
|
|
@ -114,17 +115,17 @@ struct userdata {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ENTRY_VERSION 3
|
#define ENTRY_VERSION 4
|
||||||
|
|
||||||
struct entry {
|
struct entry {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
|
pa_bool_t muted_valid, volume_valid, device_valid, card_valid;
|
||||||
pa_bool_t muted:1;
|
pa_bool_t muted;
|
||||||
pa_channel_map channel_map;
|
pa_channel_map channel_map;
|
||||||
pa_cvolume volume;
|
pa_cvolume volume;
|
||||||
char device[PA_NAME_MAX];
|
char* device;
|
||||||
char card[PA_NAME_MAX];
|
char* card;
|
||||||
} PA_GCC_PACKED;
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SUBCOMMAND_TEST,
|
SUBCOMMAND_TEST,
|
||||||
|
|
@ -135,8 +136,13 @@ enum {
|
||||||
SUBCOMMAND_EVENT
|
SUBCOMMAND_EVENT
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct entry *read_entry(struct userdata *u, const char *name);
|
|
||||||
static void apply_entry(struct userdata *u, const char *name, struct entry *e);
|
static struct entry* entry_new(void);
|
||||||
|
static void entry_free(struct entry *e);
|
||||||
|
static struct entry *entry_read(struct userdata *u, const char *name);
|
||||||
|
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace);
|
||||||
|
static struct entry* entry_copy(const struct entry *e);
|
||||||
|
static void entry_apply(struct userdata *u, const char *name, struct entry *e);
|
||||||
static void trigger_save(struct userdata *u);
|
static void trigger_save(struct userdata *u);
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
|
|
@ -590,8 +596,6 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
|
||||||
pa_cvolume vol;
|
pa_cvolume vol;
|
||||||
dbus_bool_t muted = FALSE;
|
dbus_bool_t muted = FALSE;
|
||||||
dbus_bool_t apply_immediately = FALSE;
|
dbus_bool_t apply_immediately = FALSE;
|
||||||
pa_datum key;
|
|
||||||
pa_datum value;
|
|
||||||
struct dbus_entry *dbus_entry = NULL;
|
struct dbus_entry *dbus_entry = NULL;
|
||||||
struct entry *e = NULL;
|
struct entry *e = NULL;
|
||||||
|
|
||||||
|
|
@ -624,7 +628,7 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
|
||||||
pa_bool_t volume_updated = FALSE;
|
pa_bool_t volume_updated = FALSE;
|
||||||
pa_bool_t device_updated = FALSE;
|
pa_bool_t device_updated = FALSE;
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(u, name));
|
pa_assert_se(e = entry_read(u, name));
|
||||||
mute_updated = e->muted != muted;
|
mute_updated = e->muted != muted;
|
||||||
e->muted = muted;
|
e->muted = muted;
|
||||||
e->muted_valid = TRUE;
|
e->muted_valid = TRUE;
|
||||||
|
|
@ -635,7 +639,8 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
|
||||||
e->volume_valid = !!map.channels;
|
e->volume_valid = !!map.channels;
|
||||||
|
|
||||||
device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
|
device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
|
||||||
pa_strlcpy(e->device, device, sizeof(e->device));
|
pa_xfree(e->device);
|
||||||
|
e->device = pa_xstrdup(device);
|
||||||
e->device_valid = !!device[0];
|
e->device_valid = !!device[0];
|
||||||
|
|
||||||
if (mute_updated)
|
if (mute_updated)
|
||||||
|
|
@ -649,34 +654,28 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
|
||||||
dbus_entry = dbus_entry_new(u, name);
|
dbus_entry = dbus_entry_new(u, name);
|
||||||
pa_assert_se(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) == 0);
|
pa_assert_se(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) == 0);
|
||||||
|
|
||||||
e = pa_xnew0(struct entry, 1);
|
e = entry_new();
|
||||||
e->version = ENTRY_VERSION;
|
|
||||||
e->muted_valid = TRUE;
|
e->muted_valid = TRUE;
|
||||||
e->volume_valid = !!map.channels;
|
e->volume_valid = !!map.channels;
|
||||||
e->device_valid = !!device[0];
|
e->device_valid = !!device[0];
|
||||||
e->muted = muted;
|
e->muted = muted;
|
||||||
e->volume = vol;
|
e->volume = vol;
|
||||||
e->channel_map = map;
|
e->channel_map = map;
|
||||||
pa_strlcpy(e->device, device, sizeof(e->device));
|
e->device = pa_xstrdup(device);
|
||||||
|
|
||||||
send_new_entry_signal(dbus_entry);
|
send_new_entry_signal(dbus_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
key.data = (char *) name;
|
pa_assert_se(entry_write(u, name, e, TRUE));
|
||||||
key.size = strlen(name);
|
|
||||||
|
|
||||||
value.data = e;
|
|
||||||
value.size = sizeof(struct entry);
|
|
||||||
|
|
||||||
pa_assert_se(pa_database_set(u->database, &key, &value, TRUE) == 0);
|
|
||||||
if (apply_immediately)
|
if (apply_immediately)
|
||||||
apply_entry(u, name, e);
|
entry_apply(u, name, e);
|
||||||
|
|
||||||
trigger_save(u);
|
trigger_save(u);
|
||||||
|
|
||||||
pa_dbus_send_empty_reply(conn, msg);
|
pa_dbus_send_empty_reply(conn, msg);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
||||||
|
|
@ -727,13 +726,13 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void
|
||||||
pa_assert(msg);
|
pa_assert(msg);
|
||||||
pa_assert(de);
|
pa_assert(de);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
device = e->device_valid ? e->device : "";
|
device = e->device_valid ? e->device : "";
|
||||||
|
|
||||||
pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device);
|
pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
||||||
|
|
@ -749,31 +748,25 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBus
|
||||||
|
|
||||||
dbus_message_iter_get_basic(iter, &device);
|
dbus_message_iter_get_basic(iter, &device);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
|
updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
|
||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
pa_datum key;
|
pa_xfree(e->device);
|
||||||
pa_datum value;
|
e->device = pa_xstrdup(device);
|
||||||
|
|
||||||
pa_strlcpy(e->device, device, sizeof(e->device));
|
|
||||||
e->device_valid = !!device[0];
|
e->device_valid = !!device[0];
|
||||||
|
|
||||||
key.data = de->entry_name;
|
pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
|
||||||
key.size = strlen(de->entry_name);
|
|
||||||
value.data = e;
|
|
||||||
value.size = sizeof(struct entry);
|
|
||||||
pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
|
|
||||||
|
|
||||||
apply_entry(de->userdata, de->entry_name, e);
|
entry_apply(de->userdata, de->entry_name, e);
|
||||||
send_device_updated_signal(de, e);
|
send_device_updated_signal(de, e);
|
||||||
trigger_save(de->userdata);
|
trigger_save(de->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_dbus_send_empty_reply(conn, msg);
|
pa_dbus_send_empty_reply(conn, msg);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
||||||
|
|
@ -786,7 +779,7 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void
|
||||||
pa_assert(msg);
|
pa_assert(msg);
|
||||||
pa_assert(de);
|
pa_assert(de);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
pa_assert_se(reply = dbus_message_new_method_return(msg));
|
pa_assert_se(reply = dbus_message_new_method_return(msg));
|
||||||
|
|
||||||
|
|
@ -795,7 +788,7 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void
|
||||||
|
|
||||||
pa_assert_se(dbus_connection_send(conn, reply, NULL));
|
pa_assert_se(dbus_connection_send(conn, reply, NULL));
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
||||||
|
|
@ -813,32 +806,25 @@ static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBus
|
||||||
if (get_volume_arg(conn, msg, iter, &map, &vol) < 0)
|
if (get_volume_arg(conn, msg, iter, &map, &vol) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
|
updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
|
||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
pa_datum key;
|
|
||||||
pa_datum value;
|
|
||||||
|
|
||||||
e->volume = vol;
|
e->volume = vol;
|
||||||
e->channel_map = map;
|
e->channel_map = map;
|
||||||
e->volume_valid = !!map.channels;
|
e->volume_valid = !!map.channels;
|
||||||
|
|
||||||
key.data = de->entry_name;
|
pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
|
||||||
key.size = strlen(de->entry_name);
|
|
||||||
value.data = e;
|
|
||||||
value.size = sizeof(struct entry);
|
|
||||||
pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
|
|
||||||
|
|
||||||
apply_entry(de->userdata, de->entry_name, e);
|
entry_apply(de->userdata, de->entry_name, e);
|
||||||
send_volume_updated_signal(de, e);
|
send_volume_updated_signal(de, e);
|
||||||
trigger_save(de->userdata);
|
trigger_save(de->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_dbus_send_empty_reply(conn, msg);
|
pa_dbus_send_empty_reply(conn, msg);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
||||||
|
|
@ -850,13 +836,13 @@ static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *
|
||||||
pa_assert(msg);
|
pa_assert(msg);
|
||||||
pa_assert(de);
|
pa_assert(de);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
mute = e->muted_valid ? e->muted : FALSE;
|
mute = e->muted_valid ? e->muted : FALSE;
|
||||||
|
|
||||||
pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute);
|
pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
|
||||||
|
|
@ -872,31 +858,24 @@ static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMe
|
||||||
|
|
||||||
dbus_message_iter_get_basic(iter, &mute);
|
dbus_message_iter_get_basic(iter, &mute);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
updated = !e->muted_valid || e->muted != mute;
|
updated = !e->muted_valid || e->muted != mute;
|
||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
pa_datum key;
|
|
||||||
pa_datum value;
|
|
||||||
|
|
||||||
e->muted = mute;
|
e->muted = mute;
|
||||||
e->muted_valid = TRUE;
|
e->muted_valid = TRUE;
|
||||||
|
|
||||||
key.data = de->entry_name;
|
pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
|
||||||
key.size = strlen(de->entry_name);
|
|
||||||
value.data = e;
|
|
||||||
value.size = sizeof(struct entry);
|
|
||||||
pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
|
|
||||||
|
|
||||||
apply_entry(de->userdata, de->entry_name, e);
|
entry_apply(de->userdata, de->entry_name, e);
|
||||||
send_mute_updated_signal(de, e);
|
send_mute_updated_signal(de, e);
|
||||||
trigger_save(de->userdata);
|
trigger_save(de->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_dbus_send_empty_reply(conn, msg);
|
pa_dbus_send_empty_reply(conn, msg);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
||||||
|
|
@ -913,7 +892,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
|
||||||
pa_assert(msg);
|
pa_assert(msg);
|
||||||
pa_assert(de);
|
pa_assert(de);
|
||||||
|
|
||||||
pa_assert_se(e = read_entry(de->userdata, de->entry_name));
|
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
|
||||||
|
|
||||||
device = e->device_valid ? e->device : "";
|
device = e->device_valid ? e->device : "";
|
||||||
mute = e->muted_valid ? e->muted : FALSE;
|
mute = e->muted_valid ? e->muted : FALSE;
|
||||||
|
|
@ -942,7 +921,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
|
||||||
|
|
||||||
dbus_message_unref(reply);
|
dbus_message_unref(reply);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
|
||||||
|
|
@ -1009,9 +988,25 @@ static char *get_name(pa_proplist *p, const char *prefix) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct entry *read_entry(struct userdata *u, const char *name) {
|
static struct entry* entry_new(void) {
|
||||||
|
struct entry *r = pa_xnew0(struct entry, 1);
|
||||||
|
r->version = ENTRY_VERSION;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void entry_free(struct entry* e) {
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
pa_xfree(e->device);
|
||||||
|
pa_xfree(e->card);
|
||||||
|
pa_xfree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry *entry_read(struct userdata *u, const char *name) {
|
||||||
pa_datum key, data;
|
pa_datum key, data;
|
||||||
struct entry *e;
|
struct entry *e = NULL;
|
||||||
|
pa_tagstruct *t = NULL;
|
||||||
|
const char *device, *card;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
pa_assert(name);
|
pa_assert(name);
|
||||||
|
|
@ -1024,29 +1019,29 @@ static struct entry *read_entry(struct userdata *u, const char *name) {
|
||||||
if (!pa_database_get(u->database, &key, &data))
|
if (!pa_database_get(u->database, &key, &data))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (data.size != sizeof(struct entry)) {
|
t = pa_tagstruct_new(data.data, data.size);
|
||||||
/* This is probably just a database upgrade, hence let's not
|
e = entry_new();
|
||||||
* consider this more than a debug message */
|
|
||||||
pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
|
if (pa_tagstruct_getu8(t, &e->version) < 0 ||
|
||||||
|
e->version > ENTRY_VERSION ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
|
||||||
|
pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
|
||||||
|
pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->device_valid) < 0 ||
|
||||||
|
pa_tagstruct_gets(t, &device) < 0 ||
|
||||||
|
pa_tagstruct_get_boolean(t, &e->card_valid) < 0 ||
|
||||||
|
pa_tagstruct_gets(t, &card) < 0) {
|
||||||
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
e = (struct entry*) data.data;
|
e->device = pa_xstrdup(device);
|
||||||
|
e->card = pa_xstrdup(card);
|
||||||
|
|
||||||
if (e->version != ENTRY_VERSION) {
|
if (!pa_tagstruct_eof(t))
|
||||||
pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
if (!memchr(e->device, 0, sizeof(e->device))) {
|
|
||||||
pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memchr(e->card, 0, sizeof(e->card))) {
|
|
||||||
pa_log_warn("Database contains entry for stream %s with missing NUL byte in card name", name);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
|
if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
|
||||||
pa_log_warn("Invalid device name stored in database for stream %s", name);
|
pa_log_warn("Invalid device name stored in database for stream %s", name);
|
||||||
|
|
@ -1068,14 +1063,67 @@ static struct entry *read_entry(struct userdata *u, const char *name) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
pa_datum_free(&data);
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
|
pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
entry_free(e);
|
||||||
|
if (t)
|
||||||
|
pa_tagstruct_free(t);
|
||||||
pa_datum_free(&data);
|
pa_datum_free(&data);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace) {
|
||||||
|
pa_tagstruct *t;
|
||||||
|
pa_datum key, data;
|
||||||
|
pa_bool_t r;
|
||||||
|
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(name);
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
t = pa_tagstruct_new(NULL, 0);
|
||||||
|
pa_tagstruct_putu8(t, e->version);
|
||||||
|
pa_tagstruct_put_boolean(t, e->volume_valid);
|
||||||
|
pa_tagstruct_put_channel_map(t, &e->channel_map);
|
||||||
|
pa_tagstruct_put_cvolume(t, &e->volume);
|
||||||
|
pa_tagstruct_put_boolean(t, e->muted_valid);
|
||||||
|
pa_tagstruct_put_boolean(t, e->muted);
|
||||||
|
pa_tagstruct_put_boolean(t, e->device_valid);
|
||||||
|
pa_tagstruct_puts(t, e->device);
|
||||||
|
pa_tagstruct_put_boolean(t, e->card_valid);
|
||||||
|
pa_tagstruct_puts(t, e->card);
|
||||||
|
|
||||||
|
key.data = (char *) name;
|
||||||
|
key.size = strlen(name);
|
||||||
|
|
||||||
|
data.data = (void*)pa_tagstruct_data(t, &data.size);
|
||||||
|
|
||||||
|
r = (pa_database_set(u->database, &key, &data, replace) == 0);
|
||||||
|
|
||||||
|
pa_tagstruct_free(t);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct entry* entry_copy(const struct entry *e) {
|
||||||
|
struct entry* r;
|
||||||
|
|
||||||
|
pa_assert(e);
|
||||||
|
r = entry_new();
|
||||||
|
*r = *e;
|
||||||
|
r->device = pa_xstrdup(e->device);
|
||||||
|
r->card = pa_xstrdup(e->card);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static void trigger_save(struct userdata *u) {
|
static void trigger_save(struct userdata *u) {
|
||||||
pa_native_connection *c;
|
pa_native_connection *c;
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
|
|
@ -1106,11 +1154,11 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
|
||||||
pa_assert(b);
|
pa_assert(b);
|
||||||
|
|
||||||
if (a->device_valid != b->device_valid ||
|
if (a->device_valid != b->device_valid ||
|
||||||
(a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
|
(a->device_valid && !pa_streq(a->device, b->device)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (a->card_valid != b->card_valid ||
|
if (a->card_valid != b->card_valid ||
|
||||||
(a->card_valid && strncmp(a->card, b->card, sizeof(a->card))))
|
(a->card_valid && !pa_streq(a->card, b->card)))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (a->muted_valid != b->muted_valid ||
|
if (a->muted_valid != b->muted_valid ||
|
||||||
|
|
@ -1127,9 +1175,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
|
||||||
|
|
||||||
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
struct entry entry, *old = NULL;
|
struct entry *entry, *old = NULL;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
pa_datum key, data;
|
|
||||||
|
|
||||||
/* These are only used when D-Bus is enabled, but in order to reduce ifdef
|
/* These are only used when D-Bus is enabled, but in order to reduce ifdef
|
||||||
* clutter these are defined here unconditionally. */
|
* clutter these are defined here unconditionally. */
|
||||||
|
|
@ -1151,9 +1198,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
|
t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pa_zero(entry);
|
|
||||||
entry.version = ENTRY_VERSION;
|
|
||||||
|
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
|
||||||
pa_sink_input *sink_input;
|
pa_sink_input *sink_input;
|
||||||
|
|
||||||
|
|
@ -1163,39 +1207,42 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
if (!(name = get_name(sink_input->proplist, "sink-input")))
|
if (!(name = get_name(sink_input->proplist, "sink-input")))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((old = read_entry(u, name))) {
|
if ((old = entry_read(u, name))) {
|
||||||
entry = *old;
|
entry = entry_copy(old);
|
||||||
created_new_entry = FALSE;
|
created_new_entry = FALSE;
|
||||||
}
|
} else
|
||||||
|
entry = entry_new();
|
||||||
|
|
||||||
if (sink_input->save_volume && pa_sink_input_is_volume_readable(sink_input)) {
|
if (sink_input->save_volume && pa_sink_input_is_volume_readable(sink_input)) {
|
||||||
pa_assert(sink_input->volume_writable);
|
pa_assert(sink_input->volume_writable);
|
||||||
|
|
||||||
entry.channel_map = sink_input->channel_map;
|
entry->channel_map = sink_input->channel_map;
|
||||||
pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
|
pa_sink_input_get_volume(sink_input, &entry->volume, FALSE);
|
||||||
entry.volume_valid = TRUE;
|
entry->volume_valid = TRUE;
|
||||||
|
|
||||||
volume_updated = !created_new_entry
|
volume_updated = !created_new_entry
|
||||||
&& (!old->volume_valid
|
&& (!old->volume_valid
|
||||||
|| !pa_channel_map_equal(&entry.channel_map, &old->channel_map)
|
|| !pa_channel_map_equal(&entry->channel_map, &old->channel_map)
|
||||||
|| !pa_cvolume_equal(&entry.volume, &old->volume));
|
|| !pa_cvolume_equal(&entry->volume, &old->volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sink_input->save_muted) {
|
if (sink_input->save_muted) {
|
||||||
entry.muted = pa_sink_input_get_mute(sink_input);
|
entry->muted = pa_sink_input_get_mute(sink_input);
|
||||||
entry.muted_valid = TRUE;
|
entry->muted_valid = TRUE;
|
||||||
|
|
||||||
mute_updated = !created_new_entry && (!old->muted_valid || entry.muted != old->muted);
|
mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sink_input->save_sink) {
|
if (sink_input->save_sink) {
|
||||||
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
|
pa_xfree(entry->device);
|
||||||
entry.device_valid = TRUE;
|
entry->device = pa_xstrdup(sink_input->sink->name);
|
||||||
|
entry->device_valid = TRUE;
|
||||||
|
|
||||||
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
|
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
|
||||||
if (sink_input->sink->card) {
|
if (sink_input->sink->card) {
|
||||||
pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card));
|
pa_xfree(entry->card);
|
||||||
entry.card_valid = TRUE;
|
entry->card = pa_xstrdup(sink_input->sink->card->name);
|
||||||
|
entry->card_valid = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1210,44 +1257,45 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
if (!(name = get_name(source_output->proplist, "source-output")))
|
if (!(name = get_name(source_output->proplist, "source-output")))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ((old = read_entry(u, name))) {
|
if ((old = entry_read(u, name))) {
|
||||||
entry = *old;
|
entry = entry_copy(old);
|
||||||
created_new_entry = FALSE;
|
created_new_entry = FALSE;
|
||||||
}
|
} else
|
||||||
|
entry = entry_new();
|
||||||
|
|
||||||
if (source_output->save_source) {
|
if (source_output->save_source) {
|
||||||
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
|
pa_xfree(entry->device);
|
||||||
entry.device_valid = TRUE;
|
entry->device = pa_xstrdup(source_output->source->name);
|
||||||
|
entry->device_valid = TRUE;
|
||||||
|
|
||||||
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
|
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
|
||||||
|
|
||||||
if (source_output->source->card) {
|
if (source_output->source->card) {
|
||||||
pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card));
|
pa_xfree(entry->card);
|
||||||
entry.card_valid = TRUE;
|
entry->card = pa_xstrdup(source_output->source->card->name);
|
||||||
|
entry->card_valid = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_assert(entry);
|
||||||
|
|
||||||
if (old) {
|
if (old) {
|
||||||
|
|
||||||
if (entries_equal(old, &entry)) {
|
if (entries_equal(old, entry)) {
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
}
|
}
|
||||||
|
|
||||||
key.data = name;
|
|
||||||
key.size = strlen(name);
|
|
||||||
|
|
||||||
data.data = &entry;
|
|
||||||
data.size = sizeof(entry);
|
|
||||||
|
|
||||||
pa_log_info("Storing volume/mute/device for stream %s.", name);
|
pa_log_info("Storing volume/mute/device for stream %s.", name);
|
||||||
|
|
||||||
pa_database_set(u->database, &key, &data, TRUE);
|
if (entry_write(u, name, entry, TRUE))
|
||||||
|
trigger_save(u);
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (created_new_entry) {
|
if (created_new_entry) {
|
||||||
|
|
@ -1258,17 +1306,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
|
||||||
pa_assert_se(de = pa_hashmap_get(u->dbus_entries, name));
|
pa_assert_se(de = pa_hashmap_get(u->dbus_entries, name));
|
||||||
|
|
||||||
if (device_updated)
|
if (device_updated)
|
||||||
send_device_updated_signal(de, &entry);
|
send_device_updated_signal(de, entry);
|
||||||
if (volume_updated)
|
if (volume_updated)
|
||||||
send_volume_updated_signal(de, &entry);
|
send_volume_updated_signal(de, entry);
|
||||||
if (mute_updated)
|
if (mute_updated)
|
||||||
send_mute_updated_signal(de, &entry);
|
send_mute_updated_signal(de, entry);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
entry_free(entry);
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
||||||
trigger_save(u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
|
static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
|
||||||
|
|
@ -1285,7 +1332,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
||||||
|
|
||||||
if (new_data->sink)
|
if (new_data->sink)
|
||||||
pa_log_debug("Not restoring device for stream %s, because already set to '%s'.", name, new_data->sink->name);
|
pa_log_debug("Not restoring device for stream %s, because already set to '%s'.", name, new_data->sink->name);
|
||||||
else if ((e = read_entry(u, name))) {
|
else if ((e = entry_read(u, name))) {
|
||||||
pa_sink *s = NULL;
|
pa_sink *s = NULL;
|
||||||
|
|
||||||
if (e->device_valid)
|
if (e->device_valid)
|
||||||
|
|
@ -1305,7 +1352,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
|
||||||
if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
|
if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
|
||||||
pa_log_info("Restoring device for stream %s.", name);
|
pa_log_info("Restoring device for stream %s.", name);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1325,7 +1372,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
|
||||||
if (!(name = get_name(new_data->proplist, "sink-input")))
|
if (!(name = get_name(new_data->proplist, "sink-input")))
|
||||||
return PA_HOOK_OK;
|
return PA_HOOK_OK;
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (u->restore_volume && e->volume_valid) {
|
if (u->restore_volume && e->volume_valid) {
|
||||||
if (!new_data->volume_writable)
|
if (!new_data->volume_writable)
|
||||||
|
|
@ -1356,7 +1403,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
|
||||||
pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
|
pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1381,7 +1428,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
|
||||||
|
|
||||||
if (new_data->source)
|
if (new_data->source)
|
||||||
pa_log_debug("Not restoring device for stream %s, because already set", name);
|
pa_log_debug("Not restoring device for stream %s, because already set", name);
|
||||||
else if ((e = read_entry(u, name))) {
|
else if ((e = entry_read(u, name))) {
|
||||||
pa_source *s = NULL;
|
pa_source *s = NULL;
|
||||||
|
|
||||||
if (e->device_valid)
|
if (e->device_valid)
|
||||||
|
|
@ -1402,7 +1449,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
|
||||||
pa_source_output_new_data_set_source(new_data, s, TRUE);
|
pa_source_output_new_data_set_source(new_data, s, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1443,11 +1490,11 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
|
||||||
if (!(name = get_name(si->proplist, "sink-input")))
|
if (!(name = get_name(si->proplist, "sink-input")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
if (e->device_valid && pa_streq(e->device, sink->name))
|
if (e->device_valid && pa_streq(e->device, sink->name))
|
||||||
pa_sink_input_move_to(si, sink, TRUE);
|
pa_sink_input_move_to(si, sink, TRUE);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1491,11 +1538,11 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
|
||||||
if (!(name = get_name(so->proplist, "source-output")))
|
if (!(name = get_name(so->proplist, "source-output")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
if (e->device_valid && pa_streq(e->device, source->name))
|
if (e->device_valid && pa_streq(e->device, source->name))
|
||||||
pa_source_output_move_to(so, source, TRUE);
|
pa_source_output_move_to(so, source, TRUE);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1527,7 +1574,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
|
||||||
if (!(name = get_name(si->proplist, "sink-input")))
|
if (!(name = get_name(si->proplist, "sink-input")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (e->device_valid) {
|
if (e->device_valid) {
|
||||||
pa_sink *d;
|
pa_sink *d;
|
||||||
|
|
@ -1538,7 +1585,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
|
||||||
pa_sink_input_move_to(si, d, TRUE);
|
pa_sink_input_move_to(si, d, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1573,7 +1620,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
|
||||||
if (!(name = get_name(so->proplist, "source-output")))
|
if (!(name = get_name(so->proplist, "source-output")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
|
|
||||||
if (e->device_valid) {
|
if (e->device_valid) {
|
||||||
pa_source *d;
|
pa_source *d;
|
||||||
|
|
@ -1584,7 +1631,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
|
||||||
pa_source_output_move_to(so, d, TRUE);
|
pa_source_output_move_to(so, d, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1595,7 +1642,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
|
||||||
|
|
||||||
#define EXT_VERSION 1
|
#define EXT_VERSION 1
|
||||||
|
|
||||||
static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
|
static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
|
||||||
pa_sink_input *si;
|
pa_sink_input *si;
|
||||||
pa_source_output *so;
|
pa_source_output *so;
|
||||||
uint32_t idx;
|
uint32_t idx;
|
||||||
|
|
@ -1704,14 +1751,14 @@ PA_GCC_UNUSED static void stream_restore_dump_database(struct userdata *u) {
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
pa_datum_free(&key);
|
pa_datum_free(&key);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
char t[256];
|
char t[256];
|
||||||
pa_log("name=%s", name);
|
pa_log("name=%s", name);
|
||||||
pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
|
pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
|
||||||
pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
|
pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
|
||||||
pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
|
pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
|
||||||
pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
|
pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1768,7 +1815,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
pa_datum_free(&key);
|
pa_datum_free(&key);
|
||||||
|
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
pa_cvolume r;
|
pa_cvolume r;
|
||||||
pa_channel_map cm;
|
pa_channel_map cm;
|
||||||
|
|
||||||
|
|
@ -1778,7 +1825,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
|
pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
|
||||||
pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
|
pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
|
||||||
|
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
@ -1818,74 +1865,70 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
while (!pa_tagstruct_eof(t)) {
|
while (!pa_tagstruct_eof(t)) {
|
||||||
const char *name, *device;
|
const char *name, *device;
|
||||||
pa_bool_t muted;
|
pa_bool_t muted;
|
||||||
struct entry entry;
|
struct entry *entry;
|
||||||
pa_datum key, data;
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
struct entry *old;
|
struct entry *old;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pa_zero(entry);
|
entry = entry_new();
|
||||||
entry.version = ENTRY_VERSION;
|
|
||||||
|
|
||||||
if (pa_tagstruct_gets(t, &name) < 0 ||
|
if (pa_tagstruct_gets(t, &name) < 0 ||
|
||||||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
|
pa_tagstruct_get_channel_map(t, &entry->channel_map) ||
|
||||||
pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
|
pa_tagstruct_get_cvolume(t, &entry->volume) < 0 ||
|
||||||
pa_tagstruct_gets(t, &device) < 0 ||
|
pa_tagstruct_gets(t, &device) < 0 ||
|
||||||
pa_tagstruct_get_boolean(t, &muted) < 0)
|
pa_tagstruct_get_boolean(t, &muted) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (!name || !*name)
|
if (!name || !*name) {
|
||||||
|
entry_free(entry);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
entry.volume_valid = entry.volume.channels > 0;
|
entry->volume_valid = entry->volume.channels > 0;
|
||||||
|
|
||||||
if (entry.volume_valid)
|
if (entry->volume_valid)
|
||||||
if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
|
if (!pa_cvolume_compatible_with_channel_map(&entry->volume, &entry->channel_map)) {
|
||||||
|
entry_free(entry);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
entry.muted = muted;
|
entry->muted = muted;
|
||||||
entry.muted_valid = TRUE;
|
entry->muted_valid = TRUE;
|
||||||
|
|
||||||
if (device)
|
entry->device = pa_xstrdup(device);
|
||||||
pa_strlcpy(entry.device, device, sizeof(entry.device));
|
entry->device_valid = device && !!entry->device[0];
|
||||||
entry.device_valid = !!entry.device[0];
|
|
||||||
|
|
||||||
if (entry.device_valid &&
|
if (entry->device_valid && !pa_namereg_is_valid_name(entry->device)) {
|
||||||
!pa_namereg_is_valid_name(entry.device))
|
entry_free(entry);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
old = read_entry(u, name);
|
old = entry_read(u, name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
key.data = (char*) name;
|
|
||||||
key.size = strlen(name);
|
|
||||||
|
|
||||||
data.data = &entry;
|
|
||||||
data.size = sizeof(entry);
|
|
||||||
|
|
||||||
pa_log_debug("Client %s changes entry %s.",
|
pa_log_debug("Client %s changes entry %s.",
|
||||||
pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)),
|
pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)),
|
||||||
name);
|
name);
|
||||||
|
|
||||||
if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) {
|
if (entry_write(u, name, entry, mode == PA_UPDATE_REPLACE)) {
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
struct dbus_entry *de;
|
struct dbus_entry *de;
|
||||||
|
|
||||||
if (old) {
|
if (old) {
|
||||||
pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
|
pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
|
||||||
|
|
||||||
if ((old->device_valid != entry.device_valid)
|
if ((old->device_valid != entry->device_valid)
|
||||||
|| (entry.device_valid && !pa_streq(entry.device, old->device)))
|
|| (entry->device_valid && !pa_streq(entry->device, old->device)))
|
||||||
send_device_updated_signal(de, &entry);
|
send_device_updated_signal(de, entry);
|
||||||
|
|
||||||
if ((old->volume_valid != entry.volume_valid)
|
if ((old->volume_valid != entry->volume_valid)
|
||||||
|| (entry.volume_valid && (!pa_cvolume_equal(&entry.volume, &old->volume)
|
|| (entry->volume_valid && (!pa_cvolume_equal(&entry->volume, &old->volume)
|
||||||
|| !pa_channel_map_equal(&entry.channel_map, &old->channel_map))))
|
|| !pa_channel_map_equal(&entry->channel_map, &old->channel_map))))
|
||||||
send_volume_updated_signal(de, &entry);
|
send_volume_updated_signal(de, entry);
|
||||||
|
|
||||||
if (!old->muted_valid || (entry.muted != old->muted))
|
if (!old->muted_valid || (entry->muted != old->muted))
|
||||||
send_mute_updated_signal(de, &entry);
|
send_mute_updated_signal(de, entry);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
de = dbus_entry_new(u, name);
|
de = dbus_entry_new(u, name);
|
||||||
|
|
@ -1895,13 +1938,14 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (apply_immediately)
|
if (apply_immediately)
|
||||||
apply_entry(u, name, &entry);
|
entry_apply(u, name, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DBUS
|
#ifdef HAVE_DBUS
|
||||||
if (old)
|
if (old)
|
||||||
pa_xfree(old);
|
entry_free(old);
|
||||||
#endif
|
#endif
|
||||||
|
entry_free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger_save(u);
|
trigger_save(u);
|
||||||
|
|
@ -2080,11 +2124,11 @@ int pa__init(pa_module*m) {
|
||||||
name = pa_xstrndup(key.data, key.size);
|
name = pa_xstrndup(key.data, key.size);
|
||||||
pa_datum_free(&key);
|
pa_datum_free(&key);
|
||||||
|
|
||||||
/* Use read_entry() for checking that the entry is valid. */
|
/* Use entry_read() for checking that the entry is valid. */
|
||||||
if ((e = read_entry(u, name))) {
|
if ((e = entry_read(u, name))) {
|
||||||
de = dbus_entry_new(u, name);
|
de = dbus_entry_new(u, name);
|
||||||
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
|
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
|
||||||
pa_xfree(e);
|
entry_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_xfree(name);
|
pa_xfree(name);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue