~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to panels/sound/gvc-mixer-control.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2006-2008 Lennart Poettering
 
4
 * Copyright (C) 2008 Sjoerd Simons <sjoerd@luon.net>
 
5
 * Copyright (C) 2008 William Jon McCann
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 *
 
21
 */
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#include <stdlib.h>
 
26
#include <stdio.h>
 
27
#include <unistd.h>
 
28
 
 
29
#include <glib.h>
 
30
#include <glib/gi18n-lib.h>
 
31
 
 
32
#include <pulse/pulseaudio.h>
 
33
#include <pulse/glib-mainloop.h>
 
34
#include <pulse/ext-stream-restore.h>
 
35
 
 
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"
 
46
 
 
47
#define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate))
 
48
 
 
49
#define RECONNECT_DELAY 5
 
50
 
 
51
enum {
 
52
        PROP_0,
 
53
        PROP_NAME
 
54
};
 
55
 
 
56
struct GvcMixerControlPrivate
 
57
{
 
58
        pa_glib_mainloop *pa_mainloop;
 
59
        pa_mainloop_api  *pa_api;
 
60
        pa_context       *pa_context;
 
61
        int               n_outstanding;
 
62
        guint             reconnect_id;
 
63
        char             *name;
 
64
 
 
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;
 
71
 
 
72
        gboolean          event_sink_input_is_set;
 
73
        guint             event_sink_input_id;
 
74
 
 
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 */
 
80
        GHashTable       *clients;
 
81
        GHashTable       *cards;
 
82
 
 
83
        GvcMixerStream   *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
 
84
};
 
85
 
 
86
enum {
 
87
        CONNECTING,
 
88
        READY,
 
89
        STREAM_ADDED,
 
90
        STREAM_REMOVED,
 
91
        CARD_ADDED,
 
92
        CARD_REMOVED,
 
93
        DEFAULT_SINK_CHANGED,
 
94
        DEFAULT_SOURCE_CHANGED,
 
95
        LAST_SIGNAL
 
96
};
 
97
 
 
98
static guint signals [LAST_SIGNAL] = { 0, };
 
99
 
 
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);
 
103
 
 
104
G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
 
105
 
 
106
pa_context *
 
107
gvc_mixer_control_get_pa_context (GvcMixerControl *control)
 
108
{
 
109
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
110
        return control->priv->pa_context;
 
111
}
 
112
 
 
113
/**
 
114
 * gvc_mixer_control_get_event_sink_input:
 
115
 *
 
116
 * @control:
 
117
 *
 
118
 * Returns: (transfer none):
 
119
 */
 
120
GvcMixerStream *
 
121
gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
 
122
{
 
123
        GvcMixerStream *stream;
 
124
 
 
125
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
126
 
 
127
        stream = g_hash_table_lookup (control->priv->all_streams,
 
128
                                      GUINT_TO_POINTER (control->priv->event_sink_input_id));
 
129
 
 
130
        return stream;
 
131
}
 
132
 
 
133
static void
 
134
gvc_mixer_control_stream_restore_cb (pa_context *c,
 
135
                                     const pa_ext_stream_restore_info *info,
 
136
                                     int eol,
 
137
                                     void *userdata)
 
138
{
 
139
        pa_operation *o;
 
140
        GvcMixerControl *control = (GvcMixerControl *) userdata;
 
141
        pa_ext_stream_restore_info new_info;
 
142
 
 
143
        if (eol || control->priv->new_default_stream == NULL)
 
144
                return;
 
145
 
 
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;
 
150
 
 
151
        new_info.device = gvc_mixer_stream_get_name (control->priv->new_default_stream);
 
152
 
 
153
        o = pa_ext_stream_restore_write (control->priv->pa_context,
 
154
                                         PA_UPDATE_REPLACE,
 
155
                                         &new_info, 1,
 
156
                                         TRUE, NULL, NULL);
 
157
 
 
158
        if (o == NULL) {
 
159
                g_warning ("pa_ext_stream_restore_write() failed: %s",
 
160
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
 
161
                return;
 
162
        }
 
163
 
 
164
        g_debug ("Changed default device for %s to %s", info->name, info->device);
 
165
 
 
166
        pa_operation_unref (o);
 
167
}
 
168
 
 
169
gboolean
 
170
gvc_mixer_control_set_default_sink (GvcMixerControl *control,
 
171
                                    GvcMixerStream  *stream)
 
172
{
 
173
        pa_operation *o;
 
174
 
 
175
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
 
176
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
177
 
 
178
        o = pa_context_set_default_sink (control->priv->pa_context,
 
179
                                         gvc_mixer_stream_get_name (stream),
 
180
                                         NULL,
 
181
                                         NULL);
 
182
        if (o == NULL) {
 
183
                g_warning ("pa_context_set_default_sink() failed: %s",
 
184
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
 
185
                return FALSE;
 
186
        }
 
187
 
 
188
        pa_operation_unref (o);
 
189
 
 
190
        control->priv->new_default_stream = stream;
 
191
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
 
192
 
 
193
        o = pa_ext_stream_restore_read (control->priv->pa_context,
 
194
                                        gvc_mixer_control_stream_restore_cb,
 
195
                                        control);
 
196
 
 
197
        if (o == NULL) {
 
198
                g_warning ("pa_ext_stream_restore_read() failed: %s",
 
199
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
 
200
                return FALSE;
 
201
        }
 
202
 
 
203
        pa_operation_unref (o);
 
204
 
 
205
        return TRUE;
 
206
}
 
207
 
 
208
gboolean
 
209
gvc_mixer_control_set_default_source (GvcMixerControl *control,
 
210
                                      GvcMixerStream  *stream)
 
211
{
 
212
        pa_operation *o;
 
213
 
 
214
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
 
215
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
216
 
 
217
        o = pa_context_set_default_source (control->priv->pa_context,
 
218
                                           gvc_mixer_stream_get_name (stream),
 
219
                                           NULL,
 
220
                                           NULL);
 
221
        if (o == NULL) {
 
222
                g_warning ("pa_context_set_default_source() failed");
 
223
                return FALSE;
 
224
        }
 
225
 
 
226
        pa_operation_unref (o);
 
227
 
 
228
        return TRUE;
 
229
}
 
230
 
 
231
/**
 
232
 * gvc_mixer_control_get_default_sink:
 
233
 *
 
234
 * @control:
 
235
 *
 
236
 * Returns: (transfer none):
 
237
 */
 
238
GvcMixerStream *
 
239
gvc_mixer_control_get_default_sink (GvcMixerControl *control)
 
240
{
 
241
        GvcMixerStream *stream;
 
242
 
 
243
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
244
 
 
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));
 
248
        } else {
 
249
                stream = NULL;
 
250
        }
 
251
 
 
252
        return stream;
 
253
}
 
254
 
 
255
/**
 
256
 * gvc_mixer_control_get_default_source:
 
257
 *
 
258
 * @control:
 
259
 *
 
260
 * Returns: (transfer none):
 
261
 */
 
262
GvcMixerStream *
 
263
gvc_mixer_control_get_default_source (GvcMixerControl *control)
 
264
{
 
265
        GvcMixerStream *stream;
 
266
 
 
267
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
268
 
 
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));
 
272
        } else {
 
273
                stream = NULL;
 
274
        }
 
275
 
 
276
        return stream;
 
277
}
 
278
 
 
279
static gpointer
 
280
gvc_mixer_control_lookup_id (GHashTable *hash_table,
 
281
                             guint       id)
 
282
{
 
283
        return g_hash_table_lookup (hash_table,
 
284
                                    GUINT_TO_POINTER (id));
 
285
}
 
286
 
 
287
/**
 
288
 * gvc_mixer_control_lookup_stream_id:
 
289
 *
 
290
 * @control:
 
291
 * @id:
 
292
 *
 
293
 * Returns: (transfer none):
 
294
 */
 
295
GvcMixerStream *
 
296
gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
 
297
                                    guint            id)
 
298
{
 
299
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
300
 
 
301
        return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
 
302
}
 
303
 
 
304
/**
 
305
 * gvc_mixer_control_lookup_card_id:
 
306
 *
 
307
 * @control:
 
308
 * @id:
 
309
 *
 
310
 * Returns: (transfer none):
 
311
 */
 
312
GvcMixerCard *
 
313
gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
 
