mirror of
https://gitlab.gnome.org/GNOME/libgnome-volume-control.git
synced 2024-11-21 16:30:41 -05:00
mixer-control: remove objects properly when reconnecting to PA
GvcMixerCards are not removed when reconnecting to PA server, which causes duplicate card entries to appear on PA restart. Moreover, the old GvcMixerCard instances have pointers to the old already freed pa_context, resulting to use-after-free on operations. Duplicate entries are also caused by sink/source removal on reconnect not sending right signals. Make it clean up all Gvc objects as if we got subscribe remove events for them all: Use remove_sink/remove_source to remove sinks/sources so that the right signals are emitted. Remove cards using remove_card, so that also they get cleaned up. Remove also any leftover GvcMixerUIDevices. Move cleanup of streams etc. before pa_context unref, so that we free the objects referring the pa_context before freeing the context. This fixes gnome-control-center crashing when PA server is restarted, and one e.g. tries to do something that ends up in gvc_mixer_card_change_profile such as selecting output device with port in different profile. It also fixes duplicate entries appearing in the device lists on Pipewire restart (they don't appear with Pulseaudio since PA device IDs don't change on restart). It should also fix similar crashes in gnome-shell.
This commit is contained in:
parent
8e7a5a4c3e
commit
f3806ad5df
@ -3119,6 +3119,9 @@ remove_card (GvcMixerControl *control,
|
|||||||
|
|
||||||
g_object_get (G_OBJECT (device), "card", &card, NULL);
|
g_object_get (G_OBJECT (device), "card", &card, NULL);
|
||||||
|
|
||||||
|
if (card == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (gvc_mixer_card_get_index (card) == index) {
|
if (gvc_mixer_card_get_index (card) == index) {
|
||||||
g_signal_emit (G_OBJECT (control),
|
g_signal_emit (G_OBJECT (control),
|
||||||
signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
|
signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
|
||||||
@ -3434,27 +3437,45 @@ gvc_mixer_new_pa_context (GvcMixerControl *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
|
remove_all_items (GvcMixerControl *control,
|
||||||
|
GHashTable *hash_table,
|
||||||
|
void (*remove_item)(GvcMixerControl *control, guint index))
|
||||||
{
|
{
|
||||||
GHashTableIter iter;
|
GHashTableIter iter;
|
||||||
gpointer key, value;
|
gpointer key, value;
|
||||||
|
|
||||||
g_hash_table_iter_init (&iter, hash_table);
|
g_hash_table_iter_init (&iter, hash_table);
|
||||||
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
||||||
remove_stream (control, value);
|
if (remove_item) {
|
||||||
|
remove_item (control, GPOINTER_TO_UINT (key));
|
||||||
|
g_hash_table_remove (hash_table, key);
|
||||||
|
g_hash_table_iter_init (&iter, hash_table);
|
||||||
|
} else {
|
||||||
g_hash_table_iter_remove (&iter);
|
g_hash_table_iter_remove (&iter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
idle_reconnect (gpointer data)
|
idle_reconnect (gpointer data)
|
||||||
{
|
{
|
||||||
GvcMixerControl *control = GVC_MIXER_CONTROL (data);
|
GvcMixerControl *control = GVC_MIXER_CONTROL (data);
|
||||||
GHashTableIter iter;
|
|
||||||
gpointer key, value;
|
|
||||||
|
|
||||||
g_return_val_if_fail (control, FALSE);
|
g_return_val_if_fail (control, FALSE);
|
||||||
|
|
||||||
|
g_debug ("Reconnect: clean up all objects");
|
||||||
|
|
||||||
|
remove_all_items (control, control->priv->sinks, remove_sink);
|
||||||
|
remove_all_items (control, control->priv->sources, remove_source);
|
||||||
|
remove_all_items (control, control->priv->sink_inputs, remove_sink_input);
|
||||||
|
remove_all_items (control, control->priv->source_outputs, remove_source_output);
|
||||||
|
remove_all_items (control, control->priv->cards, remove_card);
|
||||||
|
remove_all_items (control, control->priv->ui_inputs, NULL);
|
||||||
|
remove_all_items (control, control->priv->ui_outputs, NULL);
|
||||||
|
remove_all_items (control, control->priv->clients, remove_client);
|
||||||
|
|
||||||
|
g_debug ("Reconnect: make new connection");
|
||||||
|
|
||||||
if (control->priv->pa_context) {
|
if (control->priv->pa_context) {
|
||||||
pa_context_unref (control->priv->pa_context);
|
pa_context_unref (control->priv->pa_context);
|
||||||
control->priv->pa_context = NULL;
|
control->priv->pa_context = NULL;
|
||||||
@ -3462,15 +3483,6 @@ idle_reconnect (gpointer data)
|
|||||||
gvc_mixer_new_pa_context (control);
|
gvc_mixer_new_pa_context (control);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_all_streams (control, control->priv->sinks);
|
|
||||||
remove_all_streams (control, control->priv->sources);
|
|
||||||
remove_all_streams (control, control->priv->sink_inputs);
|
|
||||||
remove_all_streams (control, control->priv->source_outputs);
|
|
||||||
|
|
||||||
g_hash_table_iter_init (&iter, control->priv->clients);
|
|
||||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
||||||
g_hash_table_iter_remove (&iter);
|
|
||||||
|
|
||||||
gvc_mixer_control_open (control); /* cannot fail */
|
gvc_mixer_control_open (control); /* cannot fail */
|
||||||
|
|
||||||
control->priv->reconnect_id = 0;
|
control->priv->reconnect_id = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user