1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2006-2008 Lennart Poettering
4
* Copyright (C) 2008 Sjoerd Simons <sjoerd@luon.net>
5
* Copyright (C) 2008 William Jon McCann
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30
#include <glib/gi18n-lib.h>
32
#include <pulse/pulseaudio.h>
33
#include <pulse/glib-mainloop.h>
34
#include <pulse/ext-stream-restore.h>
36
#include "gvc-mixer-control.h"
37
#include "gvc-mixer-sink.h"
38
#include "gvc-mixer-source.h"
39
#include "gvc-mixer-sink-input.h"
40
#include "gvc-mixer-source-output.h"
41
#include "gvc-mixer-event-role.h"
42
#include "gvc-mixer-card.h"
43
#include "gvc-mixer-card-private.h"
44
#include "gvc-channel-map-private.h"
45
#include "gvc-mixer-control-private.h"
47
#define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate))
49
#define RECONNECT_DELAY 5
56
struct GvcMixerControlPrivate
58
pa_glib_mainloop *pa_mainloop;
59
pa_mainloop_api *pa_api;
60
pa_context *pa_context;
65
gboolean default_sink_is_set;
66
guint default_sink_id;
67
char *default_sink_name;
68
gboolean default_source_is_set;
69
guint default_source_id;
70
char *default_source_name;
72
gboolean event_sink_input_is_set;
73
guint event_sink_input_id;
75
GHashTable *all_streams;
76
GHashTable *sinks; /* fixed outputs */
77
GHashTable *sources; /* fixed inputs */
78
GHashTable *sink_inputs; /* routable output streams */
79
GHashTable *source_outputs; /* routable input streams */
83
GvcMixerStream *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
94
DEFAULT_SOURCE_CHANGED,
98
static guint signals [LAST_SIGNAL] = { 0, };
100
static void gvc_mixer_control_class_init (GvcMixerControlClass *klass);
101
static void gvc_mixer_control_init (GvcMixerControl *mixer_control);
102
static void gvc_mixer_control_finalize (GObject *object);
104
G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
107
gvc_mixer_control_get_pa_context (GvcMixerControl *control)
109
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
110
return control->priv->pa_context;
114
* gvc_mixer_control_get_event_sink_input:
118
* Returns: (transfer none):
121
gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
123
GvcMixerStream *stream;
125
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
127
stream = g_hash_table_lookup (control->priv->all_streams,
128
GUINT_TO_POINTER (control->priv->event_sink_input_id));
134
gvc_mixer_control_stream_restore_cb (pa_context *c,
135
const pa_ext_stream_restore_info *info,
140
GvcMixerControl *control = (GvcMixerControl *) userdata;
141
pa_ext_stream_restore_info new_info;
143
if (eol || control->priv->new_default_stream == NULL)
146
new_info.name = info->name;
147
new_info.channel_map = info->channel_map;
148
new_info.volume = info->volume;
149
new_info.mute = info->mute;
151
new_info.device = gvc_mixer_stream_get_name (control->priv->new_default_stream);
153
o = pa_ext_stream_restore_write (control->priv->pa_context,
159
g_warning ("pa_ext_stream_restore_write() failed: %s",
160
pa_strerror (pa_context_errno (control->priv->pa_context)));
164
g_debug ("Changed default device for %s to %s", info->name, info->device);
166
pa_operation_unref (o);
170
gvc_mixer_control_set_default_sink (GvcMixerControl *control,
171
GvcMixerStream *stream)
175
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
176
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
178
o = pa_context_set_default_sink (control->priv->pa_context,
179
gvc_mixer_stream_get_name (stream),
183
g_warning ("pa_context_set_default_sink() failed: %s",
184
pa_strerror (pa_context_errno (control->priv->pa_context)));
188
pa_operation_unref (o);
190
control->priv->new_default_stream = stream;
191
g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
193
o = pa_ext_stream_restore_read (control->priv->pa_context,
194
gvc_mixer_control_stream_restore_cb,
198
g_warning ("pa_ext_stream_restore_read() failed: %s",
199
pa_strerror (pa_context_errno (control->priv->pa_context)));
203
pa_operation_unref (o);
209
gvc_mixer_control_set_default_source (GvcMixerControl *control,
210
GvcMixerStream *stream)
214
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
215
g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
217
o = pa_context_set_default_source (control->priv->pa_context,
218
gvc_mixer_stream_get_name (stream),
222
g_warning ("pa_context_set_default_source() failed");
226
pa_operation_unref (o);
232
* gvc_mixer_control_get_default_sink:
236
* Returns: (transfer none):
239
gvc_mixer_control_get_default_sink (GvcMixerControl *control)
241
GvcMixerStream *stream;
243
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
245
if (control->priv->default_sink_is_set) {
246
stream = g_hash_table_lookup (control->priv->all_streams,
247
GUINT_TO_POINTER (control->priv->default_sink_id));
256
* gvc_mixer_control_get_default_source:
260
* Returns: (transfer none):
263
gvc_mixer_control_get_default_source (GvcMixerControl *control)
265
GvcMixerStream *stream;
267
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
269
if (control->priv->default_source_is_set) {
270
stream = g_hash_table_lookup (control->priv->all_streams,
271
GUINT_TO_POINTER (control->priv->default_source_id));
280
gvc_mixer_control_lookup_id (GHashTable *hash_table,
283
return g_hash_table_lookup (hash_table,
284
GUINT_TO_POINTER (id));
288
* gvc_mixer_control_lookup_stream_id:
293
* Returns: (transfer none):
296
gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
299
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
301
return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
305
* gvc_mixer_control_lookup_card_id:
310
* Returns: (transfer none):
313
gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
316
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
318
return gvc_mixer_control_lookup_id (control->priv->cards, id);
322
listify_hash_values_hfunc (gpointer key,
326
GSList **list = user_data;
328
*list = g_slist_prepend (*list, value);
332
gvc_name_collate (const char *namea,
335
if (nameb == NULL && namea == NULL)
342
return g_utf8_collate (namea, nameb);
346
gvc_card_collate (GvcMixerCard *a,
352
g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0);
353
g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0);
355
namea = gvc_mixer_card_get_name (a);
356
nameb = gvc_mixer_card_get_name (b);
358
return gvc_name_collate (namea, nameb);
362
* gvc_mixer_control_get_cards:
366
* Returns: (transfer container) (element-type Gvc.MixerCard):
369
gvc_mixer_control_get_cards (GvcMixerControl *control)
373
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
376
g_hash_table_foreach (control->priv->cards,
377
listify_hash_values_hfunc,
379
return g_slist_sort (retval, (GCompareFunc) gvc_card_collate);
383
gvc_stream_collate (GvcMixerStream *a,
389
g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0);
390
g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0);
392
namea = gvc_mixer_stream_get_name (a);
393
nameb = gvc_mixer_stream_get_name (b);
395
return gvc_name_collate (namea, nameb);
399
* gvc_mixer_control_get_streams:
403
* Returns: (transfer container) (element-type Gvc.MixerStream):
406
gvc_mixer_control_get_streams (GvcMixerControl *control)
410
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
413
g_hash_table_foreach (control->priv->all_streams,
414
listify_hash_values_hfunc,
416
return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
420
* gvc_mixer_control_get_sinks:
424
* Returns: (transfer container) (element-type Gvc.MixerSink):
427
gvc_mixer_control_get_sinks (GvcMixerControl *control)
431
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
434
g_hash_table_foreach (control->priv->sinks,
435
listify_hash_values_hfunc,
437
return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
441
* gvc_mixer_control_get_sources:
445
* Returns: (transfer container) (element-type Gvc.MixerSource):
448
gvc_mixer_control_get_sources (GvcMixerControl *control)
452
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
455
g_hash_table_foreach (control->priv->sources,
456
listify_hash_values_hfunc,
458
return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
462
* gvc_mixer_control_get_sink_inputs:
466
* Returns: (transfer container) (element-type Gvc.MixerSinkInput):
469
gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
473
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
476
g_hash_table_foreach (control->priv->sink_inputs,
477
listify_hash_values_hfunc,
479
return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
483
* gvc_mixer_control_get_source_outputs:
487
* Returns: (transfer container) (element-type Gvc.MixerSourceOutput):
490
gvc_mixer_control_get_source_outputs (GvcMixerControl *control)
494
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
497
g_hash_table_foreach (control->priv->source_outputs,
498
listify_hash_values_hfunc,
500
return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
504
dec_outstanding (GvcMixerControl *control)
506
if (control->priv->n_outstanding <= 0) {
510
if (--control->priv->n_outstanding <= 0) {
511
g_signal_emit (G_OBJECT (control), signals[READY], 0);
516
gvc_mixer_control_is_ready (GvcMixerControl *control)
518
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
520
return (control->priv->n_outstanding == 0);
525
_set_default_source (GvcMixerControl *control,
526
GvcMixerStream *stream)
530
if (stream == NULL) {
531
control->priv->default_source_id = 0;
532
control->priv->default_source_is_set = FALSE;
533
g_signal_emit (control,
534
signals[DEFAULT_SOURCE_CHANGED],
540
new_id = gvc_mixer_stream_get_id (stream);
542
if (control->priv->default_source_id != new_id) {
543
control->priv->default_source_id = new_id;
544
control->priv->default_source_is_set = TRUE;
545
g_signal_emit (control,
546
signals[DEFAULT_SOURCE_CHANGED],
553
_set_default_sink (GvcMixerControl *control,
554
GvcMixerStream *stream)
558
if (stream == NULL) {
559
/* Don't tell front-ends about an unset default
560
* sink if it's already unset */
561
if (control->priv->default_sink_is_set == FALSE)
563
control->priv->default_sink_id = 0;
564
control->priv->default_sink_is_set = FALSE;
565
g_signal_emit (control,
566
signals[DEFAULT_SINK_CHANGED],
572
new_id = gvc_mixer_stream_get_id (stream);
574
if (control->priv->default_sink_id != new_id) {
575
control->priv->default_sink_id = new_id;
576
control->priv->default_sink_is_set = TRUE;
577
g_signal_emit (control,
578
signals[DEFAULT_SINK_CHANGED],
585
_stream_has_name (gpointer key,
586
GvcMixerStream *stream,
591
t_name = gvc_mixer_stream_get_name (stream);
595
&& strcmp (t_name, name) == 0) {
602
static GvcMixerStream *
603
find_stream_for_name (GvcMixerControl *control,
606
GvcMixerStream *stream;
608
stream = g_hash_table_find (control->priv->all_streams,
609
(GHRFunc)_stream_has_name,
615
update_default_source_from_name (GvcMixerControl *control,
618
gboolean changed = FALSE;
620
if ((control->priv->default_source_name == NULL
622
|| (control->priv->default_source_name != NULL
624
|| (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) {
629
GvcMixerStream *stream;
631
g_free (control->priv->default_source_name);
632
control->priv->default_source_name = g_strdup (name);
634
stream = find_stream_for_name (control, name);
635
_set_default_source (control, stream);
640
update_default_sink_from_name (GvcMixerControl *control,
643
gboolean changed = FALSE;
645
if ((control->priv->default_sink_name == NULL
647
|| (control->priv->default_sink_name != NULL
649
|| (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) {
654
GvcMixerStream *stream;
655
g_free (control->priv->default_sink_name);
656
control->priv->default_sink_name = g_strdup (name);
658
stream = find_stream_for_name (control, name);
659
_set_default_sink (control, stream);
664
update_server (GvcMixerControl *control,
665
const pa_server_info *info)
667
if (info->default_source_name != NULL) {
668
update_default_source_from_name (control, info->default_source_name);
670
if (info->default_sink_name != NULL) {
671
update_default_sink_from_name (control, info->default_sink_name);
676
remove_stream (GvcMixerControl *control,
677
GvcMixerStream *stream)
681
g_object_ref (stream);
683
id = gvc_mixer_stream_get_id (stream);
685
if (id == control->priv->default_sink_id) {
686
_set_default_sink (control, NULL);
687
} else if (id == control->priv->default_source_id) {
688
_set_default_source (control, NULL);
691
g_hash_table_remove (control->priv->all_streams,
692
GUINT_TO_POINTER (id));
693
g_signal_emit (G_OBJECT (control),
694
signals[STREAM_REMOVED],
696
gvc_mixer_stream_get_id (stream));
697
g_object_unref (stream);
701
add_stream (GvcMixerControl *control,
702
GvcMixerStream *stream)
704
g_hash_table_insert (control->priv->all_streams,
705
GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
707
g_signal_emit (G_OBJECT (control),
708
signals[STREAM_ADDED],
710
gvc_mixer_stream_get_id (stream));
714
set_icon_name_from_proplist (GvcMixerStream *stream,
716
const char *default_icon_name)
720
if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) {
724
if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
728
if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
732
if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
736
if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
738
if (strcmp (t, "video") == 0 ||
739
strcmp (t, "phone") == 0) {
743
if (strcmp (t, "music") == 0) {
748
if (strcmp (t, "game") == 0) {
749
t = "applications-games";
753
if (strcmp (t, "event") == 0) {
754
t = "dialog-information";
759
t = default_icon_name;
762
gvc_mixer_stream_set_icon_name (stream, t);
766
update_sink (GvcMixerControl *control,
767
const pa_sink_info *info)
769
GvcMixerStream *stream;
771
pa_volume_t max_volume;
773
char map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
775
pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
777
g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'",
786
stream = g_hash_table_lookup (control->priv->sinks,
787
GUINT_TO_POINTER (info->index));
788
if (stream == NULL) {
792
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
793
stream = gvc_mixer_sink_new (control->priv->pa_context,
797
for (i = 0; i < info->n_ports; i++) {
798
GvcMixerStreamPort *port;
800
port = g_new0 (GvcMixerStreamPort, 1);
801
port->port = g_strdup (info->ports[i]->name);
802
port->human_port = g_strdup (info->ports[i]->description);
803
port->priority = info->ports[i]->priority;
804
list = g_list_prepend (list, port);
806
gvc_mixer_stream_set_ports (stream, list);
808
g_object_unref (map);
810
} else if (gvc_mixer_stream_is_running (stream)) {
811
/* Ignore events if volume changes are outstanding */
812
g_debug ("Ignoring event, volume changes are outstanding");
816
max_volume = pa_cvolume_max (&info->volume);
817
gvc_mixer_stream_set_name (stream, info->name);
818
gvc_mixer_stream_set_card_index (stream, info->card);
819
gvc_mixer_stream_set_description (stream, info->description);
820
set_icon_name_from_proplist (stream, info->proplist, "audio-card");
821
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
822
gvc_mixer_stream_set_is_muted (stream, info->mute);
823
gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
824
gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
826
if (info->active_port != NULL)
827
gvc_mixer_stream_set_port (stream, info->active_port->name);
830
g_hash_table_insert (control->priv->sinks,
831
GUINT_TO_POINTER (info->index),
832
g_object_ref (stream));
833
add_stream (control, stream);
836
if (control->priv->default_sink_name != NULL
837
&& info->name != NULL
838
&& strcmp (control->priv->default_sink_name, info->name) == 0) {
839
_set_default_sink (control, stream);
843
map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
844
gvc_channel_map_volume_changed (map, &info->volume, FALSE);
848
update_source (GvcMixerControl *control,
849
const pa_source_info *info)
851
GvcMixerStream *stream;
853
pa_volume_t max_volume;
856
g_debug ("Updating source: index=%u name='%s' description='%s'",
862
/* completely ignore monitors, they're not real sources */
863
if (info->monitor_of_sink != PA_INVALID_INDEX) {
869
stream = g_hash_table_lookup (control->priv->sources,
870
GUINT_TO_POINTER (info->index));
871
if (stream == NULL) {
876
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
877
stream = gvc_mixer_source_new (control->priv->pa_context,
881
for (i = 0; i < info->n_ports; i++) {
882
GvcMixerStreamPort *port;
884
port = g_new0 (GvcMixerStreamPort, 1);
885
port->port = g_strdup (info->ports[i]->name);
886
port->human_port = g_strdup (info->ports[i]->description);
887
port->priority = info->ports[i]->priority;
888
list = g_list_prepend (list, port);
890
gvc_mixer_stream_set_ports (stream, list);
892
g_object_unref (map);
894
} else if (gvc_mixer_stream_is_running (stream)) {
895
/* Ignore events if volume changes are outstanding */
896
g_debug ("Ignoring event, volume changes are outstanding");
900
max_volume = pa_cvolume_max (&info->volume);
902
gvc_mixer_stream_set_name (stream, info->name);
903
gvc_mixer_stream_set_card_index (stream, info->card);
904
gvc_mixer_stream_set_description (stream, info->description);
905
set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
906
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
907
gvc_mixer_stream_set_is_muted (stream, info->mute);
908
gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
909
gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
911
if (info->active_port != NULL)
912
gvc_mixer_stream_set_port (stream, info->active_port->name);
915
g_hash_table_insert (control->priv->sources,
916
GUINT_TO_POINTER (info->index),
917
g_object_ref (stream));
918
add_stream (control, stream);
921
if (control->priv->default_source_name != NULL
922
&& info->name != NULL
923
&& strcmp (control->priv->default_source_name, info->name) == 0) {
924
_set_default_source (control, stream);
929
set_is_event_stream_from_proplist (GvcMixerStream *stream,
933
gboolean is_event_stream;
935
is_event_stream = FALSE;
937
if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
938
if (g_str_equal (t, "event"))
939
is_event_stream = TRUE;
942
gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
946
set_application_id_from_proplist (GvcMixerStream *stream,
951
if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
952
gvc_mixer_stream_set_application_id (stream, t);
957
update_sink_input (GvcMixerControl *control,
958
const pa_sink_input_info *info)
960
GvcMixerStream *stream;
962
pa_volume_t max_volume;
966
g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u",
975
stream = g_hash_table_lookup (control->priv->sink_inputs,
976
GUINT_TO_POINTER (info->index));
977
if (stream == NULL) {
979
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
980
stream = gvc_mixer_sink_input_new (control->priv->pa_context,
983
g_object_unref (map);
985
} else if (gvc_mixer_stream_is_running (stream)) {
986
/* Ignore events if volume changes are outstanding */
987
g_debug ("Ignoring event, volume changes are outstanding");
991
max_volume = pa_cvolume_max (&info->volume);
993
name = (const char *)g_hash_table_lookup (control->priv->clients,
994
GUINT_TO_POINTER (info->client));
995
gvc_mixer_stream_set_name (stream, name);
996
gvc_mixer_stream_set_description (stream, info->name);
998
set_application_id_from_proplist (stream, info->proplist);
999
set_is_event_stream_from_proplist (stream, info->proplist);
1000
set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
1001
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
1002
gvc_mixer_stream_set_is_muted (stream, info->mute);
1003
gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
1006
g_hash_table_insert (control->priv->sink_inputs,
1007
GUINT_TO_POINTER (info->index),
1008
g_object_ref (stream));
1009
add_stream (control, stream);
1014
update_source_output (GvcMixerControl *control,
1015
const pa_source_output_info *info)
1017
GvcMixerStream *stream;
1022
g_debug ("Updating source output: index=%u name='%s' client=%u source=%u",
1030
stream = g_hash_table_lookup (control->priv->source_outputs,
1031
GUINT_TO_POINTER (info->index));
1032
if (stream == NULL) {
1034
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
1035
stream = gvc_mixer_source_output_new (control->priv->pa_context,
1038
g_object_unref (map);
1042
name = (const char *)g_hash_table_lookup (control->priv->clients,
1043
GUINT_TO_POINTER (info->client));
1045
gvc_mixer_stream_set_name (stream, name);
1046
gvc_mixer_stream_set_description (stream, info->name);
1047
set_application_id_from_proplist (stream, info->proplist);
1048
set_is_event_stream_from_proplist (stream, info->proplist);
1049
set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
1052
g_hash_table_insert (control->priv->source_outputs,
1053
GUINT_TO_POINTER (info->index),
1054
g_object_ref (stream));
1055
add_stream (control, stream);
1060
update_client (GvcMixerControl *control,
1061
const pa_client_info *info)
1064
g_debug ("Updating client: index=%u name='%s'",
1068
g_hash_table_insert (control->priv->clients,
1069
GUINT_TO_POINTER (info->index),
1070
g_strdup (info->name));
1074
card_num_streams_to_status (guint sinks,
1081
if (sinks == 0 && sources == 0) {
1083
* The device has been disabled */
1084
return g_strdup (_("Disabled"));
1090
* The number of sound outputs on a particular device */
1091
sinks_str = g_strdup_printf (ngettext ("%u Output",
1100
* The number of sound inputs on a particular device */
1101
sources_str = g_strdup_printf (ngettext ("%u Input",
1106
if (sources_str == NULL)
1108
if (sinks_str == NULL)
1110
ret = g_strdup_printf ("%s / %s", sinks_str, sources_str);
1112
g_free (sources_str);
1117
update_card (GvcMixerControl *control,
1118
const pa_card_info *info)
1121
gboolean is_new = FALSE;
1127
g_debug ("Udpating card %s (index: %u driver: %s):",
1128
info->name, info->index, info->driver);
1130
for (i = 0; i < info->n_profiles; i++) {
1131
struct pa_card_profile_info pi = info->profiles[i];
1132
gboolean is_default;
1134
is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0);
1135
g_debug ("\tProfile '%s': %d sources %d sinks%s",
1136
pi.name, pi.n_sources, pi.n_sinks,
1137
is_default ? " (Current)" : "");
1140
key = pa_proplist_iterate (info->proplist, &state);
1141
while (key != NULL) {
1142
g_debug ("\tProperty: '%s' = '%s'",
1143
key, pa_proplist_gets (info->proplist, key));
1144
key = pa_proplist_iterate (info->proplist, &state);
1147
card = g_hash_table_lookup (control->priv->cards,
1148
GUINT_TO_POINTER (info->index));
1152
for (i = 0; i < info->n_profiles; i++) {
1153
struct pa_card_profile_info pi = info->profiles[i];
1154
GvcMixerCardProfile *profile;
1156
profile = g_new0 (GvcMixerCardProfile, 1);
1157
profile->profile = g_strdup (pi.name);
1158
profile->human_profile = g_strdup (pi.description);
1159
profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources);
1160
profile->n_sinks = pi.n_sinks;
1161
profile->n_sources = pi.n_sources;
1162
profile->priority = pi.priority;
1163
list = g_list_prepend (list, profile);
1165
card = gvc_mixer_card_new (control->priv->pa_context,
1167
gvc_mixer_card_set_profiles (card, list);
1171
gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description"));
1172
gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name"));
1173
gvc_mixer_card_set_profile (card, info->active_profile->name);
1176
g_hash_table_insert (control->priv->cards,
1177
GUINT_TO_POINTER (info->index),
1178
g_object_ref (card));
1180
g_signal_emit (G_OBJECT (control),
1181
signals[CARD_ADDED],
1187
_pa_context_get_sink_info_cb (pa_context *context,
1188
const pa_sink_info *i,
1192
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1195
if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1199
g_warning ("Sink callback failure");
1204
dec_outstanding (control);
1208
update_sink (control, i);
1212
_pa_context_get_source_info_cb (pa_context *context,
1213
const pa_source_info *i,
1217
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1220
if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1224
g_warning ("Source callback failure");
1229
dec_outstanding (control);
1233
update_source (control, i);
1237
_pa_context_get_sink_input_info_cb (pa_context *context,
1238
const pa_sink_input_info *i,
1242
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1245
if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1249
g_warning ("Sink input callback failure");
1254
dec_outstanding (control);
1258
update_sink_input (control, i);
1262
_pa_context_get_source_output_info_cb (pa_context *context,
1263
const pa_source_output_info *i,
1267
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1270
if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1274
g_warning ("Source output callback failure");
1279
dec_outstanding (control);
1283
update_source_output (control, i);
1287
_pa_context_get_client_info_cb (pa_context *context,
1288
const pa_client_info *i,
1292
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1295
if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1299
g_warning ("Client callback failure");
1304
dec_outstanding (control);
1308
update_client (control, i);
1312
_pa_context_get_card_info_by_index_cb (pa_context *context,
1313
const pa_card_info *i,
1317
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1320
if (pa_context_errno (context) == PA_ERR_NOENTITY)
1323
g_warning ("Card callback failure");
1328
dec_outstanding (control);
1332
update_card (control, i);
1336
_pa_context_get_server_info_cb (pa_context *context,
1337
const pa_server_info *i,
1340
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1343
g_warning ("Server info callback failure");
1347
update_server (control, i);
1348
dec_outstanding (control);
1352
remove_event_role_stream (GvcMixerControl *control)
1354
g_debug ("Removing event role");
1358
update_event_role_stream (GvcMixerControl *control,
1359
const pa_ext_stream_restore_info *info)
1361
GvcMixerStream *stream;
1363
pa_volume_t max_volume;
1365
if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
1370
g_debug ("Updating event role: name='%s' device='%s'",
1377
if (!control->priv->event_sink_input_is_set) {
1378
pa_channel_map pa_map;
1381
pa_map.channels = 1;
1382
pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
1383
map = gvc_channel_map_new_from_pa_channel_map (&pa_map);
1385
stream = gvc_mixer_event_role_new (control->priv->pa_context,
1388
control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
1389
control->priv->event_sink_input_is_set = TRUE;
1393
stream = g_hash_table_lookup (control->priv->all_streams,
1394
GUINT_TO_POINTER (control->priv->event_sink_input_id));
1397
max_volume = pa_cvolume_max (&info->volume);
1399
gvc_mixer_stream_set_name (stream, _("System Sounds"));
1400
gvc_mixer_stream_set_icon_name (stream, "multimedia-volume-control");
1401
gvc_mixer_stream_set_volume (stream, (guint)max_volume);
1402
gvc_mixer_stream_set_is_muted (stream, info->mute);
1405
add_stream (control, stream);
1410
_pa_ext_stream_restore_read_cb (pa_context *context,
1411
const pa_ext_stream_restore_info *i,
1415
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1418
g_debug ("Failed to initialized stream_restore extension: %s",
1419
pa_strerror (pa_context_errno (context)));
1420
remove_event_role_stream (control);
1425
dec_outstanding (control);
1426
/* If we don't have an event stream to restore, then
1427
* set one up with a default 100% volume */
1428
if (!control->priv->event_sink_input_is_set) {
1429
pa_ext_stream_restore_info info;
1431
memset (&info, 0, sizeof(info));
1432
info.name = "sink-input-by-media-role:event";
1433
info.volume.channels = 1;
1434
info.volume.values[0] = PA_VOLUME_NORM;
1435
update_event_role_stream (control, &info);
1440
update_event_role_stream (control, i);
1444
_pa_ext_stream_restore_subscribe_cb (pa_context *context,
1447
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1450
o = pa_ext_stream_restore_read (context,
1451
_pa_ext_stream_restore_read_cb,
1454
g_warning ("pa_ext_stream_restore_read() failed");
1458
pa_operation_unref (o);
1462
req_update_server_info (GvcMixerControl *control,
1467
o = pa_context_get_server_info (control->priv->pa_context,
1468
_pa_context_get_server_info_cb,
1471
g_warning ("pa_context_get_server_info() failed");
1474
pa_operation_unref (o);
1478
req_update_client_info (GvcMixerControl *control,
1484
o = pa_context_get_client_info_list (control->priv->pa_context,
1485
_pa_context_get_client_info_cb,
1488
o = pa_context_get_client_info (control->priv->pa_context,
1490
_pa_context_get_client_info_cb,
1495
g_warning ("pa_context_client_info_list() failed");
1498
pa_operation_unref (o);
1502
req_update_card (GvcMixerControl *control,
1508
o = pa_context_get_card_info_list (control->priv->pa_context,
1509
_pa_context_get_card_info_by_index_cb,
1512
o = pa_context_get_card_info_by_index (control->priv->pa_context,
1514
_pa_context_get_card_info_by_index_cb,
1519
g_warning ("pa_context_get_card_info_by_index() failed");
1522
pa_operation_unref (o);
1526
req_update_sink_info (GvcMixerControl *control,
1532
o = pa_context_get_sink_info_list (control->priv->pa_context,
1533
_pa_context_get_sink_info_cb,
1536
o = pa_context_get_sink_info_by_index (control->priv->pa_context,
1538
_pa_context_get_sink_info_cb,
1543
g_warning ("pa_context_get_sink_info_list() failed");
1546
pa_operation_unref (o);
1550
req_update_source_info (GvcMixerControl *control,
1556
o = pa_context_get_source_info_list (control->priv->pa_context,
1557
_pa_context_get_source_info_cb,
1560
o = pa_context_get_source_info_by_index(control->priv->pa_context,
1562
_pa_context_get_source_info_cb,
1567
g_warning ("pa_context_get_source_info_list() failed");
1570
pa_operation_unref (o);
1574
req_update_sink_input_info (GvcMixerControl *control,
1580
o = pa_context_get_sink_input_info_list (control->priv->pa_context,
1581
_pa_context_get_sink_input_info_cb,
1584
o = pa_context_get_sink_input_info (control->priv->pa_context,
1586
_pa_context_get_sink_input_info_cb,
1591
g_warning ("pa_context_get_sink_input_info_list() failed");
1594
pa_operation_unref (o);
1598
req_update_source_output_info (GvcMixerControl *control,
1604
o = pa_context_get_source_output_info_list (control->priv->pa_context,
1605
_pa_context_get_source_output_info_cb,
1608
o = pa_context_get_source_output_info (control->priv->pa_context,
1610
_pa_context_get_source_output_info_cb,
1615
g_warning ("pa_context_get_source_output_info_list() failed");
1618
pa_operation_unref (o);
1622
remove_client (GvcMixerControl *control,
1625
g_hash_table_remove (control->priv->clients,
1626
GUINT_TO_POINTER (index));
1630
remove_card (GvcMixerControl *control,
1633
g_hash_table_remove (control->priv->cards,
1634
GUINT_TO_POINTER (index));
1636
g_signal_emit (G_OBJECT (control),
1637
signals[CARD_REMOVED],
1643
remove_sink (GvcMixerControl *control,
1646
GvcMixerStream *stream;
1649
g_debug ("Removing sink: index=%u", index);
1652
stream = g_hash_table_lookup (control->priv->sinks,
1653
GUINT_TO_POINTER (index));
1654
if (stream == NULL) {
1657
g_hash_table_remove (control->priv->sinks,
1658
GUINT_TO_POINTER (index));
1660
remove_stream (control, stream);
1664
remove_source (GvcMixerControl *control,
1667
GvcMixerStream *stream;
1670
g_debug ("Removing source: index=%u", index);
1673
stream = g_hash_table_lookup (control->priv->sources,
1674
GUINT_TO_POINTER (index));
1675
if (stream == NULL) {
1678
g_hash_table_remove (control->priv->sources,
1679
GUINT_TO_POINTER (index));
1681
remove_stream (control, stream);
1685
remove_sink_input (GvcMixerControl *control,
1688
GvcMixerStream *stream;
1691
g_debug ("Removing sink input: index=%u", index);
1693
stream = g_hash_table_lookup (control->priv->sink_inputs,
1694
GUINT_TO_POINTER (index));
1695
if (stream == NULL) {
1698
g_hash_table_remove (control->priv->sink_inputs,
1699
GUINT_TO_POINTER (index));
1701
remove_stream (control, stream);
1705
remove_source_output (GvcMixerControl *control,
1708
GvcMixerStream *stream;
1711
g_debug ("Removing source output: index=%u", index);
1714
stream = g_hash_table_lookup (control->priv->source_outputs,
1715
GUINT_TO_POINTER (index));
1716
if (stream == NULL) {
1719
g_hash_table_remove (control->priv->source_outputs,
1720
GUINT_TO_POINTER (index));
1722
remove_stream (control, stream);
1726
_pa_context_subscribe_cb (pa_context *context,
1727
pa_subscription_event_type_t t,
1731
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1733
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
1734
case PA_SUBSCRIPTION_EVENT_SINK:
1735
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1736
remove_sink (control, index);
1738
req_update_sink_info (control, index);
1742
case PA_SUBSCRIPTION_EVENT_SOURCE:
1743
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1744
remove_source (control, index);
1746
req_update_source_info (control, index);
1750
case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
1751
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1752
remove_sink_input (control, index);
1754
req_update_sink_input_info (control, index);
1758
case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
1759
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1760
remove_source_output (control, index);
1762
req_update_source_output_info (control, index);
1766
case PA_SUBSCRIPTION_EVENT_CLIENT:
1767
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1768
remove_client (control, index);
1770
req_update_client_info (control, index);
1774
case PA_SUBSCRIPTION_EVENT_SERVER:
1775
req_update_server_info (control, index);
1778
case PA_SUBSCRIPTION_EVENT_CARD:
1779
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1780
remove_card (control, index);
1782
req_update_card (control, index);
1789
gvc_mixer_control_ready (GvcMixerControl *control)
1793
pa_context_set_subscribe_callback (control->priv->pa_context,
1794
_pa_context_subscribe_cb,
1796
o = pa_context_subscribe (control->priv->pa_context,
1797
(pa_subscription_mask_t)
1798
(PA_SUBSCRIPTION_MASK_SINK|
1799
PA_SUBSCRIPTION_MASK_SOURCE|
1800
PA_SUBSCRIPTION_MASK_SINK_INPUT|
1801
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1802
PA_SUBSCRIPTION_MASK_CLIENT|
1803
PA_SUBSCRIPTION_MASK_SERVER|
1804
PA_SUBSCRIPTION_MASK_CARD),
1809
g_warning ("pa_context_subscribe() failed");
1812
pa_operation_unref (o);
1814
req_update_server_info (control, -1);
1815
req_update_client_info (control, -1);
1816
req_update_sink_info (control, -1);
1817
req_update_source_info (control, -1);
1818
req_update_sink_input_info (control, -1);
1819
req_update_source_output_info (control, -1);
1820
req_update_card (control, -1);
1822
control->priv->n_outstanding = 6;
1824
/* This call is not always supported */
1825
o = pa_ext_stream_restore_read (control->priv->pa_context,
1826
_pa_ext_stream_restore_read_cb,
1829
pa_operation_unref (o);
1830
control->priv->n_outstanding++;
1832
pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
1833
_pa_ext_stream_restore_subscribe_cb,
1836
o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
1841
pa_operation_unref (o);
1845
g_debug ("Failed to initialized stream_restore extension: %s",
1846
pa_strerror (pa_context_errno (control->priv->pa_context)));
1851
gvc_mixer_new_pa_context (GvcMixerControl *self)
1853
pa_proplist *proplist;
1855
g_return_if_fail (self);
1856
g_return_if_fail (!self->priv->pa_context);
1858
proplist = pa_proplist_new ();
1859
pa_proplist_sets (proplist,
1860
PA_PROP_APPLICATION_NAME,
1862
pa_proplist_sets (proplist,
1863
PA_PROP_APPLICATION_ID,
1864
"org.gnome.VolumeControl");
1865
pa_proplist_sets (proplist,
1866
PA_PROP_APPLICATION_ICON_NAME,
1867
"multimedia-volume-control");
1868
pa_proplist_sets (proplist,
1869
PA_PROP_APPLICATION_VERSION,
1872
self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
1874
pa_proplist_free (proplist);
1875
g_assert (self->priv->pa_context);
1879
remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
1881
GHashTableIter iter;
1882
gpointer key, value;
1884
g_hash_table_iter_init (&iter, hash_table);
1885
while (g_hash_table_iter_next (&iter, &key, &value)) {
1886
remove_stream (control, value);
1887
g_hash_table_iter_remove (&iter);
1892
idle_reconnect (gpointer data)
1894
GvcMixerControl *control = GVC_MIXER_CONTROL (data);
1895
GHashTableIter iter;
1896
gpointer key, value;
1898
g_return_val_if_fail (control, FALSE);
1900
if (control->priv->pa_context) {
1901
pa_context_unref (control->priv->pa_context);
1902
control->priv->pa_context = NULL;
1903
gvc_mixer_new_pa_context (control);
1906
remove_all_streams (control, control->priv->sinks);
1907
remove_all_streams (control, control->priv->sources);
1908
remove_all_streams (control, control->priv->sink_inputs);
1909
remove_all_streams (control, control->priv->source_outputs);
1911
g_hash_table_iter_init (&iter, control->priv->clients);
1912
while (g_hash_table_iter_next (&iter, &key, &value))
1913
g_hash_table_iter_remove (&iter);
1915
gvc_mixer_control_open (control); /* cannot fail */
1917
control->priv->reconnect_id = 0;
1922
_pa_context_state_cb (pa_context *context,
1925
GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1927
switch (pa_context_get_state (context)) {
1928
case PA_CONTEXT_UNCONNECTED:
1929
case PA_CONTEXT_CONNECTING:
1930
case PA_CONTEXT_AUTHORIZING:
1931
case PA_CONTEXT_SETTING_NAME:
1934
case PA_CONTEXT_READY:
1935
gvc_mixer_control_ready (control);
1938
case PA_CONTEXT_FAILED:
1939
g_warning ("Connection failed, reconnecting...");
1940
if (control->priv->reconnect_id == 0)
1941
control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
1944
case PA_CONTEXT_TERMINATED:
1952
gvc_mixer_control_open (GvcMixerControl *control)
1956
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
1957
g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
1958
g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE);
1960
pa_context_set_state_callback (control->priv->pa_context,
1961
_pa_context_state_cb,
1964
g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
1965
res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
1967
g_warning ("Failed to connect context: %s",
1968
pa_strerror (pa_context_errno (control->priv->pa_context)));
1975
gvc_mixer_control_close (GvcMixerControl *control)
1977
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
1978
g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
1980
pa_context_disconnect (control->priv->pa_context);
1985
gvc_mixer_control_dispose (GObject *object)
1987
GvcMixerControl *control = GVC_MIXER_CONTROL (object);
1989
if (control->priv->reconnect_id != 0) {
1990
g_source_remove (control->priv->reconnect_id);
1991
control->priv->reconnect_id = 0;
1994
if (control->priv->pa_context != NULL) {
1995
pa_context_unref (control->priv->pa_context);
1996
control->priv->pa_context = NULL;
1999
if (control->priv->default_source_name != NULL) {
2000
g_free (control->priv->default_source_name);
2001
control->priv->default_source_name = NULL;
2003
if (control->priv->default_sink_name != NULL) {
2004
g_free (control->priv->default_sink_name);
2005
control->priv->default_sink_name = NULL;
2008
if (control->priv->pa_mainloop != NULL) {
2009
pa_glib_mainloop_free (control->priv->pa_mainloop);
2010
control->priv->pa_mainloop = NULL;
2013
if (control->priv->all_streams != NULL) {
2014
g_hash_table_destroy (control->priv->all_streams);
2015
control->priv->all_streams = NULL;
2018
if (control->priv->sinks != NULL) {
2019
g_hash_table_destroy (control->priv->sinks);
2020
control->priv->sinks = NULL;
2022
if (control->priv->sources != NULL) {
2023
g_hash_table_destroy (control->priv->sources);
2024
control->priv->sources = NULL;
2026
if (control->priv->sink_inputs != NULL) {
2027
g_hash_table_destroy (control->priv->sink_inputs);
2028
control->priv->sink_inputs = NULL;
2030
if (control->priv->source_outputs != NULL) {
2031
g_hash_table_destroy (control->priv->source_outputs);
2032
control->priv->source_outputs = NULL;
2034
if (control->priv->clients != NULL) {
2035
g_hash_table_destroy (control->priv->clients);
2036
control->priv->clients = NULL;
2038
if (control->priv->cards != NULL) {
2039
g_hash_table_destroy (control->priv->cards);
2040
control->priv->cards = NULL;
2043
G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
2047
gvc_mixer_control_set_property (GObject *object,
2049
const GValue *value,
2052
GvcMixerControl *self = GVC_MIXER_CONTROL (object);
2056
g_free (self->priv->name);
2057
self->priv->name = g_value_dup_string (value);
2058
g_object_notify (G_OBJECT (self), "name");
2061
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2067
gvc_mixer_control_get_property (GObject *object,
2072
GvcMixerControl *self = GVC_MIXER_CONTROL (object);
2076
g_value_set_string (value, self->priv->name);
2079
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2086
gvc_mixer_control_constructor (GType type,
2087
guint n_construct_properties,
2088
GObjectConstructParam *construct_params)
2091
GvcMixerControl *self;
2093
object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
2095
self = GVC_MIXER_CONTROL (object);
2097
gvc_mixer_new_pa_context (self);
2103
gvc_mixer_control_class_init (GvcMixerControlClass *klass)
2105
GObjectClass *object_class = G_OBJECT_CLASS (klass);
2107
object_class->constructor = gvc_mixer_control_constructor;
2108
object_class->dispose = gvc_mixer_control_dispose;
2109
object_class->finalize = gvc_mixer_control_finalize;
2110
object_class->set_property = gvc_mixer_control_set_property;
2111
object_class->get_property = gvc_mixer_control_get_property;
2113
g_object_class_install_property (object_class,
2115
g_param_spec_string ("name",
2117
"Name to display for this mixer control",
2119
G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
2121
signals [CONNECTING] =
2122
g_signal_new ("connecting",
2123
G_TYPE_FROM_CLASS (klass),
2125
G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
2127
g_cclosure_marshal_VOID__VOID,
2130
g_signal_new ("ready",
2131
G_TYPE_FROM_CLASS (klass),
2133
G_STRUCT_OFFSET (GvcMixerControlClass, ready),
2135
g_cclosure_marshal_VOID__VOID,
2137
signals [STREAM_ADDED] =
2138
g_signal_new ("stream-added",
2139
G_TYPE_FROM_CLASS (klass),
2141
G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
2143
g_cclosure_marshal_VOID__UINT,
2144
G_TYPE_NONE, 1, G_TYPE_UINT);
2145
signals [STREAM_REMOVED] =
2146
g_signal_new ("stream-removed",
2147
G_TYPE_FROM_CLASS (klass),
2149
G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
2151
g_cclosure_marshal_VOID__UINT,
2152
G_TYPE_NONE, 1, G_TYPE_UINT);
2153
signals [CARD_ADDED] =
2154
g_signal_new ("card-added",
2155
G_TYPE_FROM_CLASS (klass),
2157
G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
2159
g_cclosure_marshal_VOID__UINT,
2160
G_TYPE_NONE, 1, G_TYPE_UINT);
2161
signals [CARD_REMOVED] =
2162
g_signal_new ("card-removed",
2163
G_TYPE_FROM_CLASS (klass),
2165
G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
2167
g_cclosure_marshal_VOID__UINT,
2168
G_TYPE_NONE, 1, G_TYPE_UINT);
2169
signals [DEFAULT_SINK_CHANGED] =
2170
g_signal_new ("default-sink-changed",
2171
G_TYPE_FROM_CLASS (klass),
2173
G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
2175
g_cclosure_marshal_VOID__UINT,
2176
G_TYPE_NONE, 1, G_TYPE_UINT);
2177
signals [DEFAULT_SOURCE_CHANGED] =
2178
g_signal_new ("default-source-changed",
2179
G_TYPE_FROM_CLASS (klass),
2181
G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
2183
g_cclosure_marshal_VOID__UINT,
2184
G_TYPE_NONE, 1, G_TYPE_UINT);
2186
g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
2190
gvc_mixer_control_init (GvcMixerControl *control)
2192
control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control);
2194
control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
2195
g_assert (control->priv->pa_mainloop);
2197
control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
2198
g_assert (control->priv->pa_api);
2200
control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2201
control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2202
control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2203
control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2204
control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2205
control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2207
control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
2211
gvc_mixer_control_finalize (GObject *object)
2213
GvcMixerControl *mixer_control;
2215
g_return_if_fail (object != NULL);
2216
g_return_if_fail (GVC_IS_MIXER_CONTROL (object));
2218
mixer_control = GVC_MIXER_CONTROL (object);
2219
g_free (mixer_control->priv->name);
2220
mixer_control->priv->name = NULL;
2222
g_return_if_fail (mixer_control->priv != NULL);
2223
G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
2227
gvc_mixer_control_new (const char *name)
2230
control = g_object_new (GVC_TYPE_MIXER_CONTROL,
2233
return GVC_MIXER_CONTROL (control);
2236
/* FIXME: Remove when PA 0.9.23 is used */
2237
#ifndef PA_VOLUME_UI_MAX
2238
#define PA_VOLUME_UI_MAX pa_sw_volume_from_dB(+11.0)
2242
gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
2244
return (gdouble) PA_VOLUME_NORM;
2248
gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control)
2250
return (gdouble) PA_VOLUME_UI_MAX;