314
                                  guint            id)
 
315
{
 
316
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
317
 
 
318
        return gvc_mixer_control_lookup_id (control->priv->cards, id);
 
319
}
 
320
 
 
321
static void
 
322
listify_hash_values_hfunc (gpointer key,
 
323
                           gpointer value,
 
324
                           gpointer user_data)
 
325
{
 
326
        GSList **list = user_data;
 
327
 
 
328
        *list = g_slist_prepend (*list, value);
 
329
}
 
330
 
 
331
static int
 
332
gvc_name_collate (const char *namea,
 
333
                  const char *nameb)
 
334
{
 
335
        if (nameb == NULL && namea == NULL)
 
336
                return 0;
 
337
        if (nameb == NULL)
 
338
                return 1;
 
339
        if (namea == NULL)
 
340
                return -1;
 
341
 
 
342
        return g_utf8_collate (namea, nameb);
 
343
}
 
344
 
 
345
static int
 
346
gvc_card_collate (GvcMixerCard *a,
 
347
                  GvcMixerCard *b)
 
348
{
 
349
        const char *namea;
 
350
        const char *nameb;
 
351
 
 
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);
 
354
 
 
355
        namea = gvc_mixer_card_get_name (a);
 
356
        nameb = gvc_mixer_card_get_name (b);
 
357
 
 
358
        return gvc_name_collate (namea, nameb);
 
359
}
 
360
 
 
361
/**
 
362
 * gvc_mixer_control_get_cards:
 
363
 *
 
364
 * @control:
 
365
 *
 
366
 * Returns: (transfer container) (element-type Gvc.MixerCard):
 
367
 */
 
368
GSList *
 
369
gvc_mixer_control_get_cards (GvcMixerControl *control)
 
370
{
 
371
        GSList *retval;
 
372
 
 
373
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
374
 
 
375
        retval = NULL;
 
376
        g_hash_table_foreach (control->priv->cards,
 
377
                              listify_hash_values_hfunc,
 
378
                              &retval);
 
379
        return g_slist_sort (retval, (GCompareFunc) gvc_card_collate);
 
380
}
 
381
 
 
382
static int
 
383
gvc_stream_collate (GvcMixerStream *a,
 
384
                    GvcMixerStream *b)
 
385
{
 
386
        const char *namea;
 
387
        const char *nameb;
 
388
 
 
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);
 
391
 
 
392
        namea = gvc_mixer_stream_get_name (a);
 
393
        nameb = gvc_mixer_stream_get_name (b);
 
394
 
 
395
        return gvc_name_collate (namea, nameb);
 
396
}
 
397
 
 
398
/**
 
399
 * gvc_mixer_control_get_streams:
 
400
 *
 
401
 * @control:
 
402
 *
 
403
 * Returns: (transfer container) (element-type Gvc.MixerStream):
 
404
 */
 
405
GSList *
 
406
gvc_mixer_control_get_streams (GvcMixerControl *control)
 
407
{
 
408
        GSList *retval;
 
409
 
 
410
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
411
 
 
412
        retval = NULL;
 
413
        g_hash_table_foreach (control->priv->all_streams,
 
414
                              listify_hash_values_hfunc,
 
415
                              &retval);
 
416
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 
417
}
 
418
 
 
419
/**
 
420
 * gvc_mixer_control_get_sinks:
 
421
 *
 
422
 * @control:
 
423
 *
 
424
 * Returns: (transfer container) (element-type Gvc.MixerSink):
 
425
 */
 
426
GSList *
 
427
gvc_mixer_control_get_sinks (GvcMixerControl *control)
 
428
{
 
429
        GSList *retval;
 
430
 
 
431
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
432
 
 
433
        retval = NULL;
 
434
        g_hash_table_foreach (control->priv->sinks,
 
435
                              listify_hash_values_hfunc,
 
436
                              &retval);
 
437
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 
438
}
 
439
 
 
440
/**
 
441
 * gvc_mixer_control_get_sources:
 
442
 *
 
443
 * @control:
 
444
 *
 
445
 * Returns: (transfer container) (element-type Gvc.MixerSource):
 
446
 */
 
447
GSList *
 
448
gvc_mixer_control_get_sources (GvcMixerControl *control)
 
449
{
 
450
        GSList *retval;
 
451
 
 
452
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
453
 
 
454
        retval = NULL;
 
455
        g_hash_table_foreach (control->priv->sources,
 
456
                              listify_hash_values_hfunc,
 
457
                              &retval);
 
458
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 
459
}
 
460
 
 
461
/**
 
462
 * gvc_mixer_control_get_sink_inputs:
 
463
 *
 
464
 * @control:
 
465
 *
 
466
 * Returns: (transfer container) (element-type Gvc.MixerSinkInput):
 
467
 */
 
468
GSList *
 
469
gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
 
470
{
 
471
        GSList *retval;
 
472
 
 
473
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
474
 
 
475
        retval = NULL;
 
476
        g_hash_table_foreach (control->priv->sink_inputs,
 
477
                              listify_hash_values_hfunc,
 
478
                              &retval);
 
479
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 
480
}
 
481
 
 
482
/**
 
483
 * gvc_mixer_control_get_source_outputs:
 
484
 *
 
485
 * @control:
 
486
 *
 
487
 * Returns: (transfer container) (element-type Gvc.MixerSourceOutput):
 
488
 */
 
489
GSList *
 
490
gvc_mixer_control_get_source_outputs (GvcMixerControl *control)
 
491
{
 
492
        GSList *retval;
 
493
 
 
494
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
495
 
 
496
        retval = NULL;
 
497
        g_hash_table_foreach (control->priv->source_outputs,
 
498
                              listify_hash_values_hfunc,
 
499
                              &retval);
 
500
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 
501
}
 
502
 
 
503
static void
 
504
dec_outstanding (GvcMixerControl *control)
 
505
{
 
506
        if (control->priv->n_outstanding <= 0) {
 
507
                return;
 
508
        }
 
509
 
 
510
        if (--control->priv->n_outstanding <= 0) {
 
511
                g_signal_emit (G_OBJECT (control), signals[READY], 0);
 
512
        }
 
513
}
 
514
 
 
515
gboolean
 
516
gvc_mixer_control_is_ready (GvcMixerControl *control)
 
517
{
 
518
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
 
519
 
 
520
        return (control->priv->n_outstanding == 0);
 
521
}
 
522
 
 
523
 
 
524
static void
 
525
_set_default_source (GvcMixerControl *control,
 
526
                     GvcMixerStream  *stream)
 
527
{
 
528
        guint new_id;
 
529
 
 
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],
 
535
                               0,
 
536
                               PA_INVALID_INDEX);
 
537
                return;
 
538
        }
 
539
 
 
540
        new_id = gvc_mixer_stream_get_id (stream);
 
541
 
 
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],
 
547
                               0,
 
548
                               new_id);
 
549
        }
 
550
}
 
551
 
 
552
static void
 
553
_set_default_sink (GvcMixerControl *control,
 
554
                   GvcMixerStream  *stream)
 
555
{
 
556
        guint new_id;
 
557
 
 
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)
 
562
                        return;
 
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],
 
567
                               0,
 
568
                               PA_INVALID_INDEX);
 
569
                return;
 
570
        }
 
571
 
 
572
        new_id = gvc_mixer_stream_get_id (stream);
 
573
 
 
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],
 
579
                               0,
 
580
                               new_id);
 
581
        }
 
582
}
 
583
 
 
584
static gboolean
 
585
_stream_has_name (gpointer        key,
 
586
                  GvcMixerStream *stream,
 
587
                  const char     *name)
 
588
{
 
589
        const char *t_name;
 
590
 
 
591
        t_name = gvc_mixer_stream_get_name (stream);
 
592
 
 
593
        if (t_name != NULL
 
594
            && name != NULL
 
595
            && strcmp (t_name, name) == 0) {
 
596
                return TRUE;
 
597
        }
 
598
 
 
599
        return FALSE;
 
600
}
 
601
 
 
602
static GvcMixerStream  *
 
603
find_stream_for_name (GvcMixerControl *control,
 
604
                      const char      *name)
 
605
{
 
606
        GvcMixerStream *stream;
 
607
 
 
608
        stream = g_hash_table_find (control->priv->all_streams,
 
609
                                    (GHRFunc)_stream_has_name,
 
610
                                    (char *)name);
 
611
        return stream;
 
612
}
 
