1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2008 William Jon McCann
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28
#include <glib/gi18n-lib.h>
29
#include <gdk/gdkkeysyms.h>
31
#include <pulse/pulseaudio.h>
33
#include "gvc-channel-bar.h"
34
#include "gvc-balance-bar.h"
35
#include "gvc-combo-box.h"
36
#include "gvc-mixer-control.h"
37
#include "gvc-mixer-card.h"
38
#include "gvc-mixer-sink.h"
39
#include "gvc-mixer-source.h"
40
#include "gvc-mixer-source-output.h"
41
#include "gvc-mixer-dialog.h"
42
#include "gvc-sound-theme-chooser.h"
43
#include "gvc-level-bar.h"
44
#include "gvc-speaker-test.h"
45
#include "gvc-mixer-control-private.h"
47
#define SCALE_SIZE 128
49
#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate))
51
struct GvcMixerDialogPrivate
53
GvcMixerControl *mixer_control;
56
GtkWidget *output_bar;
58
GtkWidget *input_level_bar;
59
GtkWidget *effects_bar;
60
GtkWidget *output_stream_box;
61
GtkWidget *sound_effects_box;
63
GtkWidget *hw_treeview;
64
GtkWidget *hw_settings_box;
65
GtkWidget *hw_profile_combo;
67
GtkWidget *output_box;
68
GtkWidget *applications_box;
69
GtkWidget *no_apps_label;
70
GtkWidget *output_treeview;
71
GtkWidget *output_settings_box;
72
GtkWidget *output_balance_bar;
73
GtkWidget *output_fade_bar;
74
GtkWidget *output_lfe_bar;
75
GtkWidget *output_port_combo;
76
GtkWidget *input_treeview;
77
GtkWidget *input_port_combo;
78
GtkWidget *input_settings_box;
79
GtkWidget *sound_theme_chooser;
80
GtkWidget *click_feedback_button;
81
GtkWidget *audible_bell_button;
82
GtkSizeGroup *size_group;
84
gdouble last_input_peak;
104
HW_PROFILE_HUMAN_COLUMN,
115
static void gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass);
116
static void gvc_mixer_dialog_init (GvcMixerDialog *mixer_dialog);
117
static void gvc_mixer_dialog_finalize (GObject *object);
119
static void bar_set_stream (GvcMixerDialog *dialog,
121
GvcMixerStream *stream);
123
static void on_adjustment_value_changed (GtkAdjustment *adjustment,
124
GvcMixerDialog *dialog);
126
G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_VBOX)
129
update_default_input (GvcMixerDialog *dialog)
135
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
136
ret = gtk_tree_model_get_iter_first (model, &iter);
138
g_debug ("No default input selected or available");
145
GvcMixerStream *stream;
147
gtk_tree_model_get (model, &iter,
149
ACTIVE_COLUMN, &toggled,
152
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
153
if (stream == NULL) {
154
g_warning ("Unable to find stream for id: %u", id);
159
if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
163
gtk_list_store_set (GTK_LIST_STORE (model),
165
ACTIVE_COLUMN, is_default,
168
GtkTreeSelection *selection;
170
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
171
gtk_tree_selection_select_iter (selection, &iter);
173
} while (gtk_tree_model_iter_next (model, &iter));
177
update_description (GvcMixerDialog *dialog,
180
GvcMixerStream *stream)
186
if (GVC_IS_MIXER_SOURCE (stream))
187
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
188
else if (GVC_IS_MIXER_SINK (stream))
189
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
191
g_assert_not_reached ();
192
gtk_tree_model_get_iter_first (model, &iter);
194
id = gvc_mixer_stream_get_id (stream);
198
gtk_tree_model_get (model, &iter,
199
ID_COLUMN, ¤t_id,
201
if (id != current_id)
204
gtk_list_store_set (GTK_LIST_STORE (model),
209
} while (gtk_tree_model_iter_next (model, &iter));
213
port_selection_changed (GvcComboBox *combo_box,
215
GvcMixerDialog *dialog)
217
GvcMixerStream *stream;
219
stream = g_object_get_data (G_OBJECT (combo_box), "stream");
220
if (stream == NULL) {
221
g_warning ("Could not find stream for port combo box");
224
if (gvc_mixer_stream_change_port (stream, port) == FALSE) {
225
g_warning ("Could not change port for stream");
230
update_output_settings (GvcMixerDialog *dialog)
232
GvcMixerStream *stream;
233
const GvcChannelMap *map;
237
g_debug ("Updating output settings");
238
if (dialog->priv->output_balance_bar != NULL) {
239
gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
240
dialog->priv->output_balance_bar);
241
dialog->priv->output_balance_bar = NULL;
243
if (dialog->priv->output_fade_bar != NULL) {
244
gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
245
dialog->priv->output_fade_bar);
246
dialog->priv->output_fade_bar = NULL;
248
if (dialog->priv->output_lfe_bar != NULL) {
249
gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
250
dialog->priv->output_lfe_bar);
251
dialog->priv->output_lfe_bar = NULL;
253
if (dialog->priv->output_port_combo != NULL) {
254
gtk_container_remove (GTK_CONTAINER (dialog->priv->output_settings_box),
255
dialog->priv->output_port_combo);
256
dialog->priv->output_port_combo = NULL;
259
stream = gvc_mixer_control_get_default_sink (dialog->priv->mixer_control);
260
if (stream == NULL) {
261
g_warning ("Default sink stream not found");
265
gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->output_bar),
266
gvc_mixer_stream_get_base_volume (stream));
267
gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->output_bar),
268
gvc_mixer_stream_get_can_decibel (stream));
270
/* Update the adjustment in case the previous bar wasn't decibel
271
* capable, and we clipped it */
272
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->output_bar)));
273
gtk_adjustment_set_value (adj,
274
gvc_mixer_stream_get_volume (stream));
276
map = gvc_mixer_stream_get_channel_map (stream);
278
g_warning ("Default sink stream has no channel map");
282
dialog->priv->output_balance_bar = gvc_balance_bar_new (map, BALANCE_TYPE_RL);
283
if (dialog->priv->size_group != NULL) {
284
gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_balance_bar),
285
dialog->priv->size_group,
288
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
289
dialog->priv->output_balance_bar,
291
gtk_widget_show (dialog->priv->output_balance_bar);
293
if (gvc_channel_map_can_fade (map)) {
294
dialog->priv->output_fade_bar = gvc_balance_bar_new (map, BALANCE_TYPE_FR);
295
if (dialog->priv->size_group != NULL) {
296
gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_fade_bar),
297
dialog->priv->size_group,
300
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
301
dialog->priv->output_fade_bar,
303
gtk_widget_show (dialog->priv->output_fade_bar);
306
if (gvc_channel_map_has_lfe (map)) {
307
dialog->priv->output_lfe_bar = gvc_balance_bar_new (map, BALANCE_TYPE_LFE);
308
if (dialog->priv->size_group != NULL) {
309
gvc_balance_bar_set_size_group (GVC_BALANCE_BAR (dialog->priv->output_lfe_bar),
310
dialog->priv->size_group,
313
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
314
dialog->priv->output_lfe_bar,
316
gtk_widget_show (dialog->priv->output_lfe_bar);
319
ports = gvc_mixer_stream_get_ports (stream);
321
const GvcMixerStreamPort *port;
322
port = gvc_mixer_stream_get_port (stream);
324
dialog->priv->output_port_combo = gvc_combo_box_new (_("Co_nnector:"));
325
gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->output_port_combo),
327
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->output_port_combo), port->port);
328
g_object_set_data (G_OBJECT (dialog->priv->output_port_combo), "stream", stream);
329
g_signal_connect (G_OBJECT (dialog->priv->output_port_combo), "changed",
330
G_CALLBACK (port_selection_changed), dialog);
332
gtk_box_pack_start (GTK_BOX (dialog->priv->output_settings_box),
333
dialog->priv->output_port_combo,
336
gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->output_port_combo), dialog->priv->size_group, FALSE);
338
gtk_widget_show (dialog->priv->output_port_combo);
341
/* FIXME: We could make this into a "No settings" label instead */
342
gtk_widget_set_sensitive (dialog->priv->output_balance_bar, gvc_channel_map_can_balance (map));
346
update_default_output (GvcMixerDialog *dialog)
351
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
352
gtk_tree_model_get_iter_first (model, &iter);
357
GvcMixerStream *stream;
359
gtk_tree_model_get (model, &iter,
361
ACTIVE_COLUMN, &toggled,
364
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
365
if (stream == NULL) {
366
g_warning ("Unable to find stream for id: %u", id);
371
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
375
gtk_list_store_set (GTK_LIST_STORE (model),
377
ACTIVE_COLUMN, is_default,
380
GtkTreeSelection *selection;
382
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
383
gtk_tree_selection_select_iter (selection, &iter);
385
} while (gtk_tree_model_iter_next (model, &iter));
389
on_mixer_control_default_sink_changed (GvcMixerControl *control,
391
GvcMixerDialog *dialog)
393
GvcMixerStream *stream;
395
g_debug ("GvcMixerDialog: default sink changed: %u", id);
397
if (id == PA_INVALID_INDEX)
400
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control,
402
bar_set_stream (dialog, dialog->priv->output_bar, stream);
404
update_output_settings (dialog);
406
update_default_output (dialog);
410
#define DECAY_STEP .15
413
update_input_peak (GvcMixerDialog *dialog,
418
if (dialog->priv->last_input_peak >= DECAY_STEP) {
419
if (v < dialog->priv->last_input_peak - DECAY_STEP) {
420
v = dialog->priv->last_input_peak - DECAY_STEP;
424
dialog->priv->last_input_peak = v;
426
adj = gvc_level_bar_get_peak_adjustment (GVC_LEVEL_BAR (dialog->priv->input_level_bar));
428
gtk_adjustment_set_value (adj, v);
430
gtk_adjustment_set_value (adj, 0.0);
435
update_input_meter (GvcMixerDialog *dialog,
436
uint32_t source_index,
437
uint32_t sink_input_idx,
440
update_input_peak (dialog, v);
444
on_monitor_suspended_callback (pa_stream *s,
447
GvcMixerDialog *dialog;
451
if (pa_stream_is_suspended (s)) {
452
g_debug ("Stream suspended");
453
update_input_meter (dialog,
454
pa_stream_get_device_index (s),
461
on_monitor_read_callback (pa_stream *s,
465
GvcMixerDialog *dialog;
471
if (pa_stream_peek (s, &data, &length) < 0) {
472
g_warning ("Failed to read data from stream");
477
assert (length % sizeof (float) == 0);
479
v = ((const float *) data)[length / sizeof (float) -1];
490
update_input_meter (dialog,
491
pa_stream_get_device_index (s),
492
pa_stream_get_monitor_stream (s),
497
create_monitor_stream_for_source (GvcMixerDialog *dialog,
498
GvcMixerStream *stream)
506
pa_proplist *proplist;
507
gboolean has_monitor;
509
if (stream == NULL) {
512
has_monitor = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (stream), "has-monitor"));
513
if (has_monitor != FALSE) {
517
g_debug ("Create monitor for %u",
518
gvc_mixer_stream_get_index (stream));
520
context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
522
if (pa_context_get_server_protocol_version (context) < 13) {
527
ss.format = PA_SAMPLE_FLOAT32;
530
memset (&attr, 0, sizeof (attr));
531
attr.fragsize = sizeof (float);
532
attr.maxlength = (uint32_t) -1;
534
snprintf (t, sizeof (t), "%u", gvc_mixer_stream_get_index (stream));
536
proplist = pa_proplist_new ();
537
pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "org.gnome.VolumeControl");
538
s = pa_stream_new_with_proplist (context, _("Peak detect"), &ss, NULL, proplist);
539
pa_proplist_free (proplist);
541
g_warning ("Failed to create monitoring stream");
545
pa_stream_set_read_callback (s, on_monitor_read_callback, dialog);
546
pa_stream_set_suspended_callback (s, on_monitor_suspended_callback, dialog);
548
res = pa_stream_connect_record (s,
551
(pa_stream_flags_t) (PA_STREAM_DONT_MOVE
552
|PA_STREAM_PEAK_DETECT
553
|PA_STREAM_ADJUST_LATENCY));
555
g_warning ("Failed to connect monitoring stream");
558
g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (TRUE));
559
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", s);
560
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", stream);
565
stop_monitor_stream_for_source (GvcMixerDialog *dialog)
570
GvcMixerStream *stream;
572
s = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream");
575
stream = g_object_get_data (G_OBJECT (dialog->priv->input_level_bar), "stream");
576
g_assert (stream != NULL);
578
g_debug ("Stopping monitor for %u", pa_stream_get_index (s));
580
context = gvc_mixer_control_get_pa_context (dialog->priv->mixer_control);
582
if (pa_context_get_server_protocol_version (context) < 13) {
586
res = pa_stream_disconnect (s);
588
g_object_set_data (G_OBJECT (stream), "has-monitor", GINT_TO_POINTER (FALSE));
589
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "pa_stream", NULL);
590
g_object_set_data (G_OBJECT (dialog->priv->input_level_bar), "stream", NULL);
594
update_input_settings (GvcMixerDialog *dialog)
597
GvcMixerStream *stream;
600
g_debug ("Updating input settings");
602
stop_monitor_stream_for_source (dialog);
604
if (dialog->priv->input_port_combo != NULL) {
605
gtk_container_remove (GTK_CONTAINER (dialog->priv->input_settings_box),
606
dialog->priv->input_port_combo);
607
dialog->priv->input_port_combo = NULL;
610
stream = gvc_mixer_control_get_default_source (dialog->priv->mixer_control);
611
if (stream == NULL) {
612
g_debug ("Default source stream not found");
616
gvc_channel_bar_set_base_volume (GVC_CHANNEL_BAR (dialog->priv->input_bar),
617
gvc_mixer_stream_get_base_volume (stream));
618
gvc_channel_bar_set_is_amplified (GVC_CHANNEL_BAR (dialog->priv->input_bar),
619
gvc_mixer_stream_get_can_decibel (stream));
621
/* Update the adjustment in case the previous bar wasn't decibel
622
* capable, and we clipped it */
623
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
624
gtk_adjustment_set_value (adj,
625
gvc_mixer_stream_get_volume (stream));
627
ports = gvc_mixer_stream_get_ports (stream);
629
const GvcMixerStreamPort *port;
630
port = gvc_mixer_stream_get_port (stream);
632
dialog->priv->input_port_combo = gvc_combo_box_new (_("Co_nnector:"));
633
gvc_combo_box_set_ports (GVC_COMBO_BOX (dialog->priv->input_port_combo),
635
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->input_port_combo), port->port);
636
g_object_set_data (G_OBJECT (dialog->priv->input_port_combo), "stream", stream);
637
g_signal_connect (G_OBJECT (dialog->priv->input_port_combo), "changed",
638
G_CALLBACK (port_selection_changed), dialog);
640
gvc_combo_box_set_size_group (GVC_COMBO_BOX (dialog->priv->input_port_combo), dialog->priv->size_group, FALSE);
641
gtk_box_pack_start (GTK_BOX (dialog->priv->input_settings_box),
642
dialog->priv->input_port_combo,
644
gtk_widget_show (dialog->priv->input_port_combo);
647
create_monitor_stream_for_source (dialog, stream);
651
on_mixer_control_default_source_changed (GvcMixerControl *control,
653
GvcMixerDialog *dialog)
655
GvcMixerStream *stream;
658
g_debug ("GvcMixerDialog: default source changed: %u", id);
660
if (id == PA_INVALID_INDEX)
663
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
665
/* Disconnect the adj, otherwise it might change if is_amplified changes */
666
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (dialog->priv->input_bar)));
667
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
669
bar_set_stream (dialog, dialog->priv->input_bar, stream);
670
update_input_settings (dialog);
672
g_signal_connect (adj,
674
G_CALLBACK (on_adjustment_value_changed),
677
update_default_input (dialog);
681
gvc_mixer_dialog_set_mixer_control (GvcMixerDialog *dialog,
682
GvcMixerControl *control)
684
g_return_if_fail (GVC_MIXER_DIALOG (dialog));
685
g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
687
g_object_ref (control);
689
if (dialog->priv->mixer_control != NULL) {
690
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
691
G_CALLBACK (on_mixer_control_default_sink_changed),
693
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
694
G_CALLBACK (on_mixer_control_default_source_changed),
696
g_object_unref (dialog->priv->mixer_control);
699
dialog->priv->mixer_control = control;
701
g_signal_connect (dialog->priv->mixer_control,
702
"default-sink-changed",
703
G_CALLBACK (on_mixer_control_default_sink_changed),
705
g_signal_connect (dialog->priv->mixer_control,
706
"default-source-changed",
707
G_CALLBACK (on_mixer_control_default_source_changed),
710
g_object_notify (G_OBJECT (dialog), "mixer-control");
713
static GvcMixerControl *
714
gvc_mixer_dialog_get_mixer_control (GvcMixerDialog *dialog)
716
g_return_val_if_fail (GVC_IS_MIXER_DIALOG (dialog), NULL);
718
return dialog->priv->mixer_control;
722
gvc_mixer_dialog_set_property (GObject *object,
727
GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
730
case PROP_MIXER_CONTROL:
731
gvc_mixer_dialog_set_mixer_control (self, g_value_get_object (value));
734
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
740
gvc_mixer_dialog_get_property (GObject *object,
745
GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
748
case PROP_MIXER_CONTROL:
749
g_value_set_object (value, gvc_mixer_dialog_get_mixer_control (self));
752
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
758
on_adjustment_value_changed (GtkAdjustment *adjustment,
759
GvcMixerDialog *dialog)
761
GvcMixerStream *stream;
763
stream = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-stream");
764
if (stream != NULL) {
766
gdouble volume, rounded;
769
volume = gtk_adjustment_get_value (adjustment);
770
rounded = round (volume);
772
bar = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-bar");
773
g_object_get (bar, "name", &name, NULL);
774
g_debug ("Setting stream volume %lf (rounded: %lf) for bar '%s'", volume, rounded, name);
777
/* FIXME would need to do that in the balance bar really... */
778
/* Make sure we do not unmute muted streams, there's a button for that */
780
gvc_mixer_stream_set_is_muted (stream, TRUE);
781
/* Only push the volume if it's actually changed */
782
if (gvc_mixer_stream_set_volume(stream, (pa_volume_t) rounded) != FALSE)
783
gvc_mixer_stream_push_volume (stream);
788
on_bar_is_muted_notify (GObject *object,
790
GvcMixerDialog *dialog)
793
GvcMixerStream *stream;
795
is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
797
stream = g_object_get_data (object, "gvc-mixer-dialog-stream");
798
if (stream != NULL) {
799
gvc_mixer_stream_change_is_muted (stream, is_muted);
802
g_object_get (object, "name", &name, NULL);
803
g_warning ("Unable to find stream for bar '%s'", name);
809
lookup_bar_for_stream (GvcMixerDialog *dialog,
810
GvcMixerStream *stream)
814
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)));
820
lookup_combo_box_for_stream (GvcMixerDialog *dialog,
821
GvcMixerStream *stream)
823
GvcMixerStream *combo_stream;
826
id = gvc_mixer_stream_get_id (stream);
828
if (dialog->priv->output_port_combo != NULL) {
829
combo_stream = g_object_get_data (G_OBJECT (dialog->priv->output_port_combo),
831
if (combo_stream != NULL) {
832
if (id == gvc_mixer_stream_get_id (combo_stream))
833
return dialog->priv->output_port_combo;
837
if (dialog->priv->input_port_combo != NULL) {
838
combo_stream = g_object_get_data (G_OBJECT (dialog->priv->input_port_combo),
840
if (combo_stream != NULL) {
841
if (id == gvc_mixer_stream_get_id (combo_stream))
842
return dialog->priv->input_port_combo;
850
on_stream_description_notify (GvcMixerStream *stream,
852
GvcMixerDialog *dialog)
854
update_description (dialog, NAME_COLUMN,
855
gvc_mixer_stream_get_description (stream),
860
on_stream_port_notify (GObject *object,
862
GvcMixerDialog *dialog)
864
GvcComboBox *combo_box;
867
combo_box = GVC_COMBO_BOX (lookup_combo_box_for_stream (dialog, GVC_MIXER_STREAM (object)));
868
if (combo_box == NULL)
871
g_signal_handlers_block_by_func (G_OBJECT (combo_box),
872
port_selection_changed,
875
g_object_get (object, "port", &port, NULL);
876
gvc_combo_box_set_active (GVC_COMBO_BOX (combo_box), port);
878
g_signal_handlers_unblock_by_func (G_OBJECT (combo_box),
879
port_selection_changed,
884
on_stream_volume_notify (GObject *object,
886
GvcMixerDialog *dialog)
888
GvcMixerStream *stream;
892
stream = GVC_MIXER_STREAM (object);
894
bar = lookup_bar_for_stream (dialog, stream);
897
g_warning ("Unable to find bar for stream %s in on_stream_volume_notify()",
898
gvc_mixer_stream_get_name (stream));
902
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
904
g_signal_handlers_block_by_func (adj,
905
on_adjustment_value_changed,
908
gtk_adjustment_set_value (adj,
909
gvc_mixer_stream_get_volume (stream));
911
g_signal_handlers_unblock_by_func (adj,
912
on_adjustment_value_changed,
917
on_stream_is_muted_notify (GObject *object,
919
GvcMixerDialog *dialog)
921
GvcMixerStream *stream;
925
stream = GVC_MIXER_STREAM (object);
926
bar = lookup_bar_for_stream (dialog, stream);
929
g_warning ("Unable to find bar for stream %s in on_stream_is_muted_notify()",
930
gvc_mixer_stream_get_name (stream));
934
is_muted = gvc_mixer_stream_get_is_muted (stream);
935
gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar),
938
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
939
gtk_widget_set_sensitive (dialog->priv->applications_box,
946
save_bar_for_stream (GvcMixerDialog *dialog,
947
GvcMixerStream *stream,
950
g_hash_table_insert (dialog->priv->bars,
951
GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
956
create_bar (GvcMixerDialog *dialog,
961
bar = gvc_channel_bar_new ();
962
gtk_widget_set_sensitive (bar, FALSE);
963
gvc_channel_bar_set_size_group (GVC_CHANNEL_BAR (bar),
964
dialog->priv->size_group,
966
gvc_channel_bar_set_orientation (GVC_CHANNEL_BAR (bar),
967
GTK_ORIENTATION_HORIZONTAL);
968
gvc_channel_bar_set_show_mute (GVC_CHANNEL_BAR (bar),
970
g_signal_connect (bar,
972
G_CALLBACK (on_bar_is_muted_notify),
978
create_app_bar (GvcMixerDialog *dialog,
980
const char *icon_name)
984
bar = create_bar (dialog, FALSE);
985
gvc_channel_bar_set_ellipsize (GVC_CHANNEL_BAR (bar), TRUE);
986
gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar), icon_name);
987
if (name == NULL || strchr (name, '_') == NULL) {
988
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), name);
990
char **tokens, *escaped;
992
tokens = g_strsplit (name, "_", -1);
993
escaped = g_strjoinv ("__", tokens);
995
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar), escaped);
1003
bar_set_stream (GvcMixerDialog *dialog,
1005
GvcMixerStream *stream)
1008
GvcMixerStream *old_stream;
1010
g_assert (bar != NULL);
1012
old_stream = g_object_get_data (G_OBJECT (bar), "gvc-mixer-dialog-stream");
1013
if (old_stream != NULL) {
1016
g_object_get (bar, "name", &name, NULL);
1017
g_debug ("Disconnecting old stream '%s' from bar '%s'",
1018
gvc_mixer_stream_get_name (old_stream), name);
1021
g_signal_handlers_disconnect_by_func (old_stream, on_stream_is_muted_notify, dialog);
1022
g_signal_handlers_disconnect_by_func (old_stream, on_stream_volume_notify, dialog);
1023
g_signal_handlers_disconnect_by_func (old_stream, on_stream_port_notify, dialog);
1024
g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (old_stream)));
1027
gtk_widget_set_sensitive (bar, (stream != NULL));
1029
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1031
g_signal_handlers_disconnect_by_func (adj, on_adjustment_value_changed, dialog);
1033
g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream);
1034
g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-stream", stream);
1035
g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-bar", bar);
1037
if (stream != NULL) {
1040
is_muted = gvc_mixer_stream_get_is_muted (stream);
1041
gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted);
1043
save_bar_for_stream (dialog, stream, bar);
1045
gtk_adjustment_set_value (adj,
1046
gvc_mixer_stream_get_volume (stream));
1048
g_signal_connect (stream,
1050
G_CALLBACK (on_stream_is_muted_notify),
1052
g_signal_connect (stream,
1054
G_CALLBACK (on_stream_volume_notify),
1056
g_signal_connect (stream,
1058
G_CALLBACK (on_stream_port_notify),
1060
g_signal_connect (adj,
1062
G_CALLBACK (on_adjustment_value_changed),
1068
add_stream (GvcMixerDialog *dialog,
1069
GvcMixerStream *stream)
1073
gboolean is_default;
1077
g_assert (stream != NULL);
1079
if (gvc_mixer_stream_is_event_stream (stream) != FALSE)
1084
id = gvc_mixer_stream_get_application_id (stream);
1086
if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
1087
bar = dialog->priv->output_bar;
1088
is_muted = gvc_mixer_stream_get_is_muted (stream);
1090
gtk_widget_set_sensitive (dialog->priv->applications_box,
1092
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1093
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
1094
update_output_settings (dialog);
1095
} else if (stream == gvc_mixer_control_get_default_source (dialog->priv->mixer_control)) {
1096
bar = dialog->priv->input_bar;
1097
adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
1098
g_signal_handlers_disconnect_by_func(adj, on_adjustment_value_changed, dialog);
1099
update_input_settings (dialog);
1101
} else if (stream == gvc_mixer_control_get_event_sink_input (dialog->priv->mixer_control)) {
1102
bar = dialog->priv->effects_bar;
1103
g_debug ("Adding effects stream");
1104
} else if (! GVC_IS_MIXER_SOURCE (stream)
1105
&& !GVC_IS_MIXER_SINK (stream)
1106
&& !gvc_mixer_stream_is_virtual (stream)
1107
&& g_strcmp0 (id, "org.gnome.VolumeControl") != 0
1108
&& g_strcmp0 (id, "org.PulseAudio.pavucontrol") != 0) {
1111
name = gvc_mixer_stream_get_name (stream);
1112
bar = create_app_bar (dialog, name,
1113
gvc_mixer_stream_get_icon_name (stream));
1115
gtk_box_pack_start (GTK_BOX (dialog->priv->applications_box), bar, FALSE, FALSE, 12);
1116
dialog->priv->num_apps++;
1117
gtk_widget_hide (dialog->priv->no_apps_label);
1120
if (GVC_IS_MIXER_SOURCE (stream)) {
1121
GtkTreeModel *model;
1125
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
1126
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1128
icon = gvc_mixer_stream_get_gicon (stream);
1129
gtk_list_store_set (GTK_LIST_STORE (model),
1131
NAME_COLUMN, gvc_mixer_stream_get_description (stream),
1133
ACTIVE_COLUMN, is_default,
1135
ID_COLUMN, gvc_mixer_stream_get_id (stream),
1138
g_object_unref (icon);
1141
GtkTreeSelection *selection;
1143
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->input_treeview));
1144
gtk_tree_selection_select_iter (selection, &iter);
1147
g_signal_connect (stream,
1148
"notify::description",
1149
G_CALLBACK (on_stream_description_notify),
1151
} else if (GVC_IS_MIXER_SINK (stream)) {
1152
GtkTreeModel *model;
1154
const GvcChannelMap *map;
1157
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
1158
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1160
map = gvc_mixer_stream_get_channel_map (stream);
1161
icon = gvc_mixer_stream_get_gicon (stream);
1162
gtk_list_store_set (GTK_LIST_STORE (model),
1164
NAME_COLUMN, gvc_mixer_stream_get_description (stream),
1166
ACTIVE_COLUMN, is_default,
1168
ID_COLUMN, gvc_mixer_stream_get_id (stream),
1169
SPEAKERS_COLUMN, gvc_channel_map_get_mapping (map),
1172
g_object_unref (icon);
1175
GtkTreeSelection *selection;
1177
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->output_treeview));
1178
gtk_tree_selection_select_iter (selection, &iter);
1181
g_signal_connect (stream,
1182
"notify::description",
1183
G_CALLBACK (on_stream_description_notify),
1188
bar_set_stream (dialog, bar, stream);
1189
gtk_widget_show (bar);
1194
on_control_stream_added (GvcMixerControl *control,
1196
GvcMixerDialog *dialog)
1198
GvcMixerStream *stream;
1201
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
1203
g_debug ("GvcMixerDialog: Stream %u already added", id);
1207
stream = gvc_mixer_control_lookup_stream_id (control, id);
1208
if (stream != NULL) {
1209
add_stream (dialog, stream);
1214
find_item_by_id (GtkTreeModel *model,
1219
gboolean found_item;
1223
if (!gtk_tree_model_get_iter_first (model, iter)) {
1230
gtk_tree_model_get (model, iter,
1236
} while (!found_item && gtk_tree_model_iter_next (model, iter));
1242
remove_stream (GvcMixerDialog *dialog,
1248
GtkTreeModel *model;
1250
/* remove bars for applications and reset fixed bars */
1251
bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (id));
1252
if (bar == dialog->priv->output_bar
1253
|| bar == dialog->priv->input_bar
1254
|| bar == dialog->priv->effects_bar) {
1256
g_object_get (bar, "name", &name, NULL);
1257
g_debug ("Removing stream for bar '%s'", name);
1259
bar_set_stream (dialog, bar, NULL);
1260
} else if (bar != NULL) {
1261
g_hash_table_remove (dialog->priv->bars, GUINT_TO_POINTER (id));
1262
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (bar)),
1264
dialog->priv->num_apps--;
1265
if (dialog->priv->num_apps == 0) {
1266
gtk_widget_show (dialog->priv->no_apps_label);
1270
/* remove from any models */
1271
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->output_treeview));
1272
found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
1274
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1276
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->input_treeview));
1277
found = find_item_by_id (GTK_TREE_MODEL (model), id, ID_COLUMN, &iter);
1279
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1284
on_control_stream_removed (GvcMixerControl *control,
1286
GvcMixerDialog *dialog)
1288
remove_stream (dialog, id);
1292
add_card (GvcMixerDialog *dialog,
1295
GtkTreeModel *model;
1297
GtkTreeSelection *selection;
1298
GvcMixerCardProfile *profile;
1302
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1303
index = gvc_mixer_card_get_index (card);
1304
if (find_item_by_id (GTK_TREE_MODEL (model), index, HW_ID_COLUMN, &iter) == FALSE)
1305
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1306
profile = gvc_mixer_card_get_profile (card);
1307
g_assert (profile != NULL);
1308
icon = g_themed_icon_new_with_default_fallbacks (gvc_mixer_card_get_icon_name (card));
1309
//FIXME we need the status (default for a profile?) here
1310
gtk_list_store_set (GTK_LIST_STORE (model),
1312
HW_NAME_COLUMN, gvc_mixer_card_get_name (card),
1313
HW_ID_COLUMN, index,
1314
HW_ICON_COLUMN, icon,
1315
HW_PROFILE_COLUMN, profile->profile,
1316
HW_PROFILE_HUMAN_COLUMN, profile->human_profile,
1317
HW_STATUS_COLUMN, profile->status,
1318
HW_SENSITIVE_COLUMN, g_strcmp0 (profile->profile, "off") != 0,
1321
g_object_unref (icon);
1323
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1324
if (gtk_tree_selection_get_selected (selection, NULL, NULL) == FALSE) {
1325
gtk_tree_selection_select_iter (selection, &iter);
1326
} else if (dialog->priv->hw_profile_combo != NULL) {
1327
GvcMixerCard *selected;
1329
/* Set the current profile if it changed for the selected card */
1330
selected = g_object_get_data (G_OBJECT (dialog->priv->hw_profile_combo), "card");
1331
if (gvc_mixer_card_get_index (selected) == gvc_mixer_card_get_index (card)) {
1332
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo),
1334
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
1335
"show-button", profile->n_sinks == 1,
1342
on_control_card_added (GvcMixerControl *control,
1344
GvcMixerDialog *dialog)
1348
card = gvc_mixer_control_lookup_card_id (control, id);
1350
add_card (dialog, card);
1355
remove_card (GvcMixerDialog *dialog,
1360
GtkTreeModel *model;
1362
/* remove from any models */
1363
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1364
found = find_item_by_id (GTK_TREE_MODEL (model), id, HW_ID_COLUMN, &iter);
1366
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
1370
on_control_card_removed (GvcMixerControl *control,
1372
GvcMixerDialog *dialog)
1374
remove_card (dialog, id);
1378
_gtk_label_make_bold (GtkLabel *label)
1380
PangoFontDescription *font_desc;
1382
font_desc = pango_font_description_new ();
1384
pango_font_description_set_weight (font_desc,
1387
/* This will only affect the weight of the font, the rest is
1388
* from the current state of the widget, which comes from the
1389
* theme or user prefs, since the font desc only has the
1390
* weight flag turned on.
1392
gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
1394
pango_font_description_free (font_desc);
1398
on_input_selection_changed (GtkTreeSelection *selection,
1399
GvcMixerDialog *dialog)
1401
GtkTreeModel *model;
1406
if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
1407
g_debug ("Could not get default input from selection");
1411
gtk_tree_model_get (model, &iter,
1413
ACTIVE_COLUMN, &toggled,
1418
GvcMixerStream *stream;
1420
g_debug ("Default input selected: %u", id);
1421
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
1422
if (stream == NULL) {
1423
g_warning ("Unable to find stream for id: %u", id);
1427
gvc_mixer_control_set_default_source (dialog->priv->mixer_control, stream);
1432
on_output_selection_changed (GtkTreeSelection *selection,
1433
GvcMixerDialog *dialog)
1435
GtkTreeModel *model;
1440
if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
1441
g_debug ("Could not get default output from selection");
1445
gtk_tree_model_get (model, &iter,
1447
ACTIVE_COLUMN, &toggled,
1452
GvcMixerStream *stream;
1454
g_debug ("Default output selected: %u", id);
1455
stream = gvc_mixer_control_lookup_stream_id (dialog->priv->mixer_control, id);
1456
if (stream == NULL) {
1457
g_warning ("Unable to find stream for id: %u", id);
1461
gvc_mixer_control_set_default_sink (dialog->priv->mixer_control, stream);
1466
name_to_text (GtkTreeViewColumn *column,
1467
GtkCellRenderer *cell,
1468
GtkTreeModel *model,
1472
char *name, *mapping;
1474
gtk_tree_model_get(model, iter,
1476
SPEAKERS_COLUMN, &mapping,
1479
if (mapping == NULL) {
1480
g_object_set (cell, "text", name, NULL);
1484
str = g_strdup_printf ("%s\n<i>%s</i>",
1486
g_object_set (cell, "markup", str, NULL);
1495
create_stream_treeview (GvcMixerDialog *dialog,
1496
GCallback on_selection_changed)
1498
GtkWidget *treeview;
1499
GtkListStore *store;
1500
GtkCellRenderer *renderer;
1501
GtkTreeViewColumn *column;
1502
GtkTreeSelection *selection;
1504
treeview = gtk_tree_view_new ();
1505
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
1507
store = gtk_list_store_new (NUM_COLUMNS,
1514
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
1515
GTK_TREE_MODEL (store));
1517
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1518
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1520
column = gtk_tree_view_column_new ();
1521
gtk_tree_view_column_set_title (column, _("Name"));
1522
renderer = gtk_cell_renderer_pixbuf_new ();
1523
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1524
g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_LARGE_TOOLBAR, NULL);
1525
gtk_tree_view_column_set_attributes (column, renderer,
1526
"gicon", ICON_COLUMN,
1529
renderer = gtk_cell_renderer_text_new ();
1530
gtk_tree_view_column_pack_start (column, renderer, TRUE);
1531
gtk_tree_view_column_set_cell_data_func (column, renderer,
1532
name_to_text, NULL, NULL);
1533
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1535
g_signal_connect (G_OBJECT (selection), "changed",
1536
on_selection_changed, dialog);
1538
renderer = gtk_cell_renderer_text_new ();
1539
column = gtk_tree_view_column_new_with_attributes (_("Device"),
1541
"text", DEVICE_COLUMN,
1543
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1549
on_profile_changed (GvcComboBox *widget,
1550
const char *profile,
1555
card = g_object_get_data (G_OBJECT (widget), "card");
1557
g_warning ("Could not find card for combobox");
1561
g_debug ("Profile changed to %s for card %s", profile,
1562
gvc_mixer_card_get_name (card));
1564
gvc_mixer_card_change_profile (card, profile);
1568
on_test_speakers_clicked (GvcComboBox *widget,
1571
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data);
1573
GvcMixerCardProfile *profile;
1574
GtkWidget *d, *speaker_test, *container;
1577
card = g_object_get_data (G_OBJECT (widget), "card");
1579
g_warning ("Could not find card for combobox");
1582
profile = gvc_mixer_card_get_profile (card);
1584
g_debug ("XXX Start speaker testing for profile '%s', card %s XXX",
1585
profile->profile, gvc_mixer_card_get_name (card));
1587
title = g_strdup_printf (_("Speaker Testing for %s"), gvc_mixer_card_get_name (card));
1588
d = gtk_dialog_new_with_buttons (title,
1589
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))),
1590
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1591
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
1593
/* FIXME: this should not be necessary */
1594
gtk_window_set_has_resize_grip (GTK_WINDOW (d), FALSE);
1597
speaker_test = gvc_speaker_test_new (dialog->priv->mixer_control,
1599
gtk_widget_show (speaker_test);
1600
container = gtk_dialog_get_content_area (GTK_DIALOG (d));
1601
gtk_container_add (GTK_CONTAINER (container), speaker_test);
1603
gtk_dialog_run (GTK_DIALOG (d));
1604
gtk_widget_destroy (d);
1608
on_card_selection_changed (GtkTreeSelection *selection,
1611
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (user_data);
1612
GtkTreeModel *model;
1614
const GList *profiles;
1617
GvcMixerCardProfile *current_profile;
1619
g_debug ("Card selection changed");
1621
if (dialog->priv->hw_profile_combo != NULL) {
1622
gtk_container_remove (GTK_CONTAINER (dialog->priv->hw_settings_box),
1623
dialog->priv->hw_profile_combo);
1624
dialog->priv->hw_profile_combo = NULL;
1627
if (gtk_tree_selection_get_selected (selection,
1633
model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->priv->hw_treeview));
1634
gtk_tree_model_get (model, &iter,
1637
card = gvc_mixer_control_lookup_card_id (dialog->priv->mixer_control, id);
1639
g_warning ("Unable to find card for id: %u", id);
1643
current_profile = gvc_mixer_card_get_profile (card);
1644
profiles = gvc_mixer_card_get_profiles (card);
1645
dialog->priv->hw_profile_combo = gvc_combo_box_new (_("_Profile:"));
1646
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo), "button-label", _("Test Speakers"), NULL);
1647
gvc_combo_box_set_profiles (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), profiles);
1648
gvc_combo_box_set_active (GVC_COMBO_BOX (dialog->priv->hw_profile_combo), current_profile->profile);
1650
gtk_box_pack_start (GTK_BOX (dialog->priv->hw_settings_box),
1651
dialog->priv->hw_profile_combo,
1653
g_object_set (G_OBJECT (dialog->priv->hw_profile_combo),
1654
"show-button", current_profile->n_sinks == 1,
1656
gtk_widget_show (dialog->priv->hw_profile_combo);
1658
g_object_set_data (G_OBJECT (dialog->priv->hw_profile_combo), "card", card);
1659
g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "changed",
1660
G_CALLBACK (on_profile_changed), dialog);
1661
g_signal_connect (G_OBJECT (dialog->priv->hw_profile_combo), "button-clicked",
1662
G_CALLBACK (on_test_speakers_clicked), dialog);
1666
card_to_text (GtkTreeViewColumn *column,
1667
GtkCellRenderer *cell,
1668
GtkTreeModel *model,
1672
char *name, *status, *profile, *str;
1675
gtk_tree_model_get(model, iter,
1676
HW_NAME_COLUMN, &name,
1677
HW_STATUS_COLUMN, &status,
1678
HW_PROFILE_HUMAN_COLUMN, &profile,
1679
HW_SENSITIVE_COLUMN, &sensitive,
1682
str = g_strdup_printf ("%s\n<i>%s</i>\n<i>%s</i>",
1683
name, status, profile);
1686
"sensitive", sensitive,
1696
create_cards_treeview (GvcMixerDialog *dialog,
1697
GCallback on_changed)
1699
GtkWidget *treeview;
1700
GtkListStore *store;
1701
GtkCellRenderer *renderer;
1702
GtkTreeViewColumn *column;
1703
GtkTreeSelection *selection;
1705
treeview = gtk_tree_view_new ();
1706
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
1708
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
1709
g_signal_connect (G_OBJECT (selection), "changed",
1710
on_changed, dialog);
1712
store = gtk_list_store_new (HW_NUM_COLUMNS,
1720
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
1721
GTK_TREE_MODEL (store));
1723
renderer = gtk_cell_renderer_pixbuf_new ();
1724
g_object_set (G_OBJECT (renderer), "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
1725
column = gtk_tree_view_column_new_with_attributes (NULL,
1727
"gicon", HW_ICON_COLUMN,
1728
"sensitive", HW_SENSITIVE_COLUMN,
1730
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
1732
gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (treeview), -1,
1733
_("Name"), gtk_cell_renderer_text_new (),
1734
card_to_text, NULL, NULL);
1740
gvc_mixer_dialog_constructor (GType type,
1741
guint n_construct_properties,
1742
GObjectConstructParam *construct_params)
1745
GvcMixerDialog *self;
1746
GtkWidget *main_vbox;
1748
GtkWidget *alignment;
1755
GvcMixerStream *stream;
1757
GtkTreeSelection *selection;
1759
object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, n_construct_properties, construct_params);
1761
self = GVC_MIXER_DIALOG (object);
1763
main_vbox = GTK_WIDGET (self);
1764
gtk_box_set_spacing (GTK_BOX (main_vbox), 2);
1766
gtk_container_set_border_width (GTK_CONTAINER (self), 6);
1768
self->priv->output_stream_box = gtk_hbox_new (FALSE, 12);
1769
alignment = gtk_alignment_new (0, 0, 1, 1);
1770
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0);
1771
gtk_container_add (GTK_CONTAINER (alignment), self->priv->output_stream_box);
1772
gtk_box_pack_start (GTK_BOX (main_vbox),
1775
self->priv->output_bar = create_bar (self, TRUE);
1776
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->output_bar),
1777
_("_Output volume: "));
1778
gtk_widget_set_sensitive (self->priv->output_bar, FALSE);
1779
gtk_box_pack_start (GTK_BOX (self->priv->output_stream_box),
1780
self->priv->output_bar, TRUE, TRUE, 12);
1782
self->priv->notebook = gtk_notebook_new ();
1783
gtk_box_pack_start (GTK_BOX (main_vbox),
1784
self->priv->notebook,
1786
gtk_container_set_border_width (GTK_CONTAINER (self->priv->notebook), 5);
1789
self->priv->sound_effects_box = gtk_vbox_new (FALSE, 6);
1790
gtk_container_set_border_width (GTK_CONTAINER (self->priv->sound_effects_box), 12);
1791
label = gtk_label_new (_("Sound Effects"));
1792
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1793
self->priv->sound_effects_box,
1796
self->priv->effects_bar = create_bar (self, TRUE);
1797
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->effects_bar),
1798
_("_Alert volume: "));
1799
gtk_widget_set_sensitive (self->priv->effects_bar, FALSE);
1800
gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
1801
self->priv->effects_bar, FALSE, FALSE, 0);
1803
self->priv->sound_theme_chooser = gvc_sound_theme_chooser_new ();
1804
gtk_box_pack_start (GTK_BOX (self->priv->sound_effects_box),
1805
self->priv->sound_theme_chooser,
1809
self->priv->hw_box = gtk_vbox_new (FALSE, 12);
1810
gtk_container_set_border_width (GTK_CONTAINER (self->priv->hw_box), 12);
1811
label = gtk_label_new (_("Hardware"));
1812
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1816
box = gtk_frame_new (_("C_hoose a device to configure:"));
1817
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1818
_gtk_label_make_bold (GTK_LABEL (label));
1819
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1820
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1821
gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, TRUE, TRUE, 0);
1823
alignment = gtk_alignment_new (0, 0, 1, 1);
1824
gtk_container_add (GTK_CONTAINER (box), alignment);
1825
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1827
self->priv->hw_treeview = create_cards_treeview (self,
1828
G_CALLBACK (on_card_selection_changed));
1829
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->hw_treeview);
1831
box = gtk_scrolled_window_new (NULL, NULL);
1832
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1834
GTK_POLICY_AUTOMATIC);
1835
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1837
gtk_container_add (GTK_CONTAINER (box), self->priv->hw_treeview);
1838
gtk_container_add (GTK_CONTAINER (alignment), box);
1840
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->hw_treeview));
1841
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1843
box = gtk_frame_new (_("Settings for the selected device:"));
1844
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1845
_gtk_label_make_bold (GTK_LABEL (label));
1846
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1847
gtk_box_pack_start (GTK_BOX (self->priv->hw_box), box, FALSE, TRUE, 12);
1848
self->priv->hw_settings_box = gtk_vbox_new (FALSE, 12);
1849
gtk_container_add (GTK_CONTAINER (box), self->priv->hw_settings_box);
1852
self->priv->input_box = gtk_vbox_new (FALSE, 12);
1853
gtk_container_set_border_width (GTK_CONTAINER (self->priv->input_box), 12);
1854
label = gtk_label_new (_("Input"));
1855
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1856
self->priv->input_box,
1859
self->priv->input_bar = create_bar (self, TRUE);
1860
gvc_channel_bar_set_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1861
_("_Input volume: "));
1862
gvc_channel_bar_set_low_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1863
"audio-input-microphone-low-symbolic");
1864
gvc_channel_bar_set_high_icon_name (GVC_CHANNEL_BAR (self->priv->input_bar),
1865
"audio-input-microphone-high-symbolic");
1866
gtk_widget_set_sensitive (self->priv->input_bar, FALSE);
1867
alignment = gtk_alignment_new (0, 0, 1, 1);
1868
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1869
gtk_container_add (GTK_CONTAINER (alignment), self->priv->input_bar);
1870
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1874
box = gtk_hbox_new (FALSE, 6);
1875
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1879
sbox = gtk_hbox_new (FALSE, 6);
1880
gtk_box_pack_start (GTK_BOX (box),
1884
label = gtk_label_new (_("Input level:"));
1885
gtk_box_pack_start (GTK_BOX (sbox),
1888
gtk_size_group_add_widget (self->priv->size_group, sbox);
1890
self->priv->input_level_bar = gvc_level_bar_new ();
1891
gvc_level_bar_set_orientation (GVC_LEVEL_BAR (self->priv->input_level_bar),
1892
GTK_ORIENTATION_HORIZONTAL);
1893
gvc_level_bar_set_scale (GVC_LEVEL_BAR (self->priv->input_level_bar),
1894
GVC_LEVEL_SCALE_LINEAR);
1895
gtk_box_pack_start (GTK_BOX (box),
1896
self->priv->input_level_bar,
1899
ebox = gtk_hbox_new (FALSE, 6);
1900
gtk_box_pack_start (GTK_BOX (box),
1903
gtk_size_group_add_widget (self->priv->size_group, ebox);
1905
self->priv->input_settings_box = gtk_hbox_new (FALSE, 6);
1906
gtk_box_pack_start (GTK_BOX (self->priv->input_box),
1907
self->priv->input_settings_box,
1910
box = gtk_frame_new (_("C_hoose a device for sound input:"));
1911
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1912
_gtk_label_make_bold (GTK_LABEL (label));
1913
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1914
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1915
gtk_box_pack_start (GTK_BOX (self->priv->input_box), box, TRUE, TRUE, 0);
1917
alignment = gtk_alignment_new (0, 0, 1, 1);
1918
gtk_container_add (GTK_CONTAINER (box), alignment);
1919
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1921
self->priv->input_treeview = create_stream_treeview (self,
1922
G_CALLBACK (on_input_selection_changed));
1923
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->input_treeview);
1925
box = gtk_scrolled_window_new (NULL, NULL);
1926
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1928
GTK_POLICY_AUTOMATIC);
1929
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1931
gtk_container_add (GTK_CONTAINER (box), self->priv->input_treeview);
1932
gtk_container_add (GTK_CONTAINER (alignment), box);
1935
self->priv->output_box = gtk_vbox_new (FALSE, 12);
1936
gtk_container_set_border_width (GTK_CONTAINER (self->priv->output_box), 12);
1937
label = gtk_label_new (_("Output"));
1938
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1939
self->priv->output_box,
1942
box = gtk_frame_new (_("C_hoose a device for sound output:"));
1943
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1944
_gtk_label_make_bold (GTK_LABEL (label));
1945
gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
1946
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1947
gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, TRUE, TRUE, 0);
1949
alignment = gtk_alignment_new (0, 0, 1, 1);
1950
gtk_container_add (GTK_CONTAINER (box), alignment);
1951
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1953
self->priv->output_treeview = create_stream_treeview (self,
1954
G_CALLBACK (on_output_selection_changed));
1955
gtk_label_set_mnemonic_widget (GTK_LABEL (label), self->priv->output_treeview);
1957
box = gtk_scrolled_window_new (NULL, NULL);
1958
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
1960
GTK_POLICY_AUTOMATIC);
1961
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
1963
gtk_container_add (GTK_CONTAINER (box), self->priv->output_treeview);
1964
gtk_container_add (GTK_CONTAINER (alignment), box);
1966
box = gtk_frame_new (_("Settings for the selected device:"));
1967
label = gtk_frame_get_label_widget (GTK_FRAME (box));
1968
_gtk_label_make_bold (GTK_LABEL (label));
1969
gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
1970
gtk_box_pack_start (GTK_BOX (self->priv->output_box), box, FALSE, FALSE, 12);
1971
self->priv->output_settings_box = gtk_vbox_new (FALSE, 0);
1972
gtk_container_add (GTK_CONTAINER (box), self->priv->output_settings_box);
1975
self->priv->applications_box = gtk_vbox_new (FALSE, 12);
1976
gtk_container_set_border_width (GTK_CONTAINER (self->priv->applications_box), 12);
1977
label = gtk_label_new (_("Applications"));
1978
gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
1979
self->priv->applications_box,
1981
self->priv->no_apps_label = gtk_label_new (_("No application is currently playing or recording audio."));
1982
gtk_box_pack_start (GTK_BOX (self->priv->applications_box),
1983
self->priv->no_apps_label,
1986
g_signal_connect (self->priv->mixer_control,
1988
G_CALLBACK (on_control_stream_added),
1990
g_signal_connect (self->priv->mixer_control,
1992
G_CALLBACK (on_control_stream_removed),
1994
g_signal_connect (self->priv->mixer_control,
1996
G_CALLBACK (on_control_card_added),
1998
g_signal_connect (self->priv->mixer_control,
2000
G_CALLBACK (on_control_card_removed),
2003
gtk_widget_show_all (main_vbox);
2005
streams = gvc_mixer_control_get_streams (self->priv->mixer_control);
2006
for (l = streams; l != NULL; l = l->next) {
2008
add_stream (self, stream);
2010
g_slist_free (streams);
2012
cards = gvc_mixer_control_get_cards (self->priv->mixer_control);
2013
for (l = cards; l != NULL; l = l->next) {
2015
add_card (self, card);
2017
g_slist_free (cards);
2023
gvc_mixer_dialog_dispose (GObject *object)
2025
GvcMixerDialog *dialog = GVC_MIXER_DIALOG (object);
2027
if (dialog->priv->mixer_control != NULL) {
2028
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2029
on_control_stream_added,
2031
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2032
on_control_stream_removed,
2034
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2035
on_control_card_added,
2037
g_signal_handlers_disconnect_by_func (dialog->priv->mixer_control,
2038
on_control_card_removed,
2041
g_object_unref (dialog->priv->mixer_control);
2042
dialog->priv->mixer_control = NULL;
2045
if (dialog->priv->bars != NULL) {
2046
g_hash_table_destroy (dialog->priv->bars);
2047
dialog->priv->bars = NULL;
2050
G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->dispose (object);
2054
gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass)
2056
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2058
object_class->constructor = gvc_mixer_dialog_constructor;
2059
object_class->dispose = gvc_mixer_dialog_dispose;
2060
object_class->finalize = gvc_mixer_dialog_finalize;
2061
object_class->set_property = gvc_mixer_dialog_set_property;
2062
object_class->get_property = gvc_mixer_dialog_get_property;
2064
g_object_class_install_property (object_class,
2066
g_param_spec_object ("mixer-control",
2069
GVC_TYPE_MIXER_CONTROL,
2070
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
2072
g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate));
2077
gvc_mixer_dialog_init (GvcMixerDialog *dialog)
2079
dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog);
2080
dialog->priv->bars = g_hash_table_new (NULL, NULL);
2081
dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
2085
gvc_mixer_dialog_finalize (GObject *object)
2087
GvcMixerDialog *mixer_dialog;
2089
g_return_if_fail (object != NULL);
2090
g_return_if_fail (GVC_IS_MIXER_DIALOG (object));
2092
mixer_dialog = GVC_MIXER_DIALOG (object);
2094
g_return_if_fail (mixer_dialog->priv != NULL);
2095
G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object);
2099
gvc_mixer_dialog_new (GvcMixerControl *control)
2102
dialog = g_object_new (GVC_TYPE_MIXER_DIALOG,
2103
"mixer-control", control,
2105
return GVC_MIXER_DIALOG (dialog);
2117
gvc_mixer_dialog_set_page (GvcMixerDialog *self,
2122
g_return_val_if_fail (self != NULL, FALSE);
2126
else if (g_str_equal (page, "effects"))
2128
else if (g_str_equal (page, "hardware"))
2129
num = PAGE_HARDWARE;
2130
else if (g_str_equal (page, "input"))
2132
else if (g_str_equal (page, "output"))
2134
else if (g_str_equal (page, "applications"))
2135
num = PAGE_APPLICATIONS;
2139
gtk_notebook_set_current_page (GTK_NOTEBOOK (self->priv->notebook), num);