diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c index 508913de1..3f368ab46 100644 --- a/src/modules/dbus/iface-core.c +++ b/src/modules/dbus/iface-core.c @@ -721,7 +721,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBu return; } - pa_namereg_set_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)); + pa_core_set_configured_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink)); pa_dbus_send_empty_reply(conn, msg); } @@ -809,7 +809,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, D return; } - pa_namereg_set_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)); + pa_core_set_configured_default_source(c->core, pa_dbusiface_device_get_source(fallback_source)); pa_dbus_send_empty_reply(conn, msg); } @@ -1692,6 +1692,28 @@ static pa_hook_result_t sample_cache_removed_cb(void *hook_data, void *call_data return PA_HOOK_OK; } +static pa_dbusiface_device *create_dbus_object_for_sink(pa_dbusiface_core *c, pa_sink *s) { + pa_dbusiface_device *d; + const char *object_path; + DBusMessage *signal_msg; + + d = pa_dbusiface_device_new_sink(c, s); + object_path = pa_dbusiface_device_get_path(d); + + pa_assert_se(pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0); + pa_assert_se(pa_hashmap_put(c->sinks_by_path, (char *) object_path, d) >= 0); + + pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SINK].name))); + pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg); + dbus_message_unref(signal_msg); + + return d; +} + static pa_hook_result_t default_sink_changed_cb(void *hook_data, void *call_data, void *slot_data) { pa_dbusiface_core *c = slot_data; pa_sink *new_fallback_sink = call_data; @@ -1707,7 +1729,15 @@ static pa_hook_result_t default_sink_changed_cb(void *hook_data, void *call_data c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL; if (c->fallback_sink) { - pa_assert_se((device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))); + device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)); + + /* It's possible that we haven't created a dbus object for the + * source yet, because if a new source immediately becomes the + * default source, the default source change hook is fired before + * the put hook. */ + if (!device_iface) + device_iface = create_dbus_object_for_sink(c, c->fallback_sink); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1730,6 +1760,28 @@ static pa_hook_result_t default_sink_changed_cb(void *hook_data, void *call_data return PA_HOOK_OK; } +static pa_dbusiface_device *create_dbus_object_for_source(pa_dbusiface_core *c, pa_source *s) { + pa_dbusiface_device *d; + const char *object_path; + DBusMessage *signal_msg; + + d = pa_dbusiface_device_new_source(c, s); + object_path = pa_dbusiface_device_get_path(d); + + pa_assert_se(pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0); + pa_assert_se(pa_hashmap_put(c->sources_by_path, (char *) object_path, d) >= 0); + + pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, + PA_DBUS_CORE_INTERFACE, + signals[SIGNAL_NEW_SOURCE].name))); + pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg); + dbus_message_unref(signal_msg); + + return d; +} + static pa_hook_result_t default_source_changed_cb(void *hook_data, void *call_data, void *slot_data) { pa_dbusiface_core *c = slot_data; pa_source *new_fallback_source = call_data; @@ -1745,7 +1797,15 @@ static pa_hook_result_t default_source_changed_cb(void *hook_data, void *call_da c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL; if (c->fallback_source) { - pa_assert_se((device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index)))); + device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index)); + + /* It's possible that we haven't created a dbus object for the + * source yet, because if a new source immediately becomes the + * default source, the default source change hook is fired before + * the put hook. */ + if (!device_iface) + device_iface = create_dbus_object_for_source(c, c->fallback_source); + object_path = pa_dbusiface_device_get_path(device_iface); pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, @@ -1983,26 +2043,17 @@ static pa_hook_result_t client_unlink_cb(void *hook_data, void *call_data, void static pa_hook_result_t sink_put_cb(void *hook_data, void *call_data, void *slot_data) { pa_dbusiface_core *c = slot_data; pa_sink *s = call_data; - pa_dbusiface_device *d = NULL; - const char *object_path = NULL; - DBusMessage *signal_msg = NULL; pa_assert(c); pa_assert(s); - d = pa_dbusiface_device_new_sink(c, s); - object_path = pa_dbusiface_device_get_path(d); + /* We may have alredy encountered this sink, because if the new sink was + * chosen as the default sink, the default sink change hook was fired + * first, and we saw the sink in default_sink_changed_cb(). */ + if (pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(s->index))) + return PA_HOOK_OK; - pa_assert_se(pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0); - pa_assert_se(pa_hashmap_put(c->sinks_by_path, (char *) object_path, d) >= 0); - - pa_assert_se(signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, - PA_DBUS_CORE_INTERFACE, - signals[SIGNAL_NEW_SINK].name)); - pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - - pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg); - dbus_message_unref(signal_msg); + create_dbus_object_for_sink(c, s); return PA_HOOK_OK; } @@ -2037,26 +2088,17 @@ static pa_hook_result_t sink_unlink_cb(void *hook_data, void *call_data, void *s static pa_hook_result_t source_put_cb(void *hook_data, void *call_data, void *slot_data) { pa_dbusiface_core *c = slot_data; pa_source *s = call_data; - pa_dbusiface_device *d = NULL; - const char *object_path = NULL; - DBusMessage *signal_msg = NULL; pa_assert(c); pa_assert(s); - d = pa_dbusiface_device_new_source(c, s); - object_path = pa_dbusiface_device_get_path(d); + /* We may have alredy encountered this source, because if the new source + * was chosen as the default source, the default source change hook was + * fired first, and we saw the source in default_source_changed_cb(). */ + if (pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(s->index))) + return PA_HOOK_OK; - pa_assert_se(pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(s->index), d) >= 0); - pa_assert_se(pa_hashmap_put(c->sources_by_path, (char *) object_path, d) >= 0); - - pa_assert_se((signal_msg = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, - PA_DBUS_CORE_INTERFACE, - signals[SIGNAL_NEW_SOURCE].name))); - pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)); - - pa_dbus_protocol_send_signal(c->dbus_protocol, signal_msg); - dbus_message_unref(signal_msg); + create_dbus_object_for_source(c, s); return PA_HOOK_OK; } @@ -2158,8 +2200,8 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) { c->samples = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_sample_free); c->modules = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_module_free); c->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) pa_dbusiface_client_free); - c->fallback_sink = pa_namereg_get_default_sink(core); - c->fallback_source = pa_namereg_get_default_source(core); + c->fallback_sink = core->default_sink; + c->fallback_source = core->default_source; c->default_sink_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], PA_HOOK_NORMAL, default_sink_changed_cb, c); c->default_source_changed_slot = pa_hook_connect(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c index 61f2ba091..51189196d 100644 --- a/src/modules/dbus/iface-sample.c +++ b/src/modules/dbus/iface-sample.c @@ -352,7 +352,6 @@ static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) DBusMessageIter msg_iter; dbus_uint32_t volume = 0; pa_proplist *property_list = NULL; - pa_sink *sink = NULL; pa_assert(conn); pa_assert(msg); @@ -370,13 +369,18 @@ static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) goto finish; } - if (!(sink = pa_namereg_get_default_sink(s->sample->core))) { + if (!s->sample->core->default_sink) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Can't play sample %s, because there are no sinks available.", s->sample->name); goto finish; } - if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) { + if (pa_scache_play_item(s->sample->core, + s->sample->name, + s->sample->core->default_sink, + volume, + property_list, + NULL) < 0) { pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name); goto finish; } diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c index d76e28e68..56fee67f8 100644 --- a/src/modules/module-default-device-restore.c +++ b/src/modules/module-default-device-restore.c @@ -56,7 +56,7 @@ static void load(struct userdata *u) { /* We never overwrite manually configured settings */ - if (u->core->default_sink) + if (u->core->configured_default_sink) pa_log_info("Manually configured default sink, not overwriting."); else if ((f = pa_fopen_cloexec(u->sink_filename, "r"))) { char ln[256] = ""; @@ -69,7 +69,7 @@ static void load(struct userdata *u) { if (!ln[0]) pa_log_info("No previous default sink setting, ignoring."); else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SINK))) { - pa_namereg_set_default_sink(u->core, s); + pa_core_set_configured_default_sink(u->core, s); pa_log_info("Restored default sink '%s'.", ln); } else pa_log_info("Saved default sink '%s' not existent, not restoring default sink setting.", ln); @@ -77,7 +77,7 @@ static void load(struct userdata *u) { } else if (errno != ENOENT) pa_log("Failed to load default sink: %s", pa_cstrerror(errno)); - if (u->core->default_source) + if (u->core->configured_default_source) pa_log_info("Manually configured default source, not overwriting."); else if ((f = pa_fopen_cloexec(u->source_filename, "r"))) { char ln[256] = ""; @@ -90,7 +90,7 @@ static void load(struct userdata *u) { if (!ln[0]) pa_log_info("No previous default source setting, ignoring."); else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE))) { - pa_namereg_set_default_source(u->core, s); + pa_core_set_configured_default_source(u->core, s); pa_log_info("Restored default source '%s'.", ln); } else pa_log_info("Saved default source '%s' not existent, not restoring default source setting.", ln); @@ -107,8 +107,7 @@ static void save(struct userdata *u) { if (u->sink_filename) { if ((f = pa_fopen_cloexec(u->sink_filename, "w"))) { - pa_sink *s = pa_namereg_get_default_sink(u->core); - fprintf(f, "%s\n", s ? s->name : ""); + fprintf(f, "%s\n", u->core->default_sink ? u->core->default_sink->name : ""); fclose(f); } else pa_log("Failed to save default sink: %s", pa_cstrerror(errno)); @@ -116,8 +115,7 @@ static void save(struct userdata *u) { if (u->source_filename) { if ((f = pa_fopen_cloexec(u->source_filename, "w"))) { - pa_source *s = pa_namereg_get_default_source(u->core); - fprintf(f, "%s\n", s ? s->name : ""); + fprintf(f, "%s\n", u->core->default_source ? u->core->default_source->name : ""); fclose(f); } else pa_log("Failed to save default source: %s", pa_cstrerror(errno)); diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c index 60ec7e982..f906b8800 100644 --- a/src/modules/module-intended-roles.c +++ b/src/modules/module-intended-roles.c @@ -69,7 +69,7 @@ static bool role_match(pa_proplist *proplist, const char *role) { static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) { const char *role; - pa_sink *s, *def; + pa_sink *s; uint32_t idx; pa_assert(c); @@ -92,13 +92,13 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n } /* Prefer the default sink over any other sink, just in case... */ - if ((def = pa_namereg_get_default_sink(c))) - if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, false)) + if (c->default_sink) + if (role_match(c->default_sink->proplist, role) && pa_sink_input_new_data_set_sink(new_data, c->default_sink, false)) return PA_HOOK_OK; /* @todo: favour the highest priority device, not the first one we find? */ PA_IDXSET_FOREACH(s, c->sinks, idx) { - if (s == def) + if (s == c->default_sink) continue; if (!PA_SINK_IS_LINKED(pa_sink_get_state(s))) @@ -113,7 +113,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) { const char *role; - pa_source *s, *def; + pa_source *s; uint32_t idx; pa_assert(c); @@ -136,9 +136,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou } /* Prefer the default source over any other source, just in case... */ - if ((def = pa_namereg_get_default_source(c))) - if (role_match(def->proplist, role)) { - pa_source_output_new_data_set_source(new_data, def, false); + if (c->default_source) + if (role_match(c->default_source->proplist, role)) { + pa_source_output_new_data_set_source(new_data, c->default_source, false); return PA_HOOK_OK; } @@ -146,7 +146,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou if (s->monitor_of) continue; - if (s == def) + if (s == c->default_source) continue; if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s))) @@ -259,7 +259,6 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) { pa_sink_input *si; uint32_t idx; - pa_sink *def; pa_assert(c); pa_assert(sink); @@ -271,7 +270,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str return PA_HOOK_OK; /* If there not default sink, then there is no sink at all */ - if (!(def = pa_namereg_get_default_sink(c))) + if (!c->default_sink) return PA_HOOK_OK; PA_IDXSET_FOREACH(si, sink->inputs, idx) { @@ -286,14 +285,14 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str continue; /* Would the default sink fit? If so, let's use it */ - if (def != sink && role_match(def->proplist, role)) - if (pa_sink_input_move_to(si, def, false) >= 0) + if (c->default_sink != sink && role_match(c->default_sink->proplist, role)) + if (pa_sink_input_move_to(si, c->default_sink, false) >= 0) continue; /* Try to find some other fitting sink */ /* @todo: favour the highest priority device, not the first one we find? */ PA_IDXSET_FOREACH(d, c->sinks, jdx) { - if (d == def || d == sink) + if (d == c->default_sink || d == sink) continue; if (!PA_SINK_IS_LINKED(pa_sink_get_state(d))) @@ -311,7 +310,6 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) { pa_source_output *so; uint32_t idx; - pa_source *def; pa_assert(c); pa_assert(source); @@ -323,7 +321,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc return PA_HOOK_OK; /* If there not default source, then there is no source at all */ - if (!(def = pa_namereg_get_default_source(c))) + if (!c->default_source) return PA_HOOK_OK; PA_IDXSET_FOREACH(so, source->outputs, idx) { @@ -341,15 +339,16 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc continue; /* Would the default source fit? If so, let's use it */ - if (def != source && role_match(def->proplist, role) && !source->monitor_of == !def->monitor_of) { - pa_source_output_move_to(so, def, false); + if (c->default_source != source && role_match(c->default_source->proplist, role) + && !source->monitor_of == !c->default_source->monitor_of) { + pa_source_output_move_to(so, c->default_source, false); continue; } /* Try to find some other fitting source */ /* @todo: favour the highest priority device, not the first one we find? */ PA_IDXSET_FOREACH(d, c->sources, jdx) { - if (d == def || d == source) + if (d == c->default_source || d == source) continue; if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d))) diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index 60ac1c4cf..d3c9953ec 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -96,7 +96,7 @@ static void build_group_ports(pa_hashmap *g_ports, pa_hashmap *s_ports) { } static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) { - pa_sink *target, *def, *fb_sink = NULL; + pa_sink *target, *fb_sink = NULL; uint32_t idx; pa_hashmap *all_ports; pa_device_port *best_port; @@ -104,15 +104,13 @@ static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip pa_assert(c); pa_assert(i); - def = pa_namereg_get_default_sink(c); - - if (def && def != skip && pa_sink_input_may_move_to(i, def)) - return def; + if (c->default_sink && c->default_sink != skip && pa_sink_input_may_move_to(i, c->default_sink)) + return c->default_sink; all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); PA_IDXSET_FOREACH(target, c->sinks, idx) { - if (target == def) + if (target == c->default_sink) continue; if (target == skip) @@ -204,7 +202,7 @@ static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_i } static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) { - pa_source *target, *def, *fb_source = NULL; + pa_source *target, *fb_source = NULL; uint32_t idx; pa_hashmap *all_ports; pa_device_port *best_port; @@ -212,15 +210,13 @@ static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_sou pa_assert(c); pa_assert(o); - def = pa_namereg_get_default_source(c); - - if (def && def != skip && pa_source_output_may_move_to(o, def)) - return def; + if (c->default_source && c->default_source != skip && pa_source_output_may_move_to(o, c->default_source)) + return c->default_source; all_ports = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); PA_IDXSET_FOREACH(target, c->sources, idx) { - if (target == def) + if (target == c->default_source) continue; if (target == skip) diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c index c844add6e..776c923ee 100644 --- a/src/modules/module-switch-on-connect.c +++ b/src/modules/module-switch-on-connect.c @@ -55,7 +55,7 @@ struct userdata { static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { pa_sink_input *i; uint32_t idx; - pa_sink *def; + pa_sink *old_default_sink; const char *s; struct userdata *u = userdata; @@ -75,24 +75,25 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* return PA_HOOK_OK; } - def = pa_namereg_get_default_sink(c); - if (def == sink) + if (c->default_sink == sink) return PA_HOOK_OK; if (u->only_from_unavailable) - if (!def->active_port || def->active_port->available != PA_AVAILABLE_NO) + if (!c->default_sink->active_port || c->default_sink->active_port->available != PA_AVAILABLE_NO) return PA_HOOK_OK; + old_default_sink = c->default_sink; + /* Actually do the switch to the new sink */ - pa_namereg_set_default_sink(c, sink); + pa_core_set_configured_default_sink(c, sink); /* Now move all old inputs over */ - if (pa_idxset_size(def->inputs) <= 0) { + if (pa_idxset_size(old_default_sink->inputs) <= 0) { pa_log_debug("No sink inputs to move away."); return PA_HOOK_OK; } - PA_IDXSET_FOREACH(i, def->inputs, idx) { + PA_IDXSET_FOREACH(i, old_default_sink->inputs, idx) { if (i->save_sink || !PA_SINK_INPUT_IS_LINKED(i->state)) continue; @@ -110,7 +111,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void* userdata) { pa_source_output *o; uint32_t idx; - pa_source *def; + pa_source *old_default_source; const char *s; struct userdata *u = userdata; @@ -134,24 +135,25 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, return PA_HOOK_OK; } - def = pa_namereg_get_default_source(c); - if (def == source) + if (c->default_source == source) return PA_HOOK_OK; if (u->only_from_unavailable) - if (!def->active_port || def->active_port->available != PA_AVAILABLE_NO) + if (!c->default_source->active_port || c->default_source->active_port->available != PA_AVAILABLE_NO) return PA_HOOK_OK; + old_default_source = c->default_source; + /* Actually do the switch to the new source */ - pa_namereg_set_default_source(c, source); + pa_core_set_configured_default_source(c, source); /* Now move all old outputs over */ - if (pa_idxset_size(def->outputs) <= 0) { + if (pa_idxset_size(old_default_source->outputs) <= 0) { pa_log_debug("No source outputs to move away."); return PA_HOOK_OK; } - PA_IDXSET_FOREACH(o, def->outputs, idx) { + PA_IDXSET_FOREACH(o, old_default_source->outputs, idx) { if (o->save_source || !PA_SOURCE_OUTPUT_IS_LINKED(o->state)) continue; diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 9a7360582..5a632be45 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -344,8 +344,6 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool char bytes[PA_BYTES_SNPRINT_MAX]; const pa_mempool_stat *mstat; unsigned k; - pa_sink *def_sink; - pa_source *def_source; static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = { [PA_MEMBLOCK_POOL] = "POOL", @@ -388,12 +386,10 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool pa_strbuf_printf(buf, "Default channel map: %s\n", pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map)); - def_sink = pa_namereg_get_default_sink(c); - def_source = pa_namereg_get_default_source(c); pa_strbuf_printf(buf, "Default sink name: %s\n" "Default source name: %s\n", - def_sink ? def_sink->name : "none", - def_source ? def_source->name : "none"); + c->default_sink ? c->default_sink->name : "none", + c->default_source ? c->default_source->name : "none"); for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++) pa_strbuf_printf(buf, @@ -1034,7 +1030,7 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b } if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK))) - pa_namereg_set_default_sink(c, s); + pa_core_set_configured_default_sink(c, s); else pa_strbuf_printf(buf, "Sink %s does not exist.\n", n); @@ -1056,7 +1052,7 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf } if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) - pa_namereg_set_default_source(c, s); + pa_core_set_configured_default_source(c, s); else pa_strbuf_printf(buf, "Source %s does not exist.\n", n); return 0; @@ -1850,20 +1846,20 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool } nl = false; - if ((sink = pa_namereg_get_default_sink(c))) { + if (c->default_sink) { if (!nl) { pa_strbuf_puts(buf, "\n"); nl = true; } - pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name); + pa_strbuf_printf(buf, "set-default-sink %s\n", c->default_sink->name); } - if ((source = pa_namereg_get_default_source(c))) { + if (c->default_source) { if (!nl) pa_strbuf_puts(buf, "\n"); - pa_strbuf_printf(buf, "set-default-source %s\n", source->name); + pa_strbuf_printf(buf, "set-default-source %s\n", c->default_source->name); } pa_strbuf_puts(buf, "\n### EOF\n"); diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index af79a1ebe..ded82f6e4 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -232,7 +232,7 @@ static const char *source_state_to_string(pa_source_state_t state) { char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; - pa_sink *sink, *default_sink; + pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; pa_assert(c); @@ -240,8 +240,6 @@ char *pa_sink_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); - default_sink = pa_namereg_get_default_sink(c); - PA_IDXSET_FOREACH(sink, c->sinks, idx) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], @@ -273,7 +271,7 @@ char *pa_sink_list_to_string(pa_core *c) { "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", - sink == default_sink ? '*' : ' ', + sink == c->default_sink ? '*' : ' ', sink->index, sink->name, sink->driver, @@ -350,7 +348,7 @@ char *pa_sink_list_to_string(pa_core *c) { char *pa_source_list_to_string(pa_core *c) { pa_strbuf *s; - pa_source *source, *default_source; + pa_source *source; uint32_t idx = PA_IDXSET_INVALID; pa_assert(c); @@ -358,8 +356,6 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); - default_source = pa_namereg_get_default_source(c); - PA_IDXSET_FOREACH(source, c->sources, idx) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], @@ -389,7 +385,7 @@ char *pa_source_list_to_string(pa_core *c) { "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", - source == default_source ? '*' : ' ', + source == c->default_source ? '*' : ' ', source->index, source->name, source->driver, diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index afdb0a4ca..16fd040a4 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -224,6 +224,176 @@ static void core_free(pa_object *o) { pa_xfree(c); } +void pa_core_set_configured_default_sink(pa_core *core, pa_sink *sink) { + pa_sink *old_sink; + + pa_assert(core); + + old_sink = core->configured_default_sink; + + if (sink == old_sink) + return; + + core->configured_default_sink = sink; + pa_log_info("configured_default_sink: %s -> %s", + old_sink ? old_sink->name : "(unset)", sink ? sink->name : "(unset)"); + + pa_core_update_default_sink(core); +} + +void pa_core_set_configured_default_source(pa_core *core, pa_source *source) { + pa_source *old_source; + + pa_assert(core); + + old_source = core->configured_default_source; + + if (source == old_source) + return; + + core->configured_default_source = source; + pa_log_info("configured_default_source: %s -> %s", + old_source ? old_source->name : "(unset)", source ? source->name : "(unset)"); + + pa_core_update_default_source(core); +} + +/* a < b -> return -1 + * a == b -> return 0 + * a > b -> return 1 */ +static int compare_sinks(pa_sink *a, pa_sink *b) { + pa_core *core; + + core = a->core; + + /* The configured default sink is preferred over any other sink. */ + if (b == core->configured_default_sink) + return -1; + if (a == core->configured_default_sink) + return 1; + + if (a->priority < b->priority) + return -1; + if (a->priority > b->priority) + return 1; + + /* It's hard to find any difference between these sinks, but maybe one of + * them is already the default sink? If so, it's best to keep it as the + * default to avoid changing the routing for no good reason. */ + if (b == core->default_sink) + return -1; + if (a == core->default_sink) + return 1; + + return 0; +} + +void pa_core_update_default_sink(pa_core *core) { + pa_sink *best = NULL; + pa_sink *sink; + uint32_t idx; + pa_sink *old_default_sink; + + pa_assert(core); + + PA_IDXSET_FOREACH(sink, core->sinks, idx) { + if (!best) { + best = sink; + continue; + } + + if (compare_sinks(sink, best) > 0) + best = sink; + } + + old_default_sink = core->default_sink; + + if (best == old_default_sink) + return; + + core->default_sink = best; + pa_log_info("default_sink: %s -> %s", + old_default_sink ? old_default_sink->name : "(unset)", best ? best->name : "(unset)"); + + /* If the default sink changed, it may be that the default source has to be + * changed too, because monitor sources are prioritized partly based on the + * priorities of the monitored sinks. */ + pa_core_update_default_source(core); + + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink); +} + +/* a < b -> return -1 + * a == b -> return 0 + * a > b -> return 1 */ +static int compare_sources(pa_source *a, pa_source *b) { + pa_core *core; + + core = a->core; + + /* The configured default source is preferred over any other source. */ + if (b == core->configured_default_source) + return -1; + if (a == core->configured_default_source) + return 1; + + /* Monitor sources lose to non-monitor sources. */ + if (a->monitor_of && !b->monitor_of) + return -1; + if (!a->monitor_of && b->monitor_of) + return 1; + + if (a->priority < b->priority) + return -1; + if (a->priority > b->priority) + return 1; + + /* If the sources are monitors, we can compare the monitored sinks. */ + if (a->monitor_of) + return compare_sinks(a->monitor_of, b->monitor_of); + + /* It's hard to find any difference between these sources, but maybe one of + * them is already the default source? If so, it's best to keep it as the + * default to avoid changing the routing for no good reason. */ + if (b == core->default_source) + return -1; + if (a == core->default_source) + return 1; + + return 0; +} + +void pa_core_update_default_source(pa_core *core) { + pa_source *best = NULL; + pa_source *source; + uint32_t idx; + pa_source *old_default_source; + + pa_assert(core); + + PA_IDXSET_FOREACH(source, core->sources, idx) { + if (!best) { + best = source; + continue; + } + + if (compare_sources(source, best) > 0) + best = source; + } + + old_default_source = core->default_source; + + if (best == old_default_source) + return; + + core->default_source = best; + pa_log_info("default_source: %s -> %s", + old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)"); + pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source); +} + static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) { pa_core *c = userdata; pa_assert(c->exit_event == e); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index df71a118d..8af14b047 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -162,9 +162,18 @@ struct pa_core { /* Some hashmaps for all sorts of entities */ pa_hashmap *namereg, *shared; - /* The default sink/source */ - pa_source *default_source; + /* The default sink/source as configured by the user. If the user hasn't + * explicitly configured anything, these are set to NULL. */ + pa_sink *configured_default_sink; + pa_source *configured_default_source; + + /* The effective default sink/source. If no sink or source is explicitly + * configured as the default, we pick the device that ranks highest + * according to the compare_sinks() and compare_sources() functions in + * core.c. pa_core_update_default_sink/source() has to be called whenever + * anything changes that might change the comparison results. */ pa_sink *default_sink; + pa_source *default_source; pa_channel_map default_channel_map; pa_sample_spec default_sample_spec; @@ -227,6 +236,21 @@ enum { pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size); +void pa_core_set_configured_default_sink(pa_core *core, pa_sink *sink); +void pa_core_set_configured_default_source(pa_core *core, pa_source *source); + +/* These should be called whenever something changes that may affect the + * default sink or source choice. + * + * If the default source choice happens between two monitor sources, the + * monitored sinks are compared, so if the default sink changes, the default + * source may change too. However, pa_core_update_default_sink() calls + * pa_core_update_default_source() internally, so it's sufficient to only call + * pa_core_update_default_sink() when something happens that affects the sink + * ordering. */ +void pa_core_update_default_sink(pa_core *core); +void pa_core_update_default_source(pa_core *core); + /* Check whether no one is connected to this core */ void pa_core_check_idle(pa_core *c); diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index 47bfc087f..0b7388592 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -168,17 +168,6 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0); - /* If a sink or source is registered and there was none registered - * before we inform the clients which then can ask for the default - * sink/source which is then assigned. We don't adjust the default - * sink/source here right away to give the module the chance to - * register more sinks/sources before we choose a new default - * sink/source. */ - - if ((!c->default_sink && type == PA_NAMEREG_SINK) || - (!c->default_source && type == PA_NAMEREG_SOURCE)) - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); - return e->name; } @@ -189,12 +178,6 @@ void pa_namereg_unregister(pa_core *c, const char *name) { pa_assert(name); pa_assert_se(e = pa_hashmap_remove(c->namereg, name)); - - if (c->default_sink == e->data) - pa_namereg_set_default_sink(c, NULL); - else if (c->default_source == e->data) - pa_namereg_set_default_source(c, NULL); - pa_xfree(e->name); pa_xfree(e); } @@ -205,22 +188,16 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { pa_assert(c); if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) { - pa_source *s; - - if ((s = pa_namereg_get_default_source(c))) - return s; + return c->default_source; } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) { - pa_sink *s; - - if ((s = pa_namereg_get_default_sink(c))) - return s; + return c->default_sink; } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) { - pa_sink *s; - - if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK))) - return s->monitor_source; + if (c->default_sink) + return c->default_sink->monitor_source; + else + return NULL; } if (!name) @@ -248,83 +225,3 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { return NULL; } - -pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) { - pa_assert(c); - - if (s && !PA_SINK_IS_LINKED(pa_sink_get_state(s))) - return NULL; - - if (c->default_sink != s) { - c->default_sink = s; - pa_hook_fire(&c->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], c->default_sink); - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); - } - - return s; -} - -pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { - pa_assert(c); - - if (s && !PA_SOURCE_IS_LINKED(pa_source_get_state(s))) - return NULL; - - if (c->default_source != s) { - c->default_source = s; - pa_hook_fire(&c->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], c->default_source); - pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); - } - - return s; -} - -pa_sink *pa_namereg_get_default_sink(pa_core *c) { - pa_sink *s, *best = NULL; - uint32_t idx; - - pa_assert(c); - - if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) - return c->default_sink; - - PA_IDXSET_FOREACH(s, c->sinks, idx) - if (PA_SINK_IS_LINKED(pa_sink_get_state(s))) - if (!best || s->priority > best->priority) - best = s; - - return best; -} - -pa_source *pa_namereg_get_default_source(pa_core *c) { - pa_source *s, *best = NULL; - uint32_t idx; - - pa_assert(c); - - if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) - return c->default_source; - - /* First, try to find one that isn't a monitor */ - PA_IDXSET_FOREACH(s, c->sources, idx) - if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) - if (!best || - s->priority > best->priority) - best = s; - - if (best) - return best; - - /* Then, fallback to a monitor */ - PA_IDXSET_FOREACH(s, c->sources, idx) - if (PA_SOURCE_IS_LINKED(pa_source_get_state(s))) - if (!best || - s->priority > best->priority || - (s->priority == best->priority && - s->monitor_of && - best->monitor_of && - s->monitor_of->priority > best->monitor_of->priority)) - best = s; - - return best; -} diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index 467db5d43..eb3475ad3 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -36,12 +36,6 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t void pa_namereg_unregister(pa_core *c, const char *name); void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type); -pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s); -pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s); - -pa_sink *pa_namereg_get_default_sink(pa_core *c); -pa_source *pa_namereg_get_default_source(pa_core *c); - bool pa_namereg_is_valid_name(const char *name); bool pa_namereg_is_valid_name_or_wildcard(const char *name, pa_namereg_type_t type); char* pa_namereg_make_valid_name(const char *name); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ce99f8733..266b676de 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -3637,10 +3637,9 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); pa_tagstruct *reply; - pa_sink *def_sink; - pa_source *def_source; pa_sample_spec fixed_ss; char *h, *u; + pa_core *core; pa_native_connection_assert_ref(c); pa_assert(t); @@ -3664,18 +3663,18 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t pa_tagstruct_puts(reply, h); pa_xfree(h); - fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec); + core = c->protocol->core; + + fixup_sample_spec(c, &fixed_ss, &core->default_sample_spec); pa_tagstruct_put_sample_spec(reply, &fixed_ss); - def_sink = pa_namereg_get_default_sink(c->protocol->core); - pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL); - def_source = pa_namereg_get_default_source(c->protocol->core); - pa_tagstruct_puts(reply, def_source ? def_source->name : NULL); + pa_tagstruct_puts(reply, core->default_sink ? core->default_sink->name : NULL); + pa_tagstruct_puts(reply, core->default_source ? core->default_source->name : NULL); pa_tagstruct_putu32(reply, c->protocol->core->cookie); if (c->version >= 15) - pa_tagstruct_put_channel_map(reply, &c->protocol->core->default_channel_map); + pa_tagstruct_put_channel_map(reply, &core->default_channel_map); pa_pstream_send_tagstruct(c->pstream, reply); } @@ -4347,7 +4346,7 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE); CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); - pa_namereg_set_default_source(c->protocol->core, source); + pa_core_set_configured_default_source(c->protocol->core, source); } else { pa_sink *sink; pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK); @@ -4355,7 +4354,7 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK); CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); - pa_namereg_set_default_sink(c->protocol->core, sink); + pa_core_set_configured_default_sink(c->protocol->core, sink); } pa_pstream_send_simple_ack(c->pstream, tag); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index dbbec5352..71121ca0b 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -660,6 +660,8 @@ void pa_sink_put(pa_sink* s) { pa_source_put(s->monitor_source); + pa_core_update_default_sink(s->core); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s); } @@ -690,6 +692,11 @@ void pa_sink_unlink(pa_sink* s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sinks, s, NULL); + if (s == s->core->configured_default_sink) + pa_core_set_configured_default_sink(s->core, NULL); + else + pa_core_update_default_sink(s->core); + if (s->card) pa_idxset_remove_by_data(s->card->sinks, s, NULL); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 08e7254d3..15b1eecda 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -603,6 +603,8 @@ void pa_source_put(pa_source *s) { else pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); + pa_core_update_default_source(s->core); + pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); } @@ -632,6 +634,11 @@ void pa_source_unlink(pa_source *s) { pa_namereg_unregister(s->core, s->name); pa_idxset_remove_by_data(s->core->sources, s, NULL); + if (s == s->core->configured_default_source) + pa_core_set_configured_default_source(s->core, NULL); + else + pa_core_update_default_source(s->core); + if (s->card) pa_idxset_remove_by_data(s->card->sources, s, NULL);