613
 
 
614
static void
 
615
update_default_source_from_name (GvcMixerControl *control,
 
616
                                 const char      *name)
 
617
{
 
618
        gboolean changed = FALSE;
 
619
 
 
620
        if ((control->priv->default_source_name == NULL
 
621
             && name != NULL)
 
622
            || (control->priv->default_source_name != NULL
 
623
                && name == NULL)
 
624
            || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) {
 
625
                changed = TRUE;
 
626
        }
 
627
 
 
628
        if (changed) {
 
629
                GvcMixerStream *stream;
 
630
 
 
631
                g_free (control->priv->default_source_name);
 
632
                control->priv->default_source_name = g_strdup (name);
 
633
 
 
634
                stream = find_stream_for_name (control, name);
 
635
                _set_default_source (control, stream);
 
636
        }
 
637
}
 
638
 
 
639
static void
 
640
update_default_sink_from_name (GvcMixerControl *control,
 
641
                               const char      *name)
 
642
{
 
643
        gboolean changed = FALSE;
 
644
 
 
645
        if ((control->priv->default_sink_name == NULL
 
646
             && name != NULL)
 
647
            || (control->priv->default_sink_name != NULL
 
648
                && name == NULL)
 
649
            || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) {
 
650
                changed = TRUE;
 
651
        }
 
652
 
 
653
        if (changed) {
 
654
                GvcMixerStream *stream;
 
655
                g_free (control->priv->default_sink_name);
 
656
                control->priv->default_sink_name = g_strdup (name);
 
657
 
 
658
                stream = find_stream_for_name (control, name);
 
659
                _set_default_sink (control, stream);
 
660
        }
 
661
}
 
662
 
 
663
static void
 
664
update_server (GvcMixerControl      *control,
 
665
               const pa_server_info *info)
 
666
{
 
667
        if (info->default_source_name != NULL) {
 
668
                update_default_source_from_name (control, info->default_source_name);
 
669
        }
 
670
        if (info->default_sink_name != NULL) {
 
671
                update_default_sink_from_name (control, info->default_sink_name);
 
672
        }
 
673
}
 
674
 
 
675
static void
 
676
remove_stream (GvcMixerControl *control,
 
677
               GvcMixerStream  *stream)
 
678
{
 
679
        guint id;
 
680
 
 
681
        g_object_ref (stream);
 
682
 
 
683
        id = gvc_mixer_stream_get_id (stream);
 
684
 
 
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);
 
689
        }
 
690
 
 
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],
 
695
                       0,
 
696
                       gvc_mixer_stream_get_id (stream));
 
697
        g_object_unref (stream);
 
698
}
 
699
 
 
700
static void
 
701
add_stream (GvcMixerControl *control,
 
702
            GvcMixerStream  *stream)
 
703
{
 
704
        g_hash_table_insert (control->priv->all_streams,
 
705
                             GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
 
706
                             stream);
 
707
        g_signal_emit (G_OBJECT (control),
 
708
                       signals[STREAM_ADDED],
 
709
                       0,
 
710
                       gvc_mixer_stream_get_id (stream));
 
711
}
 
712
 
 
713
static void
 
714
set_icon_name_from_proplist (GvcMixerStream *stream,
 
715
                             pa_proplist    *l,
 
716
                             const char     *default_icon_name)
 
717
{
 
718
        const char *t;
 
719
 
 
720
        if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) {
 
721
                goto finish;
 
722
        }
 
723
 
 
724
        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
 
725
                goto finish;
 
726
        }
 
727
 
 
728
        if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
 
729
                goto finish;
 
730
        }
 
731
 
 
732
        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
 
733
                goto finish;
 
734
        }
 
735
 
 
736
        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
 
737
 
 
738
                if (strcmp (t, "video") == 0 ||
 
739
                    strcmp (t, "phone") == 0) {
 
740
                        goto finish;
 
741
                }
 
742
 
 
743
                if (strcmp (t, "music") == 0) {
 
744
                        t = "audio";
 
745
                        goto finish;
 
746
                }
 
747
 
 
748
                if (strcmp (t, "game") == 0) {
 
749
                        t = "applications-games";
 
750
                        goto finish;
 
751
                }
 
752
 
 
753
                if (strcmp (t, "event") == 0) {
 
754
                        t = "dialog-information";
 
755
                        goto finish;
 
756
                }
 
757
        }
 
758
 
 
759
        t = default_icon_name;
 
760
 
 
761
 finish:
 
762
        gvc_mixer_stream_set_icon_name (stream, t);
 
763
}
 
764
 
 
765
static void
 
766
update_sink (GvcMixerControl    *control,
 
767
             const pa_sink_info *info)
 
768
{
 
769
        GvcMixerStream *stream;
 
770
        gboolean        is_new;
 
771
        pa_volume_t     max_volume;
 
772
        GvcChannelMap  *map;
 
773
        char            map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
 
774
 
 
775
        pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
 
776
#if 1
 
777
        g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'",
 
778
                 info->index,
 
779
                 info->name,
 
780
                 info->description,
 
781
                 map_buff);
 
782
#endif
 
783
 
 
784
        map = NULL;
 
785
        is_new = FALSE;
 
786
        stream = g_hash_table_lookup (control->priv->sinks,
 
787
                                      GUINT_TO_POINTER (info->index));
 
788
        if (stream == NULL) {
 
789
                GList *list = NULL;
 
790
                guint i;
 
791
 
 
792
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 
793
                stream = gvc_mixer_sink_new (control->priv->pa_context,
 
794
                                             info->index,
 
795
                                             map);
 
796
 
 
797
                for (i = 0; i < info->n_ports; i++) {
 
798
                        GvcMixerStreamPort *port;
 
799
 
 
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);
 
805
                }
 
806
                gvc_mixer_stream_set_ports (stream, list);
 
807
 
 
808
                g_object_unref (map);
 
809
                is_new = TRUE;
 
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");
 
813
                return;
 
814
        }
 
815
 
 
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);
 
825
 
 
826
        if (info->active_port != NULL)
 
827
                gvc_mixer_stream_set_port (stream, info->active_port->name);
 
828
 
 
829
        if (is_new) {
 
830
                g_hash_table_insert (control->priv->sinks,
 
831
                                     GUINT_TO_POINTER (info->index),
 
832
                                     g_object_ref (stream));
 
833
                add_stream (control, stream);
 
834
        }
 
835
 
 
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);
 
840
        }
 
841
 
 
842
        if (map == NULL)
 
843
                map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
 
844
        gvc_channel_map_volume_changed (map, &info->volume, FALSE);
 
845
}
 
846
 
 
847
static void
 
848
update_source (GvcMixerControl      *control,
 
849
               const pa_source_info *info)
 
850
{
 
851
        GvcMixerStream *stream;
 
852
        gboolean        is_new;
 
853
        pa_volume_t     max_volume;
 
854
 
 
855
#if 1
 
856
        g_debug ("Updating source: index=%u name='%s' description='%s'",
 
857
                 info->index,
 
858
                 info->name,
 
859
                 info->description);
 
860
#endif
 
861
 
 
862
        /* completely ignore monitors, they're not real sources */
 
863
        if (info->monitor_of_sink != PA_INVALID_INDEX) {
 
864
                return;
 
865
        }
 
866
 
 
867
        is_new = FALSE;
 
868
 
 
869
        stream = g_hash_table_lookup (control->priv->sources,
 
870
                                      GUINT_TO_POINTER (info->index));
 
871
        if (stream == NULL) {
 
872
                GList *list = NULL;
 
873
                guint i;
 
874
                GvcChannelMap *map;
 
875
 
 
876
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 
877
                stream = gvc_mixer_source_new (control->priv->pa_context,
 
878
                                               info->index,
 
879
                                               map);
 
880
 
 
881
                for (i = 0; i < info->n_ports; i++) {
 
882
                        GvcMixerStreamPort *port;
 
883
 
 
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);
 
889
                }
 
890
                gvc_mixer_stream_set_ports (stream, list);
 
891
 
 
892
                g_object_unref (map);
 
893
                is_new = TRUE;
 
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");
 
897
                return;
 
898
        }
 
899
 
 
900
        max_volume = pa_cvolume_max (&info->volume);
 
901
 
 
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);
 
