Commit 8a87af38 authored by Tanu Kaskinen's avatar Tanu Kaskinen Committed by PulseAudio Marge Bot
Browse files

stream-restore: Fix NULL preferred device handling

When an application sets a device for a newly created stream, we treat
that as a temporary setting, and don't save it as the preferred device
for future streams. The handling for this was broken, however: if the
stream already had a preferred device saved in the stream-restore
database, that was unset.

This was a regression introduced in
bc0e7283 and
70bbbcdc. These commits tried to detect
in subscribe_callback() when the preferred device is cleared, but as a
side effect the preferred device started to get cleared from the
database also when a stream was created with a device set by the
application.

There's no way for subscribe_callback() to distinguish the different
cases of the preferred device being NULL. This problem is solved by
using the PREFERRED_SINK/SOURCE_CHANGED hooks. The hooks are only called
when the preferred device really changes.

Fixes: #1063
Part-of: <!535>
parent 737ebcdf
......@@ -1294,7 +1294,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
/* These are only used when D-Bus is enabled, but in order to reduce ifdef
* clutter these are defined here unconditionally. */
bool created_new_entry = true;
bool device_updated = false;
bool volume_updated = false;
bool mute_updated = false;
......@@ -1350,25 +1349,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
}
if (sink_input->preferred_sink != NULL || !created_new_entry) {
pa_sink *s = NULL;
pa_xfree(entry->device);
entry->device = pa_xstrdup(sink_input->preferred_sink);
entry->device_valid = true;
if (!entry->device)
entry->device_valid = false;
device_updated = !created_new_entry && !pa_safe_streq(entry->device, old->device);
pa_xfree(entry->card);
entry->card = NULL;
entry->card_valid = false;
if (entry->device_valid && (s = pa_namereg_get(c, entry->device, PA_NAMEREG_SINK)) && s->card) {
entry->card = pa_xstrdup(s->card->name);
entry->card_valid = true;
}
}
} else {
pa_source_output *source_output;
......@@ -1410,26 +1390,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
}
if (source_output->preferred_source != NULL || !created_new_entry) {
pa_source *s = NULL;
pa_xfree(entry->device);
entry->device = pa_xstrdup(source_output->preferred_source);
entry->device_valid = true;
if (!entry->device)
entry->device_valid = false;
device_updated = !created_new_entry && !pa_safe_streq(entry->device, old->device);
pa_xfree(entry->card);
entry->card = NULL;
entry->card_valid = false;
if (entry->device_valid && (s = pa_namereg_get(c, entry->device, PA_NAMEREG_SOURCE)) && s->card) {
entry->card = pa_xstrdup(s->card->name);
entry->card_valid = true;
}
}
}
pa_assert(entry);
......@@ -1446,12 +1406,12 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
entry_free(old);
}
pa_log_info("Storing volume/mute/device for stream %s.", name);
pa_log_info("Storing volume/mute for stream %s.", name);
if (entry_write(u, name, entry, true)) {
trigger_save(u);
} else {
pa_log_error("Could not store volume/mute/device for stream %s.", name);
pa_log_error("Could not store volume/mute for stream %s.", name);
}
#ifdef HAVE_DBUS
......@@ -1460,8 +1420,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
send_new_entry_signal(de);
} else {
if (device_updated)
send_device_updated_signal(de, entry);
if (volume_updated)
send_volume_updated_signal(de, entry);
if (mute_updated)
......@@ -1469,7 +1427,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
}
#else
/* Silence compiler warnings */
(void) device_updated;
(void) volume_updated;
(void) mute_updated;
#endif
......@@ -1582,6 +1539,79 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
return PA_HOOK_OK;
}
static void update_preferred_device(struct userdata *u, const char *name, const char *device, const char *card) {
struct entry *old;
struct entry *entry;
#ifdef HAVE_DBUS
bool created_new_entry = false;
struct dbus_entry *de;
#endif
pa_assert(u);
pa_assert(name);
if ((old = entry_read(u, name)))
entry = entry_copy(old);
else {
entry = entry_new();
#ifdef HAVE_DBUS
created_new_entry = true;
#endif
}
pa_xfree(entry->device);
entry->device = pa_xstrdup(device);
entry->device_valid = !!device;
pa_xfree(entry->card);
entry->card = pa_xstrdup(card);
entry->card_valid = !!card;
pa_log_info("Storing device for stream %s.", name);
entry_write(u, name, entry, true);
trigger_save(u);
#if HAVE_DBUS
if (!(de = pa_hashmap_get(u->dbus_entries, name))) {
de = dbus_entry_new(u, name);
pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
send_new_entry_signal(de);
} else {
/* We send a D-Bus signal when the device changes, but not when the
* card changes. That's becaues the D-Bus interface doesn't expose the
* card field to clients at all. */
if (!created_new_entry && !pa_safe_streq(entry->device, old->device))
send_device_updated_signal(de, entry);
}
#endif
entry_free(entry);
if (old)
entry_free(old);
}
static pa_hook_result_t sink_input_preferred_sink_changed_cb(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
char *name;
pa_sink *sink;
const char *card_name;
pa_assert(c);
pa_assert(sink_input);
pa_assert(u);
if (!(name = pa_proplist_get_stream_group(sink_input->proplist, "sink-input", IDENTIFICATION_PROPERTY)))
return PA_HOOK_OK;
if (sink_input->preferred_sink && (sink = pa_namereg_get(c, sink_input->preferred_sink, PA_NAMEREG_SINK)) && sink->card)
card_name = sink->card->name;
update_preferred_device(u, name, sink_input->preferred_sink, card_name);
pa_xfree(name);
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
char *name;
struct entry *e;
......@@ -1690,6 +1720,27 @@ static pa_hook_result_t source_output_fixate_hook_callback(pa_core *c, pa_source
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_preferred_source_changed_cb(pa_core *c, pa_source_output *source_output, struct userdata *u) {
char *name;
pa_source *source;
const char *card_name;
pa_assert(c);
pa_assert(source_output);
pa_assert(u);
if (!(name = pa_proplist_get_stream_group(source_output->proplist, "source-output", IDENTIFICATION_PROPERTY)))
return PA_HOOK_OK;
if (source_output->preferred_source && (source = pa_namereg_get(c, source_output->preferred_source, PA_NAMEREG_SOURCE)) && source->card)
card_name = source->card->name;
update_preferred_device(u, name, source_output->preferred_source, card_name);
pa_xfree(name);
return PA_HOOK_OK;
}
static int fill_db(struct userdata *u, const char *filename) {
FILE *f;
int n = 0;
......@@ -2317,6 +2368,11 @@ int pa__init(pa_module*m) {
/* A little bit earlier than module-intended-roles ... */
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PREFERRED_SINK_CHANGED], PA_HOOK_NORMAL,
(pa_hook_cb_t) sink_input_preferred_sink_changed_cb, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PREFERRED_SOURCE_CHANGED], PA_HOOK_NORMAL,
(pa_hook_cb_t) source_output_preferred_source_changed_cb, u);
}
if (restore_volume || restore_muted) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment