gnome-control-center/panels/sound/cc-sound-panel.c
Jonas Dreßler 4afd08e234 sound: Don't call gvc_mixer_control_open() twice
Unfortunately I sneaked in a move of the gvc_mixer_control_open() call from
earlier in the function to the end of the function with commit 2152899354.

That change didn't actually move the call though, but introduced a second
call instead, leading to a critical error on opening of the sound panel.

Fix it by removing the first call to gvc_mixer_control_open() and keeping
the second one around. We want to keep the second one instead of the earlier
one because the signal handlers should be connected before we initialize gvc
and it connects to pulse, emitting all the input/output-added signals etc.

The reason why this wasn't broken before was likely because the connection
to pulse happens async, and therefore the initial emission of the signals
would effectively still have happened after we connected our handlers.

Fwiw, calling gvc_mixer_control_open() after
cc_volume_slider_set_mixer_control(), cc_subwoofer_slider_set_mixer_control()
and cc_device_combo_box_set_mixer_control() is not a problem, as those
functions all call into gvc to get static values (and as explained above,
at this point gvc wouldn't have established connection to pulse anyway).

Reported-by: Matthijs Velsink <mvelsink@gnome.org>

Fixes: 2152899354 ("sound: Listen to signals from GvcMixerControl only in sound panel")
2024-03-12 22:39:14 +00:00

390 lines
14 KiB
C

/*
* Copyright (C) 2008 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <libintl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <glib/gi18n-lib.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <pulse/pulseaudio.h>
#include <gvc-mixer-control.h>
#include "cc-list-row.h"
#include "cc-alert-chooser-window.h"
#include "cc-balance-slider.h"
#include "cc-device-combo-box.h"
#include "cc-fade-slider.h"
#include "cc-level-bar.h"
#include "cc-output-test-window.h"
#include "cc-profile-combo-box.h"
#include "cc-sound-panel.h"
#include "cc-sound-resources.h"
#include "cc-subwoofer-slider.h"
#include "cc-volume-levels-window.h"
#include "cc-volume-slider.h"
struct _CcSoundPanel
{
CcPanel parent_instance;
AdwPreferencesGroup *output_group;
CcLevelBar *output_level_bar;
CcDeviceComboBox *output_device_combo_box;
AdwPreferencesRow *output_profile_row;
CcProfileComboBox *output_profile_combo_box;
CcVolumeSlider *output_volume_slider;
CcBalanceSlider *balance_slider;
AdwPreferencesRow *fade_row;
CcFadeSlider *fade_slider;
AdwPreferencesRow *subwoofer_row;
CcSubwooferSlider *subwoofer_slider;
AdwPreferencesGroup *output_no_devices_group;
AdwPreferencesGroup *input_group;
CcLevelBar *input_level_bar;
CcDeviceComboBox *input_device_combo_box;
AdwPreferencesRow *input_profile_row;
CcProfileComboBox *input_profile_combo_box;
CcVolumeSlider *input_volume_slider;
AdwPreferencesGroup *input_no_devices_group;
CcListRow *alert_sound_row;
GvcMixerControl *mixer_control;
GSettings *sound_settings;
};
CC_PANEL_REGISTER (CcSoundPanel, cc_sound_panel)
enum
{
PROP_0,
PROP_PARAMETERS
};
#define KEY_SOUNDS_SCHEMA "org.gnome.desktop.sound"
static void
update_alert_sound_label (CcSoundPanel *self)
{
const gchar *alert_name = get_selected_alert_display_name ();
cc_list_row_set_secondary_label (self->alert_sound_row, alert_name);
}
static void
allow_amplified_changed_cb (CcSoundPanel *self)
{
cc_volume_slider_set_is_amplified (self->output_volume_slider,
g_settings_get_boolean (self->sound_settings, "allow-volume-above-100-percent"));
}
static void
set_output_stream (CcSoundPanel *self,
GvcMixerStream *stream)
{
GvcChannelMap *map = NULL;
gboolean can_fade = FALSE, has_lfe = FALSE;
cc_volume_slider_set_stream (self->output_volume_slider, stream, CC_STREAM_TYPE_OUTPUT);
cc_level_bar_set_stream (self->output_level_bar, stream);
if (stream != NULL)
{
map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
can_fade = gvc_channel_map_can_fade (map);
has_lfe = gvc_channel_map_has_lfe (map);
}
cc_fade_slider_set_channel_map (self->fade_slider, map);
cc_balance_slider_set_channel_map (self->balance_slider, map);
cc_subwoofer_slider_set_channel_map (self->subwoofer_slider, map);
gtk_widget_set_visible (GTK_WIDGET (self->fade_row), can_fade);
gtk_widget_set_visible (GTK_WIDGET (self->subwoofer_row), has_lfe);
}
static void
output_device_changed_cb (CcSoundPanel *self)
{
GvcMixerUIDevice *device;
device = cc_device_combo_box_get_device (self->output_device_combo_box);
if (device != NULL)
gvc_mixer_control_change_output (self->mixer_control, device);
}
static void
set_input_stream (CcSoundPanel *self,
GvcMixerStream *stream)
{
cc_volume_slider_set_stream (self->input_volume_slider, stream, CC_STREAM_TYPE_INPUT);
cc_level_bar_set_stream (self->input_level_bar, stream);
}
static void
input_device_changed_cb (CcSoundPanel *self)
{
GvcMixerUIDevice *device;
device = cc_device_combo_box_get_device (self->input_device_combo_box);
if (device != NULL)
gvc_mixer_control_change_input (self->mixer_control, device);
}
static void
output_device_update_cb (CcSoundPanel *self,
guint id)
{
GvcMixerUIDevice *device;
gboolean has_multi_profiles;
GvcMixerStream *stream = NULL;
g_signal_handlers_block_by_func(self->output_device_combo_box, output_device_changed_cb, self);
cc_device_combo_box_active_device_changed (self->output_device_combo_box, id);
g_signal_handlers_unblock_by_func(self->output_device_combo_box, output_device_changed_cb, self);
device = cc_device_combo_box_get_device (self->output_device_combo_box);
gtk_widget_set_visible (GTK_WIDGET (self->output_group), device != NULL);
gtk_widget_set_visible (GTK_WIDGET (self->output_no_devices_group), device == NULL);
cc_profile_combo_box_set_device (self->output_profile_combo_box, self->mixer_control, device);
has_multi_profiles = (cc_profile_combo_box_get_profile_count (self->output_profile_combo_box) > 1);
gtk_widget_set_visible (GTK_WIDGET (self->output_profile_row),
has_multi_profiles);
if (device)
stream = gvc_mixer_control_get_stream_from_device (self->mixer_control, device);
set_output_stream (self, stream);
}
static void
input_device_update_cb (CcSoundPanel *self,
guint id)
{
GvcMixerUIDevice *device;
gboolean has_multi_profiles;
GvcMixerStream *stream = NULL;
g_signal_handlers_block_by_func(self->input_device_combo_box, input_device_changed_cb, self);
cc_device_combo_box_active_device_changed (self->input_device_combo_box, id);
g_signal_handlers_unblock_by_func(self->input_device_combo_box, input_device_changed_cb, self);
device = cc_device_combo_box_get_device (self->input_device_combo_box);
gtk_widget_set_visible (GTK_WIDGET (self->input_group), device != NULL);
gtk_widget_set_visible (GTK_WIDGET (self->input_no_devices_group), device != NULL);
cc_profile_combo_box_set_device (self->input_profile_combo_box, self->mixer_control, device);
has_multi_profiles = (cc_profile_combo_box_get_profile_count (self->input_profile_combo_box) > 1);
gtk_widget_set_visible (GTK_WIDGET (self->input_profile_row),
has_multi_profiles);
if (device)
stream = gvc_mixer_control_get_stream_from_device (self->mixer_control, device);
set_input_stream (self, stream);
}
static void
test_output_configuration_button_clicked_cb (CcSoundPanel *self)
{
GvcMixerUIDevice *device;
GvcMixerStream *stream = NULL;
CcOutputTestWindow *window;
GtkWidget *toplevel;
CcShell *shell;
device = cc_device_combo_box_get_device (self->output_device_combo_box);
if (device != NULL)
stream = gvc_mixer_control_get_stream_from_device (self->mixer_control, device);
shell = cc_panel_get_shell (CC_PANEL (self));
toplevel = cc_shell_get_toplevel (shell);
window = cc_output_test_window_new (stream);
gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (toplevel));
gtk_window_present (GTK_WINDOW (window));
}
static void
volume_levels_activated_cb (CcSoundPanel *self)
{
CcVolumeLevelsWindow *volume_levels;
GtkWindow *toplevel;
CcShell *shell;
shell = cc_panel_get_shell (CC_PANEL (self));
toplevel = GTK_WINDOW (cc_shell_get_toplevel (shell));
volume_levels = cc_volume_levels_window_new (self->mixer_control);
gtk_window_set_transient_for (GTK_WINDOW (volume_levels), toplevel);
gtk_window_present (GTK_WINDOW (volume_levels));
}
static void
alert_sound_activated_cb (CcSoundPanel *self)
{
CcAlertChooserWindow *alert_chooser;
GtkWindow *toplevel;
CcShell *shell;
shell = cc_panel_get_shell (CC_PANEL (self));
toplevel = GTK_WINDOW (cc_shell_get_toplevel (shell));
alert_chooser = cc_alert_chooser_window_new ();
gtk_window_set_transient_for (GTK_WINDOW (alert_chooser), toplevel);
g_signal_connect_object (alert_chooser, "destroy",
G_CALLBACK (update_alert_sound_label),
self, G_CONNECT_SWAPPED);
gtk_window_present (GTK_WINDOW (alert_chooser));
}
static const char *
cc_sound_panel_get_help_uri (CcPanel *panel)
{
return "help:gnome-help/media#sound";
}
static void
cc_sound_panel_finalize (GObject *object)
{
CcSoundPanel *self = CC_SOUND_PANEL (object);
g_clear_object (&self->mixer_control);
g_clear_object (&self->sound_settings);
G_OBJECT_CLASS (cc_sound_panel_parent_class)->finalize (object);
}
static void
cc_sound_panel_class_init (CcSoundPanelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
panel_class->get_help_uri = cc_sound_panel_get_help_uri;
object_class->finalize = cc_sound_panel_finalize;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sound/cc-sound-panel.ui");
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_group);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_level_bar);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_device_combo_box);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_profile_row);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_profile_combo_box);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_volume_slider);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, balance_slider);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, fade_row);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, fade_slider);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, subwoofer_row);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, subwoofer_slider);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, output_no_devices_group);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_group);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_level_bar);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_device_combo_box);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_profile_row);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_profile_combo_box);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_volume_slider);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, input_no_devices_group);
gtk_widget_class_bind_template_child (widget_class, CcSoundPanel, alert_sound_row);
gtk_widget_class_bind_template_callback (widget_class, input_device_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, output_device_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, test_output_configuration_button_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, volume_levels_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, alert_sound_activated_cb);
g_type_ensure (CC_TYPE_BALANCE_SLIDER);
g_type_ensure (CC_TYPE_DEVICE_COMBO_BOX);
g_type_ensure (CC_TYPE_FADE_SLIDER);
g_type_ensure (CC_TYPE_LEVEL_BAR);
g_type_ensure (CC_TYPE_PROFILE_COMBO_BOX);
g_type_ensure (CC_TYPE_SUBWOOFER_SLIDER);
g_type_ensure (CC_TYPE_VOLUME_SLIDER);
g_type_ensure (CC_TYPE_LIST_ROW);
}
static void
cc_sound_panel_init (CcSoundPanel *self)
{
g_resources_register (cc_sound_get_resource ());
gtk_widget_init_template (GTK_WIDGET (self));
self->sound_settings = g_settings_new (KEY_SOUNDS_SCHEMA);
g_signal_connect_object (self->sound_settings,
"changed::allow-volume-above-100-percent",
G_CALLBACK (allow_amplified_changed_cb),
self,
G_CONNECT_SWAPPED);
allow_amplified_changed_cb (self);
self->mixer_control = gvc_mixer_control_new ("GNOME Settings");
cc_volume_slider_set_mixer_control (self->input_volume_slider, self->mixer_control);
cc_volume_slider_set_mixer_control (self->output_volume_slider, self->mixer_control);
cc_subwoofer_slider_set_mixer_control (self->subwoofer_slider, self->mixer_control);
cc_device_combo_box_set_mixer_control (self->input_device_combo_box, self->mixer_control, FALSE);
cc_device_combo_box_set_mixer_control (self->output_device_combo_box, self->mixer_control, TRUE);
g_signal_connect_object (self->mixer_control,
"input-added",
G_CALLBACK (cc_device_combo_box_device_added),
self->input_device_combo_box,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mixer_control,
"input-removed",
G_CALLBACK (cc_device_combo_box_device_removed),
self->input_device_combo_box,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mixer_control,
"active-input-update",
G_CALLBACK (input_device_update_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mixer_control,
"output-added",
G_CALLBACK (cc_device_combo_box_device_added),
self->output_device_combo_box,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mixer_control,
"output-removed",
G_CALLBACK (cc_device_combo_box_device_removed),
self->output_device_combo_box,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->mixer_control,
"active-output-update",
G_CALLBACK (output_device_update_cb),
self,
G_CONNECT_SWAPPED);
gvc_mixer_control_open (self->mixer_control);
update_alert_sound_label (self);
}