910
 
 
911
        if (info->active_port != NULL)
 
912
                gvc_mixer_stream_set_port (stream, info->active_port->name);
 
913
 
 
914
        if (is_new) {
 
915
                g_hash_table_insert (control->priv->sources,
 
916
                                     GUINT_TO_POINTER (info->index),
 
917
                                     g_object_ref (stream));
 
918
                add_stream (control, stream);
 
919
        }
 
920
 
 
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);
 
925
        }
 
926
}
 
927
 
 
928
static void
 
929
set_is_event_stream_from_proplist (GvcMixerStream *stream,
 
930
                                   pa_proplist    *l)
 
931
{
 
932
        const char *t;
 
933
        gboolean is_event_stream;
 
934
 
 
935
        is_event_stream = FALSE;
 
936
 
 
937
        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
 
938
                if (g_str_equal (t, "event"))
 
939
                        is_event_stream = TRUE;
 
940
        }
 
941
 
 
942
        gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
 
943
}
 
944
 
 
945
static void
 
946
set_application_id_from_proplist (GvcMixerStream *stream,
 
947
                                  pa_proplist    *l)
 
948
{
 
949
        const char *t;
 
950
 
 
951
        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
 
952
                gvc_mixer_stream_set_application_id (stream, t);
 
953
        }
 
954
}
 
955
 
 
956
static void
 
957
update_sink_input (GvcMixerControl          *control,
 
958
                   const pa_sink_input_info *info)
 
959
{
 
960
        GvcMixerStream *stream;
 
961
        gboolean        is_new;
 
962
        pa_volume_t     max_volume;
 
963
        const char     *name;
 
964
 
 
965
#if 0
 
966
        g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u",
 
967
                 info->index,
 
968
                 info->name,
 
969
                 info->client,
 
970
                 info->sink);
 
971
#endif
 
972
 
 
973
        is_new = FALSE;
 
974
 
 
975
        stream = g_hash_table_lookup (control->priv->sink_inputs,
 
976
                                      GUINT_TO_POINTER (info->index));
 
977
        if (stream == NULL) {
 
978
                GvcChannelMap *map;
 
979
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 
980
                stream = gvc_mixer_sink_input_new (control->priv->pa_context,
 
981
                                                   info->index,
 
982
                                                   map);
 
983
                g_object_unref (map);
 
984
                is_new = TRUE;
 
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");
 
988
                return;
 
989
        }
 
990
 
 
991
        max_volume = pa_cvolume_max (&info->volume);
 
992
 
 
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);
 
997
 
 
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);
 
1004
 
 
1005
        if (is_new) {
 
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);
 
1010
        }
 
1011
}
 
1012
 
 
1013
static void
 
1014
update_source_output (GvcMixerControl             *control,
 
1015
                      const pa_source_output_info *info)
 
1016
{
 
1017
        GvcMixerStream *stream;
 
1018
        gboolean        is_new;
 
1019
        const char     *name;
 
1020
 
 
1021
#if 1
 
1022
        g_debug ("Updating source output: index=%u name='%s' client=%u source=%u",
 
1023
                 info->index,
 
1024
                 info->name,
 
1025
                 info->client,
 
1026
                 info->source);
 
1027
#endif
 
1028
 
 
1029
        is_new = FALSE;
 
1030
        stream = g_hash_table_lookup (control->priv->source_outputs,
 
1031
                                      GUINT_TO_POINTER (info->index));
 
1032
        if (stream == NULL) {
 
1033
                GvcChannelMap *map;
 
1034
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 
1035
                stream = gvc_mixer_source_output_new (control->priv->pa_context,
 
1036
                                                      info->index,
 
1037
                                                      map);
 
1038
                g_object_unref (map);
 
1039
                is_new = TRUE;
 
1040
        }
 
1041
 
 
1042
        name = (const char *)g_hash_table_lookup (control->priv->clients,
 
1043
                                                  GUINT_TO_POINTER (info->client));
 
1044
 
 
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");
 
1050
 
 
1051
        if (is_new) {
 
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);
 
1056
        }
 
1057
}
 
1058
 
 
1059
static void
 
1060
update_client (GvcMixerControl      *control,
 
1061
               const pa_client_info *info)
 
1062
{
 
1063
#if 1
 
1064
        g_debug ("Updating client: index=%u name='%s'",
 
1065
                 info->index,
 
1066
                 info->name);
 
1067
#endif
 
1068
        g_hash_table_insert (control->priv->clients,
 
1069
                             GUINT_TO_POINTER (info->index),
 
1070
                             g_strdup (info->name));
 
1071
}
 
1072
 
 
1073
static char *
 
1074
card_num_streams_to_status (guint sinks,
 
1075
                            guint sources)
 
1076
{
 
1077
        char *sinks_str;
 
1078
        char *sources_str;
 
1079
        char *ret;
 
1080
 
 
1081
        if (sinks == 0 && sources == 0) {
 
1082
                /* translators:
 
1083
                 * The device has been disabled */
 
1084
                return g_strdup (_("Disabled"));
 
1085
        }
 
1086
        if (sinks == 0) {
 
1087
                sinks_str = NULL;
 
1088
        } else {
 
1089
                /* translators:
 
1090
                 * The number of sound outputs on a particular device */
 
1091
                sinks_str = g_strdup_printf (ngettext ("%u Output",
 
1092
                                                       "%u Outputs",
 
1093
                                                       sinks),
 
1094
                                             sinks);
 
1095
        }
 
1096
        if (sources == 0) {
 
1097
                sources_str = NULL;
 
1098
        } else {
 
1099
                /* translators:
 
1100
                 * The number of sound inputs on a particular device */
 
1101
                sources_str = g_strdup_printf (ngettext ("%u Input",
 
1102
                                                         "%u Inputs",
 
1103
                                                         sources),
 
1104
                                               sources);
 
1105
        }
 
1106
        if (sources_str == NULL)
 
1107
                return sinks_str;
 
1108
        if (sinks_str == NULL)
 
1109
                return sources_str;
 
1110
        ret = g_strdup_printf ("%s / %s", sinks_str, sources_str);
 
1111
        g_free (sinks_str);
 
1112
        g_free (sources_str);
 
1113
        return ret;
 
1114
}
 
1115
 
 
1116
static void
 
1117
update_card (GvcMixerControl      *control,
 
1118
             const pa_card_info   *info)
 
1119
{
 
1120
        GvcMixerCard *card;
 
1121
        gboolean      is_new = FALSE;
 
1122
#if 1
 
1123
        guint i;
 
1124
        const char *key;
 
1125
        void *state;
 
1126
 
 
1127
        g_debug ("Udpating card %s (index: %u driver: %s):",
 
1128
                 info->name, info->index, info->driver);
 
1129
 
 
1130
        for (i = 0; i < info->n_profiles; i++) {
 
1131
                struct pa_card_profile_info pi = info->profiles[i];
 
1132
                gboolean is_default;
 
1133
 
 
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)" : "");
 
1138
        }
 
1139
        state = NULL;
 
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);
 
1145
        }
 
1146
#endif
 
1147
        card = g_hash_table_lookup (control->priv->cards,
 
1148
                                    GUINT_TO_POINTER (info->index));
 
1149
        if (card == NULL) {
 
1150
                GList *list = NULL;
 
1151
 
 
1152
                for (i = 0; i < info->n_profiles; i++) {
 
1153
                        struct pa_card_profile_info pi = info->profiles[i];
 
1154
                        GvcMixerCardProfile *profile;
 
1155
 
 
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);
 
1164
                }
 
1165
                card = gvc_mixer_card_new (control->priv->pa_context,
 
1166
                                           info->index);
 
1167
                gvc_mixer_card_set_profiles (card, list);
 
1168
                is_new = TRUE;
 
1169
        }
 
1170
 
 
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);
 
1174
 
 
1175
        if (is_new) {
 
1176
                g_hash_table_insert (control->priv->cards,
 
1177
                                     GUINT_TO_POINTER (info->index),
 
1178
                                     g_object_ref (card));
 
1179
        }
 
1180
        g_signal_emit (G_OBJECT (control),
 
1181
                       signals[CARD_ADDED],
 
1182
                       0,
 
1183
                       info->index);
 
1184
}
 
1185
 
 
1186
static void
 
1187
_pa_context_get_sink_info_cb (pa_context         *context,
 
1188
                              const pa_sink_info *i,
 
1189
                              int                 eol,
 
1190
                              void               *userdata)
 
1191
{
 
1192
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1193
 
 
1194
        if (eol < 0) {
 
1195
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
 
1196
                        return;
 
1197
                }
 
1198
 
 
1199
                g_warning ("Sink callback failure");
 
1200
                return;
 
1201
        }
 
1202
 
 
1203
        if (eol > 0) {
 
1204
                dec_outstanding (control);
 
1205
                return;
 
1206
        }
 
1207
 
 
1208
        update_sink (control, i);
 
1209
}
 
1210
 
 
1211
static void
 
1212
_pa_context_get_source_info_cb (pa_context           *context,
 
1213
                                const pa_source_info *i,
 
1214
                                int                   eol,
 
1215
                                void                 *userdata)
 
1216
{
 
1217
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1218
 
 
1219
        if (eol < 0) {
 
1220
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
 
1221
                        return;
 
1222
                }
 
1223
 
 
1224
                g_warning ("Source callback failure");
 
1225
                return;
 
1226
        }
 
1227
 
 
1228
        if (eol > 0) {
 
1229
                dec_outstanding (control);
 
1230
                return;
 
1231
        }
 
1232
 
 
1233
        update_source (control, i);
 
1234
}
 
1235
 
 
1236
static void
 
1237
_pa_context_get_sink_input_info_cb (pa_context               *context,
 
1238
                                    const pa_sink_input_info *i,
 
1239
                                    int                       eol,
 
1240
                                    void                     *userdata)
 
1241
{
 
1242
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1243
 
 
1244
        if (eol < 0) {
 
1245
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
 
1246
                        return;
 
1247
                }
 
1248
 
 
1249
                g_warning ("Sink input callback failure");
 
1250
                return;
 
1251
        }
 
1252
 
 
1253
        if (eol > 0) {
 
1254
                dec_outstanding (control);
 
1255
                return;
 
1256
        }
 
1257
 
 
1258
        update_sink_input (control, i);
 
1259
}
 
1260
 
 
1261
static void
 
1262
_pa_context_get_source_output_info_cb (pa_context                  *context,
 
1263
                                       const pa_source_output_info *i,
 
1264
                                       int                          eol,
 
1265
                                       void                        *userdata)
 
1266
{
 
1267
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1268
 
 
1269
        if (eol < 0) {
 
1270
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
 
1271
                        return;
 
1272
                }
 
1273
 
 
1274
                g_warning ("Source output callback failure");
 
1275
                return;
 
1276
        }
 
1277
 
 
1278
        if (eol > 0)  {
 
1279
                dec_outstanding (control);
 
1280
                return;
 
1281
        }
 
1282
 
 
1283
        update_source_output (control, i);
 
1284
}
 
1285
 
 
1286
static void
 
1287
_pa_context_get_client_info_cb (pa_context           *context,
 
1288
                                const pa_client_info *i,
 
1289
                                int                   eol,
 
1290
                                void                 *userdata)
 
1291
{
 
1292
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1293
 
 
1294
        if (eol < 0) {
 
1295
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
 
1296
                        return;
 
1297
                }
 
1298
 
 
1299
                g_warning ("Client callback failure");
 
1300
                return;
 
1301
        }
 
1302
 
 
1303
        if (eol > 0) {
 
1304
                dec_outstanding (control);
 
1305
                return;
 
1306
        }
 
1307
 
 
1308
        update_client (control, i);
 
1309
}
 
1310
 
 
1311
static void
 
1312
_pa_context_get_card_info_by_index_cb (pa_context *context,
 
1313
                                       const pa_card_info *i,
 
1314
                                       int eol,
 
1315
                                       void *userdata)
 
1316
{
 
1317
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1318
 
 
1319
        if (eol < 0) {
 
1320
                if (pa_context_errno (context) == PA_ERR_NOENTITY)
 
1321
                        return;
 
1322
 
 
1323
                g_warning ("Card callback failure");
 
1324
                return;
 
1325
        }
 
1326
 
 
1327
        if (eol > 0) {
 
1328
                dec_outstanding (control);
 
1329
                return;
 
1330
        }
 
1331
 
 
1332
        update_card (control, i);
 
1333
}
 
1334
 
 
1335
static void
 
1336
_pa_context_get_server_info_cb (pa_context           *context,
 
1337
                                const pa_server_info *i,
 
1338
                                void                 *userdata)
 
1339
{
 
1340
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1341
 
 
1342
        if (i == NULL) {
 
1343
                g_warning ("Server info callback failure");
 
1344
                return;
 
1345
        }
 
1346
 
 
1347
        update_server (control, i);
 
1348
        dec_outstanding (control);
 
1349
}
 
1350
 
 
1351
static void
 
1352
remove_event_role_stream (GvcMixerControl *control)
 
1353
{
 
1354
        g_debug ("Removing event role");
 
1355
}
 
1356
 
 
1357
static void
 
1358
update_event_role_stream (GvcMixerControl                  *control,
 
1359
                          const pa_ext_stream_restore_info *info)
 
1360
{
 
1361
        GvcMixerStream *stream;
 
1362
        gboolean        is_new;
 
1363
        pa_volume_t     max_volume;
 
1364
 
 
1365
        if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
 
1366
                return;
 
1367
        }
 
1368
 
 
1369
#if 0
 
1370
        g_debug ("Updating event role: name='%s' device='%s'",
 
1371
                 info->name,
 
1372
                 info->device);
 
1373
#endif
 
1374
 
 
1375
        is_new = FALSE;
 
1376
 
 
1377
        if (!control->priv->event_sink_input_is_set) {
 
1378
                pa_channel_map pa_map;
 
1379
                GvcChannelMap *map;
 
1380
 
 
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);
 
1384
 
 
1385
                stream = gvc_mixer_event_role_new (control->priv->pa_context,
 
1386
                                                   info->device,
 
1387
                                                   map);
 
1388
                control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
 
1389
                control->priv->event_sink_input_is_set = TRUE;
 
1390
 
 
1391
                is_new = TRUE;
 
1392
        } else {
 
1393
                stream = g_hash_table_lookup (control->priv->all_streams,
 
1394
                                              GUINT_TO_POINTER (control->priv->event_sink_input_id));
 
1395
        }
 
1396
 
 
1397
        max_volume = pa_cvolume_max (&info->volume);
 
1398
 
 
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);
 
1403
 
 
1404
        if (is_new) {
 
1405
                add_stream (control, stream);
 
1406
        }
 
1407
}
 
1408
 
 
1409
static void
 
1410
_pa_ext_stream_restore_read_cb (pa_context                       *context,
 
1411
                                const pa_ext_stream_restore_info *i,
 
1412
                                int                               eol,
 
1413
                                void                             *userdata)
 
1414
{
 
1415
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1416
 
 
1417
        if (eol < 0) {
 
1418
                g_debug ("Failed to initialized stream_restore extension: %s",
 
1419
                         pa_strerror (pa_context_errno (context)));
 
1420
                remove_event_role_stream (control);
 
1421
                return;
 
1422
        }
 
1423
 
 
1424
        if (eol > 0) {
 
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;
 
1430
 
 
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);
 
1436
                }
 
1437
                return;
 
1438
        }
 
1439
 
 
1440
        update_event_role_stream (control, i);
 
1441
}
 
1442
 
 
1443
static void
 
1444
_pa_ext_stream_restore_subscribe_cb (pa_context *context,
 
1445
                                     void       *userdata)
 
1446
{
 
1447
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1448
        pa_operation    *o;
 
1449
 
 
1450
        o = pa_ext_stream_restore_read (context,
 
1451
                                        _pa_ext_stream_restore_read_cb,
 
1452
                                        control);
 
1453
        if (o == NULL) {
 
1454
                g_warning ("pa_ext_stream_restore_read() failed");
 
1455
                return;
 
1456
        }
 
1457
 
 
1458
        pa_operation_unref (o);
 
1459
}
 
1460
 
 
1461
static void
 
1462
req_update_server_info (GvcMixerControl *control,
 
1463
                        int              index)
 
1464
{
 
1465
        pa_operation *o;
 
1466
 
 
1467
        o = pa_context_get_server_info (control->priv->pa_context,
 
1468
                                        _pa_context_get_server_info_cb,
 
1469
                                        control);
 
1470
        if (o == NULL) {
 
1471
                g_warning ("pa_context_get_server_info() failed");
 
1472
                return;
 
1473
        }
 
1474
        pa_operation_unref (o);
 
1475
}
 
1476
 
 
1477
static void
 
1478
req_update_client_info (GvcMixerControl *control,
 
1479
                        int              index)
 
1480
{
 
1481
        pa_operation *o;
 
1482
 
 
1483
        if (index < 0) {
 
1484
                o = pa_context_get_client_info_list (control->priv->pa_context,
 
1485
                                                     _pa_context_get_client_info_cb,
 
1486
                                                     control);
 
1487
        } else {
 
1488
                o = pa_context_get_client_info (control->priv->pa_context,
 
1489
                                                index,
 
1490
                                                _pa_context_get_client_info_cb,
 
1491
                                                control);
 
1492
        }
 
1493
 
 
1494
        if (o == NULL) {
 
1495
                g_warning ("pa_context_client_info_list() failed");
 
1496
                return;
 
1497
        }
 
1498
        pa_operation_unref (o);
 
1499
}
 
1500
 
 
1501
static void
 
1502
req_update_card (GvcMixerControl *control,
 
1503
                 int              index)
 
1504
{
 
1505
        pa_operation *o;
 
1506
 
 
1507
        if (index < 0) {
 
1508
                o = pa_context_get_card_info_list (control->priv->pa_context,
 
1509
                                                   _pa_context_get_card_info_by_index_cb,
 
1510
                                                   control);
 
1511
        } else {
 
1512
                o = pa_context_get_card_info_by_index (control->priv->pa_context,
 
1513
                                                       index,
 
1514
                                                       _pa_context_get_card_info_by_index_cb,
 
1515
                                                       control);
 
1516
        }
 
1517
 
 
1518
        if (o == NULL) {
 
1519
                g_warning ("pa_context_get_card_info_by_index() failed");
 
1520
                return;
 
1521
        }
 
1522
        pa_operation_unref (o);
 
1523
}
 
1524
 
 
1525
static void
 
1526
req_update_sink_info (GvcMixerControl *control,
 
1527
                      int              index)
 
1528
{
 
1529
        pa_operation *o;
 
1530
 
 
1531
        if (index < 0) {
 
1532
                o = pa_context_get_sink_info_list (control->priv->pa_context,
 
1533
                                                   _pa_context_get_sink_info_cb,
 
1534
                                                   control);
 
1535
        } else {
 
1536
                o = pa_context_get_sink_info_by_index (control->priv->pa_context,
 
1537
                                                       index,
 
1538
                                                       _pa_context_get_sink_info_cb,
 
1539
                                                       control);
 
1540
        }
 
1541
 
 
1542
        if (o == NULL) {
 
1543
                g_warning ("pa_context_get_sink_info_list() failed");
 
1544
                return;
 
1545
        }
 
1546
        pa_operation_unref (o);
 
1547
}
 
1548
 
 
1549
static void
 
1550
req_update_source_info (GvcMixerControl *control,
 
1551
                        int              index)
 
1552
{
 
1553
        pa_operation *o;
 
1554
 
 
1555
        if (index < 0) {
 
1556
                o = pa_context_get_source_info_list (control->priv->pa_context,
 
1557
                                                     _pa_context_get_source_info_cb,
 
1558
                                                     control);
 
1559
        } else {
 
1560
                o = pa_context_get_source_info_by_index(control->priv->pa_context,
 
1561
                                                        index,
 
1562
                                                        _pa_context_get_source_info_cb,
 
1563
                                                        control);
 
1564
        }
 
1565
 
 
1566
        if (o == NULL) {
 
1567
                g_warning ("pa_context_get_source_info_list() failed");
 
1568
                return;
 
1569
        }
 
1570
        pa_operation_unref (o);
 
1571
}
 
1572
 
 
1573
static void
 
1574
req_update_sink_input_info (GvcMixerControl *control,
 
1575
                            int              index)
 
1576
{
 
1577
        pa_operation *o;
 
1578
 
 
1579
        if (index < 0) {
 
1580
                o = pa_context_get_sink_input_info_list (control->priv->pa_context,
 
1581
                                                         _pa_context_get_sink_input_info_cb,
 
1582
                                                         control);
 
1583
        } else {
 
1584
                o = pa_context_get_sink_input_info (control->priv->pa_context,
 
1585
                                                    index,
 
1586
                                                    _pa_context_get_sink_input_info_cb,
 
1587
                                                    control);
 
1588
        }
 
1589
 
 
1590
        if (o == NULL) {
 
1591
                g_warning ("pa_context_get_sink_input_info_list() failed");
 
1592
                return;
 
1593
        }
 
1594
        pa_operation_unref (o);
 
1595
}
 
1596
 
 
1597
static void
 
1598
req_update_source_output_info (GvcMixerControl *control,
 
1599
                               int              index)
 
1600
{
 
1601
        pa_operation *o;
 
1602
 
 
1603
        if (index < 0) {
 
1604
                o = pa_context_get_source_output_info_list (control->priv->pa_context,
 
1605
                                                            _pa_context_get_source_output_info_cb,
 
1606
                                                            control);
 
1607
        } else {
 
1608
                o = pa_context_get_source_output_info (control->priv->pa_context,
 
1609
                                                       index,
 
1610
                                                       _pa_context_get_source_output_info_cb,
 
1611
                                                       control);
 
1612
        }
 
1613
 
 
1614
        if (o == NULL) {
 
1615
                g_warning ("pa_context_get_source_output_info_list() failed");
 
1616
                return;
 
1617
        }
 
1618
        pa_operation_unref (o);
 
1619
}
 
1620
 
 
1621
static void
 
1622
remove_client (GvcMixerControl *control,
 
1623
               guint            index)
 
1624
{
 
1625
        g_hash_table_remove (control->priv->clients,
 
1626
                             GUINT_TO_POINTER (index));
 
1627
}
 
1628
 
 
1629
static void
 
1630
remove_card (GvcMixerControl *control,
 
1631
             guint            index)
 
1632
{
 
1633
        g_hash_table_remove (control->priv->cards,
 
1634
                             GUINT_TO_POINTER (index));
 
1635
 
 
1636
        g_signal_emit (G_OBJECT (control),
 
1637
                       signals[CARD_REMOVED],
 
1638
                       0,
 
1639
                       index);
 
1640
}
 
1641
 
 
1642
static void
 
1643
remove_sink (GvcMixerControl *control,
 
1644
             guint            index)
 
1645
{
 
1646
        GvcMixerStream *stream;
 
1647
 
 
1648
#if 0
 
1649
        g_debug ("Removing sink: index=%u", index);
 
1650
#endif
 
1651
 
 
1652
        stream = g_hash_table_lookup (control->priv->sinks,
 
1653
                                      GUINT_TO_POINTER (index));
 
1654
        if (stream == NULL) {
 
1655
                return;
 
1656
        }
 
1657
        g_hash_table_remove (control->priv->sinks,
 
1658
                             GUINT_TO_POINTER (index));
 
1659
 
 
1660
        remove_stream (control, stream);
 
1661
}
 
1662
 
 
1663
static void
 
1664
remove_source (GvcMixerControl *control,
 
1665
               guint            index)
 
1666
{
 
1667
        GvcMixerStream *stream;
 
1668
 
 
1669
#if 0
 
1670
        g_debug ("Removing source: index=%u", index);
 
1671
#endif
 
1672
 
 
1673
        stream = g_hash_table_lookup (control->priv->sources,
 
1674
                                      GUINT_TO_POINTER (index));
 
1675
        if (stream == NULL) {
 
1676
                return;
 
1677
        }
 
1678
        g_hash_table_remove (control->priv->sources,
 
1679
                             GUINT_TO_POINTER (index));
 
1680
 
 
1681
        remove_stream (control, stream);
 
1682
}
 
1683
 
 
1684
static void
 
1685
remove_sink_input (GvcMixerControl *control,
 
1686
                   guint            index)
 
1687
{
 
1688
        GvcMixerStream *stream;
 
1689
 
 
1690
#if 0
 
1691
        g_debug ("Removing sink input: index=%u", index);
 
1692
#endif
 
1693
        stream = g_hash_table_lookup (control->priv->sink_inputs,
 
1694
                                      GUINT_TO_POINTER (index));
 
1695
        if (stream == NULL) {
 
1696
                return;
 
1697
        }
 
1698
        g_hash_table_remove (control->priv->sink_inputs,
 
1699
                             GUINT_TO_POINTER (index));
 
1700
 
 
1701
        remove_stream (control, stream);
 
1702
}
 
1703
 
 
1704
static void
 
1705
remove_source_output (GvcMixerControl *control,
 
1706
                      guint            index)
 
1707
{
 
1708
        GvcMixerStream *stream;
 
1709
 
 
1710
#if 0
 
1711
        g_debug ("Removing source output: index=%u", index);
 
1712
#endif
 
1713
 
 
1714
        stream = g_hash_table_lookup (control->priv->source_outputs,
 
1715
                                      GUINT_TO_POINTER (index));
 
1716
        if (stream == NULL) {
 
1717
                return;
 
1718
        }
 
1719
        g_hash_table_remove (control->priv->source_outputs,
 
1720
                             GUINT_TO_POINTER (index));
 
1721
 
 
1722
        remove_stream (control, stream);
 
1723
}
 
1724
 
 
1725
static void
 
1726
_pa_context_subscribe_cb (pa_context                  *context,
 
1727
                          pa_subscription_event_type_t t,
 
1728
                          uint32_t                     index,
 
1729
                          void                        *userdata)
 
1730
{
 
1731
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1732
 
 
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);
 
1737
                } else {
 
1738
                        req_update_sink_info (control, index);
 
1739
                }
 
1740
                break;
 
1741
 
 
1742
        case PA_SUBSCRIPTION_EVENT_SOURCE:
 
1743
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
 
1744
                        remove_source (control, index);
 
1745
                } else {
 
1746
                        req_update_source_info (control, index);
 
1747
                }
 
1748
                break;
 
1749
 
 
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);
 
1753
                } else {
 
1754
                        req_update_sink_input_info (control, index);
 
1755
                }
 
1756
                break;
 
1757
 
 
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);
 
1761
                } else {
 
1762
                        req_update_source_output_info (control, index);
 
1763
                }
 
1764
                break;
 
1765
 
 
1766
        case PA_SUBSCRIPTION_EVENT_CLIENT:
 
1767
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
 
1768
                        remove_client (control, index);
 
1769
                } else {
 
1770
                        req_update_client_info (control, index);
 
1771
                }
 
1772
                break;
 
1773
 
 
1774
        case PA_SUBSCRIPTION_EVENT_SERVER:
 
1775
                req_update_server_info (control, index);
 
1776
                break;
 
1777
 
 
1778
        case PA_SUBSCRIPTION_EVENT_CARD:
 
1779
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
 
1780
                        remove_card (control, index);
 
1781
                } else {
 
1782
                        req_update_card (control, index);
 
1783
                }
 
1784
                break;
 
1785
        }
 
1786
}
 
1787
 
 
1788
static void
 
1789
gvc_mixer_control_ready (GvcMixerControl *control)
 
1790
{
 
1791
        pa_operation *o;
 
1792
 
 
1793
        pa_context_set_subscribe_callback (control->priv->pa_context,
 
1794
                                           _pa_context_subscribe_cb,
 
1795
                                           control);
 
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),
 
1805
                                  NULL,
 
1806
                                  NULL);
 
1807
 
 
1808
        if (o == NULL) {
 
1809
                g_warning ("pa_context_subscribe() failed");
 
1810
                return;
 
1811
        }
 
1812
        pa_operation_unref (o);
 
1813
 
 
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);
 
1821
 
 
1822
        control->priv->n_outstanding = 6;
 
1823
 
 
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,
 
1827
                                        control);
 
1828
        if (o != NULL) {
 
1829
                pa_operation_unref (o);
 
1830
                control->priv->n_outstanding++;
 
1831
 
 
1832
                pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
 
1833
                                                        _pa_ext_stream_restore_subscribe_cb,
 
1834
                                                        control);
 
1835
 
 
1836
                o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
 
1837
                                                     1,
 
1838
                                                     NULL,
 
1839
                                                     NULL);
 
1840
                if (o != NULL) {
 
1841
                        pa_operation_unref (o);
 
1842
                }
 
1843
 
 
1844
        } else {
 
1845
                g_debug ("Failed to initialized stream_restore extension: %s",
 
1846
                         pa_strerror (pa_context_errno (control->priv->pa_context)));
 
1847
        }
 
1848
}
 
1849
 
 
1850
static void
 
1851
gvc_mixer_new_pa_context (GvcMixerControl *self)
 
1852
{
 
1853
        pa_proplist     *proplist;
 
1854
 
 
1855
        g_return_if_fail (self);
 
1856
        g_return_if_fail (!self->priv->pa_context);
 
1857
 
 
1858
        proplist = pa_proplist_new ();
 
1859
        pa_proplist_sets (proplist,
 
1860
                          PA_PROP_APPLICATION_NAME,
 
1861
                          self->priv->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,
 
1870
                          PACKAGE_VERSION);
 
1871
 
 
1872
        self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
 
1873
 
 
1874
        pa_proplist_free (proplist);
 
1875
        g_assert (self->priv->pa_context);
 
1876
}
 
1877
 
 
1878
static void
 
1879
remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
 
1880
{
 
1881
        GHashTableIter iter;
 
1882
        gpointer key, value;
 
1883
 
 
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);
 
1888
        }
 
1889
}
 
1890
 
 
1891
static gboolean
 
1892
idle_reconnect (gpointer data)
 
1893
{
 
1894
        GvcMixerControl *control = GVC_MIXER_CONTROL (data);
 
1895
        GHashTableIter iter;
 
1896
        gpointer key, value;
 
1897
 
 
1898
        g_return_val_if_fail (control, FALSE);
 
1899
 
 
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);
 
1904
        }
 
1905
 
 
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);
 
1910
 
 
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);
 
1914
 
 
1915
        gvc_mixer_control_open (control); /* cannot fail */
 
1916
 
 
1917
        control->priv->reconnect_id = 0;
 
1918
        return FALSE;
 
1919
}
 
1920
 
 
1921
static void
 
1922
_pa_context_state_cb (pa_context *context,
 
1923
                      void       *userdata)
 
1924
{
 
1925
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
 
1926
 
 
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:
 
1932
                break;
 
1933
 
 
1934
        case PA_CONTEXT_READY:
 
1935
                gvc_mixer_control_ready (control);
 
1936
                break;
 
1937
 
 
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);
 
1942
                break;
 
1943
 
 
1944
        case PA_CONTEXT_TERMINATED:
 
1945
        default:
 
1946
                /* FIXME: */
 
1947
                break;
 
1948
        }
 
1949
}
 
1950
 
 
1951
gboolean
 
1952
gvc_mixer_control_open (GvcMixerControl *control)
 
1953
{
 
1954
        int res;
 
1955
 
 
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);
 
1959
 
 
1960
        pa_context_set_state_callback (control->priv->pa_context,
 
1961
                                       _pa_context_state_cb,
 
1962
                                       control);
 
1963
 
 
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);
 
1966
        if (res < 0) {
 
1967
                g_warning ("Failed to connect context: %s",
 
1968
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
 
1969
        }
 
1970
 
 
1971
        return res;
 
1972
}
 
1973
 
 
1974
gboolean
 
1975
gvc_mixer_control_close (GvcMixerControl *control)
 
1976
{
 
1977
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
 
1978
        g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
 
1979
 
 
1980
        pa_context_disconnect (control->priv->pa_context);
 
1981
        return TRUE;
 
1982
}
 
1983
 
 
1984
static void
 
1985
gvc_mixer_control_dispose (GObject *object)
 
1986
{
 
1987
        GvcMixerControl *control = GVC_MIXER_CONTROL (object);
 
1988
 
 
1989
        if (control->priv->reconnect_id != 0) {
 
1990
                g_source_remove (control->priv->reconnect_id);
 
1991
                control->priv->reconnect_id = 0;
 
1992
        }
 
1993
 
 
1994
        if (control->priv->pa_context != NULL) {
 
1995
                pa_context_unref (control->priv->pa_context);
 
1996
                control->priv->pa_context = NULL;
 
1997
        }
 
1998
 
 
1999
        if (control->priv->default_source_name != NULL) {
 
2000
                g_free (control->priv->default_source_name);
 
2001
                control->priv->default_source_name = NULL;
 
2002
        }
 
2003
        if (control->priv->default_sink_name != NULL) {
 
2004
                g_free (control->priv->default_sink_name);
 
2005
                control->priv->default_sink_name = NULL;
 
2006
        }
 
2007
 
 
2008
        if (control->priv->pa_mainloop != NULL) {
 
2009
                pa_glib_mainloop_free (control->priv->pa_mainloop);
 
2010
                control->priv->pa_mainloop = NULL;
 
2011
        }
 
2012
 
 
2013
        if (control->priv->all_streams != NULL) {
 
2014
                g_hash_table_destroy (control->priv->all_streams);
 
2015
                control->priv->all_streams = NULL;
 
2016
        }
 
2017
 
 
2018
        if (control->priv->sinks != NULL) {
 
2019
                g_hash_table_destroy (control->priv->sinks);
 
2020
                control->priv->sinks = NULL;
 
2021
        }
 
2022
        if (control->priv->sources != NULL) {
 
2023
                g_hash_table_destroy (control->priv->sources);
 
2024
                control->priv->sources = NULL;
 
2025
        }
 
2026
        if (control->priv->sink_inputs != NULL) {
 
2027
                g_hash_table_destroy (control->priv->sink_inputs);
 
2028
                control->priv->sink_inputs = NULL;
 
2029
        }
 
2030
        if (control->priv->source_outputs != NULL) {
 
2031
                g_hash_table_destroy (control->priv->source_outputs);
 
2032
                control->priv->source_outputs = NULL;
 
2033
        }
 
2034
        if (control->priv->clients != NULL) {
 
2035
                g_hash_table_destroy (control->priv->clients);
 
2036
                control->priv->clients = NULL;
 
2037
        }
 
2038
        if (control->priv->cards != NULL) {
 
2039
                g_hash_table_destroy (control->priv->cards);
 
2040
                control->priv->cards = NULL;
 
2041
        }
 
2042
 
 
2043
        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
 
2044
}
 
2045
 
 
2046
static void
 
2047
gvc_mixer_control_set_property (GObject       *object,
 
2048
                                guint          prop_id,
 
2049
                                const GValue  *value,
 
2050
                                GParamSpec    *pspec)
 
2051
{
 
2052
        GvcMixerControl *self = GVC_MIXER_CONTROL (object);
 
2053
 
 
2054
        switch (prop_id) {
 
2055
        case PROP_NAME:
 
2056
                g_free (self->priv->name);
 
2057
                self->priv->name = g_value_dup_string (value);
 
2058
                g_object_notify (G_OBJECT (self), "name");
 
2059
                break;
 
2060
        default:
 
2061
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
2062
                break;
 
2063
        }
 
2064
}
 
2065
 
 
2066
static void
 
2067
gvc_mixer_control_get_property (GObject     *object,
 
2068
                                guint        prop_id,
 
2069
                                GValue      *value,
 
2070
                                GParamSpec  *pspec)
 
2071
{
 
2072
        GvcMixerControl *self = GVC_MIXER_CONTROL (object);
 
2073
 
 
2074
        switch (prop_id) {
 
2075
        case PROP_NAME:
 
2076
                g_value_set_string (value, self->priv->name);
 
2077
                break;
 
2078
        default:
 
2079
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
2080
                break;
 
2081
        }
 
2082
}
 
2083
 
 
2084
 
 
2085
static GObject *
 
2086
gvc_mixer_control_constructor (GType                  type,
 
2087
                               guint                  n_construct_properties,
 
2088
                               GObjectConstructParam *construct_params)
 
2089
{
 
2090
        GObject         *object;
 
2091
        GvcMixerControl *self;
 
2092
 
 
2093
        object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
 
2094
 
 
2095
        self = GVC_MIXER_CONTROL (object);
 
2096
 
 
2097
        gvc_mixer_new_pa_context (self);
 
2098
 
 
2099
        return object;
 
2100
}
 
2101
 
 
2102
static void
 
2103
gvc_mixer_control_class_init (GvcMixerControlClass *klass)
 
2104
{
 
2105
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
2106
 
 
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;
 
2112
 
 
2113
        g_object_class_install_property (object_class,
 
2114
                                         PROP_NAME,
 
2115
                                         g_param_spec_string ("name",
 
2116
                                                              "Name",
 
2117
                                                              "Name to display for this mixer control",
 
2118
                                                              NULL,
 
2119
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
 
2120
 
 
2121
        signals [CONNECTING] =
 
2122
                g_signal_new ("connecting",
 
2123
                              G_TYPE_FROM_CLASS (klass),
 
2124
                              G_SIGNAL_RUN_LAST,
 
2125
                              G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
 
2126
                              NULL, NULL,
 
2127
                              g_cclosure_marshal_VOID__VOID,
 
2128
                              G_TYPE_NONE, 0);
 
2129
        signals [READY] =
 
2130
                g_signal_new ("ready",
 
2131
                              G_TYPE_FROM_CLASS (klass),
 
2132
                              G_SIGNAL_RUN_LAST,
 
2133
                              G_STRUCT_OFFSET (GvcMixerControlClass, ready),
 
2134
                              NULL, NULL,
 
2135
                              g_cclosure_marshal_VOID__VOID,
 
2136
                              G_TYPE_NONE, 0);
 
2137
        signals [STREAM_ADDED] =
 
2138
                g_signal_new ("stream-added",
 
2139
                              G_TYPE_FROM_CLASS (klass),
 
2140
                              G_SIGNAL_RUN_LAST,
 
2141
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
 
2142
                              NULL, NULL,
 
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),
 
2148
                              G_SIGNAL_RUN_LAST,
 
2149
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
 
2150
                              NULL, NULL,
 
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),
 
2156
                              G_SIGNAL_RUN_LAST,
 
2157
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
 
2158
                              NULL, NULL,
 
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),
 
2164
                              G_SIGNAL_RUN_LAST,
 
2165
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
 
2166
                              NULL, NULL,
 
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),
 
2172
                              G_SIGNAL_RUN_LAST,
 
2173
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
 
2174
                              NULL, NULL,
 
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),
 
2180
                              G_SIGNAL_RUN_LAST,
 
2181
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
 
2182
                              NULL, NULL,
 
2183
                              g_cclosure_marshal_VOID__UINT,
 
2184
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
2185
 
 
2186
        g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
 
2187
}
 
2188
 
 
2189
static void
 
2190
gvc_mixer_control_init (GvcMixerControl *control)
 
2191
{
 
2192
        control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control);
 
2193
 
 
2194
        control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
 
2195
        g_assert (control->priv->pa_mainloop);
 
2196
 
 
2197
        control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
 
2198
        g_assert (control->priv->pa_api);
 
2199
 
 
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);
 
2206
 
 
2207
        control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
 
2208
}
 
2209
 
 
2210
static void
 
2211
gvc_mixer_control_finalize (GObject *object)
 
2212
{
 
2213
        GvcMixerControl *mixer_control;
 
2214
 
 
2215
        g_return_if_fail (object != NULL);
 
2216
        g_return_if_fail (GVC_IS_MIXER_CONTROL (object));
 
2217
 
 
2218
        mixer_control = GVC_MIXER_CONTROL (object);
 
2219
        g_free (mixer_control->priv->name);
 
2220
        mixer_control->priv->name = NULL;
 
2221
 
 
2222
        g_return_if_fail (mixer_control->priv != NULL);
 
2223
        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
 
2224
}
 
2225
 
 
2226
GvcMixerControl *
 
2227
gvc_mixer_control_new (const char *name)
 
2228
{
 
2229
        GObject *control;
 
2230
        control = g_object_new (GVC_TYPE_MIXER_CONTROL,
 
2231
                                "name", name,
 
2232
                                NULL);
 
2233
        return GVC_MIXER_CONTROL (control);
 
2234
}
 
2235
 
 
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)
 
2239
#endif
 
2240
 
 
2241
gdouble
 
2242
gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
 
2243
{
 
2244
        return (gdouble) PA_VOLUME_NORM;
 
2245
}
 
2246
 
 
2247
gdouble
 
2248
gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control)
 
2249
{
 
2250
        return (gdouble) PA_VOLUME_UI_MAX;
 
2251
}