~darkxst/ubuntu/saucy/gnome-shell/upstart_log

« back to all changes in this revision

Viewing changes to src/gvc/gvc-mixer-control.c

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2013-05-31 12:01:12 UTC
  • mfrom: (1.1.49) (19.1.36 experimental)
  • Revision ID: package-import@ubuntu.com-20130531120112-ew91khxf051x9i2r
Tags: 3.8.2-1ubuntu1
* Merge with Debian (LP: #1185869, #1185721). Remaining changes:
  - debian/control.in:
    + Build-depend on libsystemd-login-dev & libsystemd-daemon-dev
    + Depend on gdm instead of gdm3
    + Don't recommend gnome-session-fallback
  - debian/patches/40_change-pam-name-to-match-gdm.patch:
  - debian/patches/revert-suspend-break.patch:
    + Disabled, not needed on Ubuntu
  - debian/patches/ubuntu-lightdm-user-switching.patch:
    + Allow user switching when using LightDM. Thanks Gerhard Stein
      for rebasing against gnome-shell 3.8!
  - debian/patches/ubuntu_lock_on_suspend.patch
    + Respect Ubuntu's lock-on-suspend setting.
      Disabled until it can be rewritten.
  - debian/patches/git_relock_screen_after_crash.patch:
    + Add Upstream fix for unlocked session after crash (LP: #1064584)
* Note that the new GNOME Classic mode (which requires installing
  gnome-shell-extensions) won't work until gnome-session 3.8 is
  available in Ubuntu

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
 * Copyright (C) 2006-2008 Lennart Poettering
4
4
 * Copyright (C) 2008 Sjoerd Simons <sjoerd@luon.net>
5
5
 * Copyright (C) 2008 William Jon McCann
 
6
 * Copyright (C) 2012 Conor Curran
6
7
 *
7
8
 * This program is free software; you can redistribute it and/or modify
8
9
 * it under the terms of the GNU General Public License as published by
43
44
#include "gvc-mixer-card-private.h"
44
45
#include "gvc-channel-map-private.h"
45
46
#include "gvc-mixer-control-private.h"
 
47
#include "gvc-mixer-ui-device.h"
46
48
 
47
49
#define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate))
48
50
 
80
82
        GHashTable       *clients;
81
83
        GHashTable       *cards;
82
84
 
83
 
        GvcMixerStream   *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
 
85
        GvcMixerStream   *new_default_sink_stream; /* new default sink stream, used in gvc_mixer_control_set_default_sink () */
 
86
        GvcMixerStream   *new_default_source_stream; /* new default source stream, used in gvc_mixer_control_set_default_source () */
 
87
 
 
88
        GHashTable       *ui_outputs; /* UI visible outputs */
 
89
        GHashTable       *ui_inputs;  /* UI visible inputs */
 
90
 
 
91
        /* When we change profile on a device that is not the server default sink,
 
92
         * it will jump back to the default sink set by the server to prevent the
 
93
         * audio setup from being 'outputless'.
 
94
         *
 
95
         * All well and good but then when we get the new stream created for the
 
96
         * new profile how do we know that this is the intended default or selected
 
97
         * device the user wishes to use. */
 
98
        guint            profile_swapping_device_id;
84
99
 
85
100
        GvcMixerControlState state;
86
101
};
93
108
        CARD_REMOVED,
94
109
        DEFAULT_SINK_CHANGED,
95
110
        DEFAULT_SOURCE_CHANGED,
 
111
        ACTIVE_OUTPUT_UPDATE,
 
112
        ACTIVE_INPUT_UPDATE,
 
113
        OUTPUT_ADDED,
 
114
        INPUT_ADDED,
 
115
        OUTPUT_REMOVED,
 
116
        INPUT_REMOVED,
96
117
        LAST_SIGNAL
97
118
};
98
119
 
132
153
 
133
154
static void
134
155
gvc_mixer_control_stream_restore_cb (pa_context *c,
 
156
                                     GvcMixerStream *new_stream,
135
157
                                     const pa_ext_stream_restore_info *info,
136
 
                                     int eol,
137
 
                                     void *userdata)
 
158
                                     GvcMixerControl *control)
138
159
{
139
160
        pa_operation *o;
140
 
        GvcMixerControl *control = (GvcMixerControl *) userdata;
141
161
        pa_ext_stream_restore_info new_info;
142
162
 
143
 
        if (eol || control->priv->new_default_stream == NULL)
 
163
        if (new_stream == NULL)
144
164
                return;
145
165
 
146
166
        new_info.name = info->name;
148
168
        new_info.volume = info->volume;
149
169
        new_info.mute = info->mute;
150
170
 
151
 
        new_info.device = gvc_mixer_stream_get_name (control->priv->new_default_stream);
 
171
        new_info.device = gvc_mixer_stream_get_name (new_stream);
152
172
 
153
173
        o = pa_ext_stream_restore_write (control->priv->pa_context,
154
174
                                         PA_UPDATE_REPLACE,
161
181
                return;
162
182
        }
163
183
 
164
 
        g_debug ("Changed default device for %s to %s", info->name, info->device);
 
184
        g_debug ("Changed default device for %s to %s", info->name, new_info.device);
165
185
 
166
186
        pa_operation_unref (o);
167
187
}
168
188
 
 
189
static void
 
190
gvc_mixer_control_stream_restore_sink_cb (pa_context *c,
 
191
                                          const pa_ext_stream_restore_info *info,
 
192
                                          int eol,
 
193
                                          void *userdata)
 
194
{
 
195
        GvcMixerControl *control = (GvcMixerControl *) userdata;
 
196
        if (eol || info == NULL || !g_str_has_prefix(info->name, "sink-input-by"))
 
197
                return;
 
198
        gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_sink_stream, info, control);
 
199
}
 
200
 
 
201
static void
 
202
gvc_mixer_control_stream_restore_source_cb (pa_context *c,
 
203
                                            const pa_ext_stream_restore_info *info,
 
204
                                            int eol,
 
205
                                            void *userdata)
 
206
{
 
207
        GvcMixerControl *control = (GvcMixerControl *) userdata;
 
208
        if (eol || info == NULL || !g_str_has_prefix(info->name, "source-output-by"))
 
209
                return;
 
210
        gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_source_stream, info, control);
 
211
}
 
212
 
 
213
/**
 
214
 * gvc_mixer_control_lookup_device_from_stream:
 
215
 * @control:
 
216
 * @stream:
 
217
 *
 
218
 * Returns: (transfer none): a #GvcUIDevice or %NULL
 
219
 */
 
220
GvcMixerUIDevice *
 
221
gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control,
 
222
                                             GvcMixerStream *stream)
 
223
{
 
224
        GList                   *devices, *d;
 
225
        gboolean                 is_network_stream;
 
226
        const GList             *ports;
 
227
        GvcMixerUIDevice        *ret;
 
228
 
 
229
        if (GVC_IS_MIXER_SOURCE (stream))
 
230
               devices = g_hash_table_get_values (control->priv->ui_inputs);
 
231
        else
 
232
               devices = g_hash_table_get_values (control->priv->ui_outputs);
 
233
 
 
234
        ret = NULL;
 
235
        ports = gvc_mixer_stream_get_ports (stream);
 
236
        is_network_stream = (ports == NULL);
 
237
 
 
238
        for (d = devices; d != NULL; d = d->next) {
 
239
                GvcMixerUIDevice *device = d->data;
 
240
                gint stream_id = G_MAXINT;
 
241
 
 
242
                g_object_get (G_OBJECT (device),
 
243
                             "stream-id", &stream_id,
 
244
                              NULL);
 
245
 
 
246
                if (is_network_stream &&
 
247
                    stream_id == gvc_mixer_stream_get_id (stream)) {
 
248
                        g_debug ("lookup device from stream - %s - it is a network_stream ",
 
249
                                 gvc_mixer_ui_device_get_description (device));
 
250
                        ret = device;
 
251
                        break;
 
252
                } else if (!is_network_stream) {
 
253
                        const GvcMixerStreamPort *port;
 
254
                        port = gvc_mixer_stream_get_port (stream);
 
255
 
 
256
                        if (stream_id == gvc_mixer_stream_get_id (stream) &&
 
257
                            g_strcmp0 (gvc_mixer_ui_device_get_port (device),
 
258
                                       port->port) == 0) {
 
259
                                g_debug ("lookup-device-from-stream found device: device description '%s', device port = '%s', device stream id %i AND stream port = '%s' stream id '%u' and stream description '%s'",
 
260
                                         gvc_mixer_ui_device_get_description (device),
 
261
                                         gvc_mixer_ui_device_get_port (device),
 
262
                                         stream_id,
 
263
                                         port->port,
 
264
                                         gvc_mixer_stream_get_id (stream),
 
265
                                         gvc_mixer_stream_get_description (stream));
 
266
                                ret = device;
 
267
                                break;
 
268
                        }
 
269
                }
 
270
        }
 
271
 
 
272
        g_debug ("gvc_mixer_control_lookup_device_from_stream - Could not find a device for stream '%s'",gvc_mixer_stream_get_description (stream));
 
273
 
 
274
        g_list_free (devices);
 
275
 
 
276
        return ret;
 
277
}
 
278
 
169
279
gboolean
170
280
gvc_mixer_control_set_default_sink (GvcMixerControl *control,
171
281
                                    GvcMixerStream  *stream)
175
285
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
176
286
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
177
287
 
 
288
        g_debug ("about to set default sink on server");
178
289
        o = pa_context_set_default_sink (control->priv->pa_context,
179
290
                                         gvc_mixer_stream_get_name (stream),
180
291
                                         NULL,
187
298
 
188
299
        pa_operation_unref (o);
189
300
 
190
 
        control->priv->new_default_stream = stream;
191
 
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
 
301
        control->priv->new_default_sink_stream = stream;
 
302
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_sink_stream);
192
303
 
193
304
        o = pa_ext_stream_restore_read (control->priv->pa_context,
194
 
                                        gvc_mixer_control_stream_restore_cb,
 
305
                                        gvc_mixer_control_stream_restore_sink_cb,
195
306
                                        control);
196
307
 
197
308
        if (o == NULL) {
209
320
gvc_mixer_control_set_default_source (GvcMixerControl *control,
210
321
                                      GvcMixerStream  *stream)
211
322
{
 
323
        GvcMixerUIDevice* input;
212
324
        pa_operation *o;
213
325
 
214
326
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
225
337
 
226
338
        pa_operation_unref (o);
227
339
 
 
340
        control->priv->new_default_source_stream = stream;
 
341
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_source_stream);
 
342
 
 
343
        o = pa_ext_stream_restore_read (control->priv->pa_context,
 
344
                                        gvc_mixer_control_stream_restore_source_cb,
 
345
                                        control);
 
346
 
 
347
        if (o == NULL) {
 
348
                g_warning ("pa_ext_stream_restore_read() failed: %s",
 
349
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
 
350
                return FALSE;
 
351
        }
 
352
 
 
353
        pa_operation_unref (o);
 
354
 
 
355
        /* source change successful, update the UI. */
 
356
        input = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
357
        g_signal_emit (G_OBJECT (control),
 
358
                       signals[ACTIVE_INPUT_UPDATE],
 
359
                       0,
 
360
                       gvc_mixer_ui_device_get_id (input));
 
361
 
228
362
        return TRUE;
229
363
}
230
364
 
314
448
        return gvc_mixer_control_lookup_id (control->priv->cards, id);
315
449
}
316
450
 
 
451
/**
 
452
 * gvc_mixer_control_lookup_output_id:
 
453
 * @control:
 
454
 * @id:
 
455
 *
 
456
 * Returns: (transfer none):
 
457
 */
 
458
GvcMixerUIDevice *
 
459
gvc_mixer_control_lookup_output_id (GvcMixerControl *control,
 
460
                                    guint            id)
 
461
{
 
462
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
463
 
 
464
        return gvc_mixer_control_lookup_id (control->priv->ui_outputs, id);
 
465
}
 
466
 
 
467
/**
 
468
 * gvc_mixer_control_lookup_input_id:
 
469
 * @control:
 
470
 * @id:
 
471
 *
 
472
 * Returns: (transfer none):
 
473
 */
 
474
GvcMixerUIDevice *
 
475
gvc_mixer_control_lookup_input_id (GvcMixerControl *control,
 
476
                                    guint            id)
 
477
{
 
478
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
479
 
 
480
        return gvc_mixer_control_lookup_id (control->priv->ui_inputs, id);
 
481
}
 
482
 
 
483
/**
 
484
 * gvc_mixer_control_get_stream_from_device:
 
485
 * @control:
 
486
 * @device:
 
487
 *
 
488
 * Returns: (transfer none):
 
489
 */
 
490
GvcMixerStream *
 
491
gvc_mixer_control_get_stream_from_device (GvcMixerControl *control,
 
492
                                          GvcMixerUIDevice *device)
 
493
{
 
494
        gint stream_id;
 
495
 
 
496
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 
497
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
 
498
 
 
499
        stream_id = gvc_mixer_ui_device_get_stream_id (device);
 
500
 
 
501
        if (stream_id == GVC_MIXER_UI_DEVICE_INVALID) {
 
502
                g_debug ("gvc_mixer_control_get_stream_from_device - device has a null stream");
 
503
                return NULL;
 
504
        }
 
505
        return gvc_mixer_control_lookup_stream_id (control, stream_id);
 
506
}
 
507
 
 
508
/**
 
509
 * gvc_mixer_control_change_profile_on_selected_device:
 
510
 * @control:
 
511
 * @device:
 
512
 * @profile: Can be null if any profile present on this port is okay
 
513
 *
 
514
 * Returns: This method will attempt to swap the profile on the card of
 
515
 * the device with given profile name.  If successfull it will set the
 
516
 * preferred profile on that device so as we know the next time the user
 
517
 * moves to that device it should have this profile active.
 
518
 */
 
519
gboolean
 
520
gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl  *control,
 
521
                                                     GvcMixerUIDevice *device,
 
522
                                                     const gchar      *profile)
 
523
{
 
524
        const gchar         *best_profile;
 
525
        GvcMixerCardProfile *current_profile;
 
526
        GvcMixerCard        *card;
 
527
 
 
528
        g_object_get (G_OBJECT (device), "card", &card, NULL);
 
529
        current_profile = gvc_mixer_card_get_profile (card);
 
530
 
 
531
        if (current_profile)
 
532
                best_profile = gvc_mixer_ui_device_get_best_profile (device, profile, current_profile->profile);
 
533
        else
 
534
                best_profile = profile;
 
535
 
 
536
        g_assert (best_profile);
 
537
 
 
538
        g_debug ("Selected '%s', moving to profile '%s' on card '%s' on stream id %i",
 
539
                profile ? profile : "(any)", best_profile,
 
540
                gvc_mixer_card_get_name (card),
 
541
                gvc_mixer_ui_device_get_stream_id (device));
 
542
 
 
543
        g_debug ("default sink name = %s and default sink id %u",
 
544
                 control->priv->default_sink_name,
 
545
                 control->priv->default_sink_id);
 
546
 
 
547
        control->priv->profile_swapping_device_id = gvc_mixer_ui_device_get_id (device);
 
548
 
 
549
        if (gvc_mixer_card_change_profile (card, best_profile)) {
 
550
                gvc_mixer_ui_device_set_user_preferred_profile (device, best_profile);
 
551
                return TRUE;
 
552
        }
 
553
        return FALSE;
 
554
}
 
555
 
 
556
/**
 
557
 * gvc_mixer_control_change_output:
 
558
 * @control:
 
559
 * @output:
 
560
 * This method is called from the UI when the user selects a previously unselected device.
 
561
 * - Firstly it queries the stream from the device.
 
562
 *   - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources)
 
563
 *   In the scenario of a NULL stream on the device
 
564
 *        - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
 
565
 *        - It then caches this device in control->priv->cached_desired_output_id so that when the update_sink triggered
 
566
 *          from when we attempt to change profile we will know exactly what device to highlight on that stream.
 
567
 *        - It attempts to swap the profile on the card from that device and returns.
 
568
 * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
 
569
 * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
 
570
 *   it will attempt to change the port on that stream to be same as the device. If this fails it will return.
 
571
 * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active output device.
 
572
 */
 
573
void
 
574
gvc_mixer_control_change_output (GvcMixerControl *control,
 
575
                                 GvcMixerUIDevice* output)
 
576
{
 
577
        GvcMixerStream           *stream;
 
578
        GvcMixerStream           *default_stream;
 
579
        const GvcMixerStreamPort *active_port;
 
580
        const gchar              *output_port;
 
581
 
 
582
        g_debug ("control change output");
 
583
 
 
584
        stream = gvc_mixer_control_get_stream_from_device (control, output);
 
585
        if (stream == NULL) {
 
586
                gvc_mixer_control_change_profile_on_selected_device (control,
 
587
                        output, NULL);
 
588
                return;
 
589
        }
 
590
 
 
591
        /* Handle a network sink as a portless or cardless device */
 
592
        if (!gvc_mixer_ui_device_has_ports (output)) {
 
593
                g_debug ("Did we try to move to a software/bluetooth sink ?");
 
594
                if (gvc_mixer_control_set_default_sink (control, stream)) {
 
595
                        /* sink change was successful,  update the UI.*/
 
596
                        g_signal_emit (G_OBJECT (control),
 
597
                                       signals[ACTIVE_OUTPUT_UPDATE],
 
598
                                       0,
 
599
                                       gvc_mixer_ui_device_get_id (output));
 
600
                }
 
601
                else {
 
602
                        g_warning ("Failed to set default sink with stream from output %s",
 
603
                                   gvc_mixer_ui_device_get_description (output));
 
604
                }
 
605
                return;
 
606
        }
 
607
 
 
608
        active_port = gvc_mixer_stream_get_port (stream);
 
609
        output_port = gvc_mixer_ui_device_get_port (output);
 
610
        /* First ensure the correct port is active on the sink */
 
611
        if (g_strcmp0 (active_port->port, output_port) != 0) {
 
612
                g_debug ("Port change, switch to = %s", output_port);
 
613
                if (gvc_mixer_stream_change_port (stream, output_port) == FALSE) {
 
614
                        g_warning ("Could not change port !");
 
615
                        return;
 
616
                }
 
617
        }
 
618
 
 
619
        default_stream = gvc_mixer_control_get_default_sink (control);
 
620
 
 
621
        /* Finally if we are not on the correct stream, swap over. */
 
622
        if (stream != default_stream) {
 
623
                GvcMixerUIDevice* output;
 
624
 
 
625
                g_debug ("Attempting to swap over to stream %s ",
 
626
                         gvc_mixer_stream_get_description (stream));
 
627
                if (gvc_mixer_control_set_default_sink (control, stream)) {
 
628
                        output = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
629
                        g_signal_emit (G_OBJECT (control),
 
630
                                       signals[ACTIVE_OUTPUT_UPDATE],
 
631
                                       0,
 
632
                                       gvc_mixer_ui_device_get_id (output));
 
633
                } else {
 
634
                        /* If the move failed for some reason reset the UI. */
 
635
                        output = gvc_mixer_control_lookup_device_from_stream (control, default_stream);
 
636
                        g_signal_emit (G_OBJECT (control),
 
637
                                       signals[ACTIVE_OUTPUT_UPDATE],
 
638
                                       0,
 
639
                                       gvc_mixer_ui_device_get_id (output));
 
640
                }
 
641
        }
 
642
}
 
643
 
 
644
 
 
645
/**
 
646
 * gvc_mixer_control_change_input:
 
647
 * @control:
 
648
 * @input:
 
649
 * This method is called from the UI when the user selects a previously unselected device.
 
650
 * - Firstly it queries the stream from the device.
 
651
 *   - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources)
 
652
 *   In the scenario of a NULL stream on the device
 
653
 *        - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
 
654
 *        - It then caches this device in control->priv->cached_desired_input_id so that when the update_source triggered
 
655
 *          from when we attempt to change profile we will know exactly what device to highlight on that stream.
 
656
 *        - It attempts to swap the profile on the card from that device and returns.
 
657
 * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
 
658
 * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
 
659
 *   it will attempt to change the port on that stream to be same as the device. If this fails it will return.
 
660
 * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active input device.
 
661
 */
 
662
void
 
663
gvc_mixer_control_change_input (GvcMixerControl *control,
 
664
                                GvcMixerUIDevice* input)
 
665
{
 
666
        GvcMixerStream           *stream;
 
667
        GvcMixerStream           *default_stream;
 
668
        const GvcMixerStreamPort *active_port;
 
669
        const gchar              *input_port;
 
670
 
 
671
        stream = gvc_mixer_control_get_stream_from_device (control, input);
 
672
        if (stream == NULL) {
 
673
                gvc_mixer_control_change_profile_on_selected_device (control,
 
674
                        input, NULL);
 
675
                return;
 
676
        }
 
677
 
 
678
        /* Handle a network sink as a portless/cardless device */
 
679
        if (!gvc_mixer_ui_device_has_ports (input)) {
 
680
                g_debug ("Did we try to move to a software/bluetooth source ?");
 
681
                if (! gvc_mixer_control_set_default_source (control, stream)) {
 
682
                        g_warning ("Failed to set default source with stream from input %s",
 
683
                                   gvc_mixer_ui_device_get_description (input));
 
684
                }
 
685
                return;
 
686
        }
 
687
 
 
688
        active_port = gvc_mixer_stream_get_port (stream);
 
689
        input_port = gvc_mixer_ui_device_get_port (input);
 
690
        /* First ensure the correct port is active on the sink */
 
691
        if (g_strcmp0 (active_port->port, input_port) != 0) {
 
692
                g_debug ("Port change, switch to = %s", input_port);
 
693
                if (gvc_mixer_stream_change_port (stream, input_port) == FALSE) {
 
694
                        g_warning ("Could not change port!");
 
695
                        return;
 
696
                }
 
697
        }
 
698
 
 
699
        default_stream = gvc_mixer_control_get_default_source (control);
 
700
 
 
701
        /* Finally if we are not on the correct stream, swap over. */
 
702
        if (stream != default_stream) {
 
703
                g_debug ("change-input - attempting to swap over to stream %s",
 
704
                         gvc_mixer_stream_get_description (stream));
 
705
                gvc_mixer_control_set_default_source (control, stream);
 
706
        }
 
707
}
 
708
 
 
709
 
317
710
static void
318
711
listify_hash_values_hfunc (gpointer key,
319
712
                           gpointer value,
511
904
        return control->priv->state;
512
905
}
513
906
 
 
907
static void
 
908
on_default_source_port_notify (GObject        *object,
 
909
                               GParamSpec     *pspec,
 
910
                               GvcMixerControl *control)
 
911
{
 
912
        char             *port;
 
913
        GvcMixerUIDevice *input;
 
914
 
 
915
        g_object_get (object, "port", &port, NULL);
 
916
        input = gvc_mixer_control_lookup_device_from_stream (control,
 
917
                                                             GVC_MIXER_STREAM (object));
 
918
 
 
919
        g_debug ("on_default_source_port_notify - moved to port '%s' which SHOULD ?? correspond to output '%s'",
 
920
                 port,
 
921
                 gvc_mixer_ui_device_get_description (input));
 
922
 
 
923
        g_signal_emit (G_OBJECT (control),
 
924
                       signals[ACTIVE_INPUT_UPDATE],
 
925
                       0,
 
926
                       gvc_mixer_ui_device_get_id (input));
 
927
 
 
928
        g_free (port);
 
929
}
 
930
 
514
931
 
515
932
static void
516
933
_set_default_source (GvcMixerControl *control,
531
948
        new_id = gvc_mixer_stream_get_id (stream);
532
949
 
533
950
        if (control->priv->default_source_id != new_id) {
 
951
                GvcMixerUIDevice *input;
534
952
                control->priv->default_source_id = new_id;
535
953
                control->priv->default_source_is_set = TRUE;
536
954
                g_signal_emit (control,
537
955
                               signals[DEFAULT_SOURCE_CHANGED],
538
956
                               0,
539
957
                               new_id);
540
 
        }
 
958
 
 
959
                if (control->priv->default_source_is_set) {
 
960
                        g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control),
 
961
                                                              on_default_source_port_notify,
 
962
                                                              control);
 
963
                }
 
964
 
 
965
                g_signal_connect (stream,
 
966
                                  "notify::port",
 
967
                                  G_CALLBACK (on_default_source_port_notify),
 
968
                                  control);
 
969
 
 
970
                input = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
971
 
 
972
                g_signal_emit (G_OBJECT (control),
 
973
                               signals[ACTIVE_INPUT_UPDATE],
 
974
                               0,
 
975
                               gvc_mixer_ui_device_get_id (input));
 
976
        }
 
977
}
 
978
 
 
979
static void
 
980
on_default_sink_port_notify (GObject        *object,
 
981
                             GParamSpec     *pspec,
 
982
                             GvcMixerControl *control)
 
983
{
 
984
        char             *port;
 
985
        GvcMixerUIDevice *output;
 
986
 
 
987
        g_object_get (object, "port", &port, NULL);
 
988
 
 
989
        output = gvc_mixer_control_lookup_device_from_stream (control,
 
990
                                                              GVC_MIXER_STREAM (object));
 
991
        if (output != NULL) {
 
992
                g_debug ("on_default_sink_port_notify - moved to port %s - which SHOULD correspond to output %s",
 
993
                         port,
 
994
                         gvc_mixer_ui_device_get_description (output));
 
995
                g_signal_emit (G_OBJECT (control),
 
996
                               signals[ACTIVE_OUTPUT_UPDATE],
 
997
                               0,
 
998
                               gvc_mixer_ui_device_get_id (output));
 
999
        }
 
1000
        g_free (port);
541
1001
}
542
1002
 
543
1003
static void
563
1023
        new_id = gvc_mixer_stream_get_id (stream);
564
1024
 
565
1025
        if (control->priv->default_sink_id != new_id) {
 
1026
                GvcMixerUIDevice *output;
 
1027
                if (control->priv->default_sink_is_set) {
 
1028
                        g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control),
 
1029
                                                              on_default_sink_port_notify,
 
1030
                                                              control);
 
1031
                }
 
1032
 
566
1033
                control->priv->default_sink_id = new_id;
 
1034
 
567
1035
                control->priv->default_sink_is_set = TRUE;
568
1036
                g_signal_emit (control,
569
1037
                               signals[DEFAULT_SINK_CHANGED],
570
1038
                               0,
571
1039
                               new_id);
 
1040
 
 
1041
                g_signal_connect (stream,
 
1042
                                  "notify::port",
 
1043
                                  G_CALLBACK (on_default_sink_port_notify),
 
1044
                                  control);
 
1045
 
 
1046
                output = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
1047
 
 
1048
                g_debug ("active_sink change");
 
1049
 
 
1050
                g_signal_emit (G_OBJECT (control),
 
1051
                               signals[ACTIVE_OUTPUT_UPDATE],
 
1052
                               0,
 
1053
                               gvc_mixer_ui_device_get_id (output));
572
1054
        }
573
1055
}
574
1056
 
590
1072
        return FALSE;
591
1073
}
592
1074
 
593
 
static GvcMixerStream  *
 
1075
static GvcMixerStream *
594
1076
find_stream_for_name (GvcMixerControl *control,
595
1077
                      const char      *name)
596
1078
{
659
1141
                update_default_source_from_name (control, info->default_source_name);
660
1142
        }
661
1143
        if (info->default_sink_name != NULL) {
 
1144
                g_debug ("update server");
662
1145
                update_default_sink_from_name (control, info->default_sink_name);
663
1146
        }
664
1147
}
701
1184
                       gvc_mixer_stream_get_id (stream));
702
1185
}
703
1186
 
 
1187
/* This method will match individual stream ports against its corresponding device
 
1188
 * It does this by:
 
1189
 * - iterates through our devices and finds the one where the card-id on the device is the same as the card-id on the stream
 
1190
 *   and the port-name on the device is the same as the streamport-name.
 
1191
 * This should always find a match and is used exclusively by sync_devices().
 
1192
 */
 
1193
static gboolean
 
1194
match_stream_with_devices (GvcMixerControl    *control,
 
1195
                           GvcMixerStreamPort *stream_port,
 
1196
                           GvcMixerStream     *stream)
 
1197
{
 
1198
        GList                   *devices, *d;
 
1199
        guint                    stream_card_id;
 
1200
        guint                    stream_id;
 
1201
        gboolean                 in_possession = FALSE;
 
1202
 
 
1203
        stream_id      =  gvc_mixer_stream_get_id (stream);
 
1204
        stream_card_id =  gvc_mixer_stream_get_card_index (stream);
 
1205
 
 
1206
        devices  = g_hash_table_get_values (GVC_IS_MIXER_SOURCE (stream) ? control->priv->ui_inputs : control->priv->ui_outputs);
 
1207
 
 
1208
        for (d = devices; d != NULL; d = d->next) {
 
1209
                GvcMixerUIDevice *device;
 
1210
                gint              device_stream_id;
 
1211
                gchar            *device_port_name;
 
1212
                gchar            *origin;
 
1213
                gchar            *description;
 
1214
                GvcMixerCard     *card;
 
1215
                gint              card_id;
 
1216
 
 
1217
                device = d->data;
 
1218
                g_object_get (G_OBJECT (device),
 
1219
                             "stream-id", &device_stream_id,
 
1220
                             "card", &card,
 
1221
                             "origin", &origin,
 
1222
                             "description", &description,
 
1223
                             "port-name", &device_port_name,
 
1224
                              NULL);
 
1225
 
 
1226
                card_id = gvc_mixer_card_get_index (card);
 
1227
 
 
1228
                g_debug ("Attempt to match_stream update_with_existing_outputs - Try description : '%s', origin : '%s', device port name : '%s', card : %p, AGAINST stream port: '%s', sink card id %i",
 
1229
                         description,
 
1230
                         origin,
 
1231
                         device_port_name,
 
1232
                         card,
 
1233
                         stream_port->port,
 
1234
                         stream_card_id);
 
1235
 
 
1236
                if (stream_card_id == card_id &&
 
1237
                    g_strcmp0 (device_port_name, stream_port->port) == 0) {
 
1238
                        g_debug ("Match device with stream: We have a match with description: '%s', origin: '%s', cached already with device id %u, so set stream id to %i",
 
1239
                                 description,
 
1240
                                 origin,
 
1241
                                 gvc_mixer_ui_device_get_id (device),
 
1242
                                 stream_id);
 
1243
 
 
1244
                        g_object_set (G_OBJECT (device),
 
1245
                                      "stream-id", (gint)stream_id,
 
1246
                                      NULL);
 
1247
                        in_possession = TRUE;
 
1248
                }
 
1249
 
 
1250
                g_free (device_port_name);
 
1251
                g_free (origin);
 
1252
                g_free (description);
 
1253
 
 
1254
                if (in_possession == TRUE)
 
1255
                        break;
 
1256
        }
 
1257
 
 
1258
        g_list_free (devices);
 
1259
        return in_possession;
 
1260
}
 
1261
 
 
1262
/*
 
1263
 * This method attempts to match a sink or source with its relevant UI device.
 
1264
 * GvcMixerStream can represent both a sink or source.
 
1265
 * Using static card port introspection implies that we know beforehand what
 
1266
 * outputs and inputs are available to the user.
 
1267
 * But that does not mean that all of these inputs and outputs are available to be used.
 
1268
 * For instance we might be able to see that there is a HDMI port available but if
 
1269
 * we are on the default analog stereo output profile there is no valid sink for
 
1270
 * that HDMI device. We first need to change profile and when update_sink() is called
 
1271
 * only then can we match the new hdmi sink with its corresponding device.
 
1272
 *
 
1273
 * Firstly it checks to see if the incoming stream has no ports.
 
1274
 * - If a stream has no ports but has a valid card ID (bluetooth), it will attempt
 
1275
 *   to match the device with the stream using the card id.
 
1276
 * - If a stream has no ports and no valid card id, it goes ahead and makes a new
 
1277
 *   device (software/network devices are only detectable at the sink/source level)
 
1278
 * If the stream has ports it will match each port against the stream using match_stream_with_devices().
 
1279
 *
 
1280
 * This method should always find a match.
 
1281
 */
 
1282
static void
 
1283
sync_devices (GvcMixerControl *control,
 
1284
              GvcMixerStream* stream)
 
1285
{
 
1286
        /* Go through ports to see what outputs can be created. */
 
1287
        const GList *stream_ports;
 
1288
        const GList *n = NULL;
 
1289
        gboolean     is_output = !GVC_IS_MIXER_SOURCE (stream);
 
1290
        gint         stream_port_count = 0;
 
1291
 
 
1292
        stream_ports = gvc_mixer_stream_get_ports (stream);
 
1293
 
 
1294
        if (stream_ports == NULL) {
 
1295
                GvcMixerUIDevice *device;
 
1296
                /* Bluetooth, no ports but a valid card */
 
1297
                if (gvc_mixer_stream_get_card_index (stream) != PA_INVALID_INDEX) {
 
1298
                        GList *devices, *d;
 
1299
                        gboolean in_possession = FALSE;
 
1300
 
 
1301
                        devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);
 
1302
 
 
1303
                        for (d = devices; d != NULL; d = d->next) {
 
1304
                                GvcMixerCard *card;
 
1305
                                gint card_id;
 
1306
 
 
1307
                                device = d->data;
 
1308
 
 
1309
                                g_object_get (G_OBJECT (device),
 
1310
                                             "card", &card,
 
1311
                                              NULL);
 
1312
                                card_id = gvc_mixer_card_get_index (card);
 
1313
                                g_debug ("sync devices, device description - '%s', device card id - %i, stream description - %s, stream card id - %i",
 
1314
                                         gvc_mixer_ui_device_get_description (device),
 
1315
                                         card_id,
 
1316
                                         gvc_mixer_stream_get_description (stream),
 
1317
                                         gvc_mixer_stream_get_card_index (stream));
 
1318
                                if (card_id == gvc_mixer_stream_get_card_index (stream)) {
 
1319
                                        in_possession = TRUE;
 
1320
                                        break;
 
1321
                                }
 
1322
                        }
 
1323
                        g_list_free (devices);
 
1324
 
 
1325
                        if (!in_possession) {
 
1326
                                g_warning ("Couldn't match the portless stream (with card) - '%s' is it an input ? -> %i, streams card id -> %i",
 
1327
                                           gvc_mixer_stream_get_description (stream),
 
1328
                                           GVC_IS_MIXER_SOURCE (stream),
 
1329
                                           gvc_mixer_stream_get_card_index (stream));
 
1330
                                return;
 
1331
                        }
 
1332
 
 
1333
                        g_object_set (G_OBJECT (device),
 
1334
                                      "stream-id", (gint)gvc_mixer_stream_get_id (stream),
 
1335
                                      "description", gvc_mixer_stream_get_description (stream),
 
1336
                                      "origin", "", /*Leave it empty for these special cases*/
 
1337
                                      "port-name", NULL,
 
1338
                                      "port-available", TRUE,
 
1339
                                      NULL);
 
1340
                } else { /* Network sink/source has no ports and no card. */
 
1341
                        GObject *object;
 
1342
 
 
1343
                        object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
 
1344
                                               "stream-id", (gint)gvc_mixer_stream_get_id (stream),
 
1345
                                               "description", gvc_mixer_stream_get_description (stream),
 
1346
                                               "origin", "", /* Leave it empty for these special cases */
 
1347
                                               "port-name", NULL,
 
1348
                                               "port-available", TRUE,
 
1349
                                                NULL);
 
1350
                        device = GVC_MIXER_UI_DEVICE (object);
 
1351
 
 
1352
                        g_hash_table_insert (is_output ? control->priv->ui_outputs : control->priv->ui_inputs,
 
1353
                                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)),
 
1354
                                             g_object_ref (device));
 
1355
 
 
1356
                }
 
1357
                g_signal_emit (G_OBJECT (control),
 
1358
                               signals[is_output ? OUTPUT_ADDED : INPUT_ADDED],
 
1359
                               0,
 
1360
                               gvc_mixer_ui_device_get_id (device));
 
1361
 
 
1362
                return;
 
1363
        }
 
1364
 
 
1365
        /* Go ahead and make sure to match each port against a previously created device */
 
1366
        for (n = stream_ports; n != NULL; n = n->next) {
 
1367
 
 
1368
                GvcMixerStreamPort *stream_port;
 
1369
                stream_port = n->data;
 
1370
                stream_port_count ++;
 
1371
 
 
1372
                if (match_stream_with_devices (control, stream_port, stream))
 
1373
                        continue;
 
1374
 
 
1375
                g_warning ("Sync_devices: Failed to match stream id: %u, description: '%s', origin: '%s'",
 
1376
                           gvc_mixer_stream_get_id (stream),
 
1377
                           stream_port->human_port,
 
1378
                           gvc_mixer_stream_get_description (stream));
 
1379
        }
 
1380
}
 
1381
 
704
1382
static void
705
1383
set_icon_name_from_proplist (GvcMixerStream *stream,
706
1384
                             pa_proplist    *l,
753
1431
        gvc_mixer_stream_set_icon_name (stream, t);
754
1432
}
755
1433
 
 
1434
/*
 
1435
 * Called when anything changes with a sink.
 
1436
 */
756
1437
static void
757
1438
update_sink (GvcMixerControl    *control,
758
1439
             const pa_sink_info *info)
759
1440
{
760
 
        GvcMixerStream *stream;
 
1441
        GvcMixerStream  *stream;
761
1442
        gboolean        is_new;
762
1443
        pa_volume_t     max_volume;
763
 
        GvcChannelMap  *map;
 
1444
        GvcChannelMap   *map;
764
1445
        char            map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
765
1446
 
766
1447
        pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
776
1457
        is_new = FALSE;
777
1458
        stream = g_hash_table_lookup (control->priv->sinks,
778
1459
                                      GUINT_TO_POINTER (info->index));
 
1460
 
779
1461
        if (stream == NULL) {
780
 
#if PA_MICRO > 15
781
1462
                GList *list = NULL;
782
1463
                guint i;
783
 
#endif /* PA_MICRO > 15 */
784
1464
 
785
1465
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
786
1466
                stream = gvc_mixer_sink_new (control->priv->pa_context,
787
1467
                                             info->index,
788
1468
                                             map);
789
 
#if PA_MICRO > 15
 
1469
 
790
1470
                for (i = 0; i < info->n_ports; i++) {
791
1471
                        GvcMixerStreamPort *port;
792
1472
 
793
 
                        port = g_new0 (GvcMixerStreamPort, 1);
 
1473
                        port = g_slice_new0 (GvcMixerStreamPort);
794
1474
                        port->port = g_strdup (info->ports[i]->name);
795
1475
                        port->human_port = g_strdup (info->ports[i]->description);
796
1476
                        port->priority = info->ports[i]->priority;
 
1477
                        port->available = info->ports[i]->available != PA_PORT_AVAILABLE_NO;
 
1478
 
797
1479
                        list = g_list_prepend (list, port);
798
1480
                }
799
1481
                gvc_mixer_stream_set_ports (stream, list);
800
 
#endif /* PA_MICRO > 15 */
 
1482
 
801
1483
                g_object_unref (map);
802
1484
                is_new = TRUE;
 
1485
 
803
1486
        } else if (gvc_mixer_stream_is_running (stream)) {
804
1487
                /* Ignore events if volume changes are outstanding */
805
1488
                g_debug ("Ignoring event, volume changes are outstanding");
811
1494
        gvc_mixer_stream_set_card_index (stream, info->card);
812
1495
        gvc_mixer_stream_set_description (stream, info->description);
813
1496
        set_icon_name_from_proplist (stream, info->proplist, "audio-card");
 
1497
        gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
 
1498
        gvc_mixer_stream_set_sysfs_path (stream, pa_proplist_gets (info->proplist, "sysfs.path"));
814
1499
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
815
1500
        gvc_mixer_stream_set_is_muted (stream, info->mute);
816
1501
        gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
817
1502
        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
818
 
#if PA_MICRO > 15
819
 
        if (info->active_port != NULL)
820
 
                gvc_mixer_stream_set_port (stream, info->active_port->name);
821
 
#endif /* PA_MICRO > 15 */
 
1503
 
 
1504
        /* Messy I know but to set the port everytime regardless of whether it has changed will cost us a
 
1505
         * port change notify signal which causes the frontend to resync.
 
1506
         * Only update the UI when something has changed. */
 
1507
        if (info->active_port != NULL) {
 
1508
                if (is_new)
 
1509
                        gvc_mixer_stream_set_port (stream, info->active_port->name);
 
1510
                else {
 
1511
                        const GvcMixerStreamPort *active_port;
 
1512
                        active_port = gvc_mixer_stream_get_port (stream);
 
1513
                        if (active_port == NULL ||
 
1514
                            g_strcmp0 (active_port->port, info->active_port->name) != 0) {
 
1515
                                g_debug ("update sink - apparently a port update");
 
1516
                                gvc_mixer_stream_set_port (stream, info->active_port->name);
 
1517
                        }
 
1518
                }
 
1519
        }
822
1520
 
823
1521
        if (is_new) {
 
1522
                g_debug ("update sink - is new");
 
1523
 
824
1524
                g_hash_table_insert (control->priv->sinks,
825
1525
                                     GUINT_TO_POINTER (info->index),
826
1526
                                     g_object_ref (stream));
827
1527
                add_stream (control, stream);
 
1528
                /* Always sink on a new stream to able to assign the right stream id
 
1529
                 * to the appropriate outputs (multiple potential outputs per stream). */
 
1530
                sync_devices (control, stream);
 
1531
        }
 
1532
 
 
1533
        /*
 
1534
         * When we change profile on a device that is not the server default sink,
 
1535
         * it will jump back to the default sink set by the server to prevent the audio setup from being 'outputless'.
 
1536
         * All well and good but then when we get the new stream created for the new profile how do we know
 
1537
         * that this is the intended default or selected device the user wishes to use.
 
1538
         * This is messy but it's the only reliable way that it can be done without ripping the whole thing apart.
 
1539
         */
 
1540
        if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
 
1541
                GvcMixerUIDevice *dev = NULL;
 
1542
                dev = gvc_mixer_control_lookup_output_id (control, control->priv->profile_swapping_device_id);
 
1543
                if (dev != NULL) {
 
1544
                        /* now check to make sure this new stream is the same stream just matched and set on the device object */
 
1545
                        if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
 
1546
                                g_debug ("Looks like we profile swapped on a non server default sink");
 
1547
                                gvc_mixer_control_set_default_sink (control, stream);
 
1548
                        }
 
1549
                }
 
1550
                control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
828
1551
        }
829
1552
 
830
1553
        if (control->priv->default_sink_name != NULL
835
1558
 
836
1559
        if (map == NULL)
837
1560
                map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
 
1561
 
838
1562
        gvc_channel_map_volume_changed (map, &info->volume, FALSE);
839
1563
}
840
1564
 
863
1587
        stream = g_hash_table_lookup (control->priv->sources,
864
1588
                                      GUINT_TO_POINTER (info->index));
865
1589
        if (stream == NULL) {
866
 
#if PA_MICRO > 15
867
1590
                GList *list = NULL;
868
1591
                guint i;
869
 
#endif /* PA_MICRO > 15 */
870
1592
                GvcChannelMap *map;
871
1593
 
872
1594
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
873
1595
                stream = gvc_mixer_source_new (control->priv->pa_context,
874
1596
                                               info->index,
875
1597
                                               map);
876
 
#if PA_MICRO > 15
 
1598
 
877
1599
                for (i = 0; i < info->n_ports; i++) {
878
1600
                        GvcMixerStreamPort *port;
879
1601
 
880
 
                        port = g_new0 (GvcMixerStreamPort, 1);
 
1602
                        port = g_slice_new0 (GvcMixerStreamPort);
881
1603
                        port->port = g_strdup (info->ports[i]->name);
882
1604
                        port->human_port = g_strdup (info->ports[i]->description);
883
1605
                        port->priority = info->ports[i]->priority;
884
1606
                        list = g_list_prepend (list, port);
885
1607
                }
886
1608
                gvc_mixer_stream_set_ports (stream, list);
887
 
#endif /* PA_MICRO > 15 */
888
1609
 
889
1610
                g_object_unref (map);
890
1611
                is_new = TRUE;
900
1621
        gvc_mixer_stream_set_card_index (stream, info->card);
901
1622
        gvc_mixer_stream_set_description (stream, info->description);
902
1623
        set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
 
1624
        gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
903
1625
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
904
1626
        gvc_mixer_stream_set_is_muted (stream, info->mute);
905
1627
        gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
906
1628
        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
907
 
#if PA_MICRO > 15
908
 
        if (info->active_port != NULL)
909
 
                gvc_mixer_stream_set_port (stream, info->active_port->name);
910
 
#endif /* PA_MICRO > 15 */
 
1629
        g_debug ("update source");
 
1630
 
 
1631
        if (info->active_port != NULL) {
 
1632
                if (is_new)
 
1633
                        gvc_mixer_stream_set_port (stream, info->active_port->name);
 
1634
                else {
 
1635
                        const GvcMixerStreamPort *active_port;
 
1636
                        active_port = gvc_mixer_stream_get_port (stream);
 
1637
                        if (active_port == NULL ||
 
1638
                            g_strcmp0 (active_port->port, info->active_port->name) != 0) {
 
1639
                                g_debug ("update source - apparently a port update");
 
1640
                                gvc_mixer_stream_set_port (stream, info->active_port->name);
 
1641
                        }
 
1642
                }
 
1643
        }
911
1644
 
912
1645
        if (is_new) {
913
1646
                g_hash_table_insert (control->priv->sources,
914
1647
                                     GUINT_TO_POINTER (info->index),
915
1648
                                     g_object_ref (stream));
916
1649
                add_stream (control, stream);
917
 
        }
918
 
 
 
1650
                sync_devices (control, stream);
 
1651
        }
 
1652
 
 
1653
        if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
 
1654
                GvcMixerUIDevice *dev = NULL;
 
1655
 
 
1656
                dev = gvc_mixer_control_lookup_input_id (control, control->priv->profile_swapping_device_id);
 
1657
 
 
1658
                if (dev != NULL) {
 
1659
                        /* now check to make sure this new stream is the same stream just matched and set on the device object */
 
1660
                        if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
 
1661
                                g_debug ("Looks like we profile swapped on a non server default sink");
 
1662
                                gvc_mixer_control_set_default_source (control, stream);
 
1663
                        }
 
1664
                }
 
1665
                control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
 
1666
        }
919
1667
        if (control->priv->default_source_name != NULL
920
1668
            && info->name != NULL
921
1669
            && strcmp (control->priv->default_source_name, info->name) == 0) {
1111
1859
        return ret;
1112
1860
}
1113
1861
 
 
1862
/*
 
1863
 * A utility method to gather which card profiles are relevant to the port .
 
1864
 */
 
1865
static GList *
 
1866
determine_profiles_for_port (pa_card_port_info *port,
 
1867
                             GList* card_profiles)
 
1868
{
 
1869
        gint i;
 
1870
        GList *supported_profiles = NULL;
 
1871
        GList *p;
 
1872
        for (i = 0; i < port->n_profiles; i++) {
 
1873
                for (p = card_profiles; p != NULL; p = p->next) {
 
1874
                        GvcMixerCardProfile *prof;
 
1875
                        prof = p->data;
 
1876
                        if (g_strcmp0 (port->profiles[i]->name, prof->profile) == 0)
 
1877
                                supported_profiles = g_list_append (supported_profiles, prof);
 
1878
                }
 
1879
        }
 
1880
        g_debug ("%i profiles supported on port %s",
 
1881
                 g_list_length (supported_profiles),
 
1882
                 port->description);
 
1883
        return g_list_sort (supported_profiles, (GCompareFunc) gvc_mixer_card_profile_compare);
 
1884
}
 
1885
 
 
1886
static gboolean
 
1887
is_card_port_an_output (GvcMixerCardPort* port)
 
1888
{
 
1889
        return port->direction == PA_DIRECTION_OUTPUT ? TRUE : FALSE;
 
1890
}
 
1891
 
 
1892
/*
 
1893
 * This method will create a ui device for the given port.
 
1894
 */
 
1895
static void
 
1896
create_ui_device_from_port (GvcMixerControl* control,
 
1897
                            GvcMixerCardPort* port,
 
1898
                            GvcMixerCard* card)
 
1899
{
 
1900
        GvcMixerUIDeviceDirection  direction;
 
1901
        GObject                   *object;
 
1902
        GvcMixerUIDevice          *uidevice;
 
1903
        gboolean                   available = port->available != PA_PORT_AVAILABLE_NO;
 
1904
 
 
1905
        direction = (is_card_port_an_output (port) == TRUE) ? UIDeviceOutput : UIDeviceInput;
 
1906
 
 
1907
        object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
 
1908
                               "type", (uint)direction,
 
1909
                               "card", card,
 
1910
                               "port-name", port->port,
 
1911
                               "description", port->human_port,
 
1912
                               "origin", gvc_mixer_card_get_name (card),
 
1913
                               "port-available", available,
 
1914
                               NULL);
 
1915
 
 
1916
        uidevice = GVC_MIXER_UI_DEVICE (object);
 
1917
        gvc_mixer_ui_device_set_profiles (uidevice, port->profiles);
 
1918
 
 
1919
        g_hash_table_insert (is_card_port_an_output (port) ? control->priv->ui_outputs : control->priv->ui_inputs,
 
1920
                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (uidevice)),
 
1921
                             g_object_ref (uidevice));
 
1922
 
 
1923
 
 
1924
        if (available) {
 
1925
                g_signal_emit (G_OBJECT (control),
 
1926
                               signals[is_card_port_an_output (port) ? OUTPUT_ADDED : INPUT_ADDED],
 
1927
                               0,
 
1928
                               gvc_mixer_ui_device_get_id (uidevice));
 
1929
        }
 
1930
 
 
1931
        g_debug ("create_ui_device_from_port, direction %u, description '%s', origin '%s', port available %i", 
 
1932
                 direction,
 
1933
                 port->human_port,
 
1934
                 gvc_mixer_card_get_name (card),
 
1935
                 available);
 
1936
}
 
1937
 
 
1938
/*
 
1939
 * This method will match up GvcMixerCardPorts with existing devices.
 
1940
 * A match is achieved if the device's card-id and the port's card-id are the same
 
1941
 * && the device's port-name and the card-port's port member are the same.
 
1942
 * A signal is then sent adding or removing that device from the UI depending on the availability of the port.
 
1943
 */
 
1944
static void
 
1945
match_card_port_with_existing_device (GvcMixerControl   *control,
 
1946
                                      GvcMixerCardPort  *card_port,
 
1947
                                      GvcMixerCard      *card,
 
1948
                                      gboolean           available)
 
1949
{
 
1950
        GList                   *d;
 
1951
        GList                   *devices;
 
1952
        GvcMixerUIDevice        *device;
 
1953
        gboolean                 is_output = is_card_port_an_output (card_port);
 
1954
 
 
1955
        devices  = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);
 
1956
 
 
1957
        for (d = devices; d != NULL; d = d->next) {
 
1958
                GvcMixerCard *device_card;
 
1959
                gchar        *device_port_name;
 
1960
 
 
1961
                device = d->data;
 
1962
                g_object_get (G_OBJECT (device),
 
1963
                             "card", &device_card,
 
1964
                             "port-name", &device_port_name,
 
1965
                              NULL);
 
1966
 
 
1967
                if (g_strcmp0 (card_port->port, device_port_name) == 0 &&
 
1968
                        device_card == card) {
 
1969
                        g_debug ("Found the relevant device %s, update its port availability flag to %i, is_output %i",
 
1970
                                 device_port_name,
 
1971
                                 available,
 
1972
                                 is_output);
 
1973
                        g_object_set (G_OBJECT (device),
 
1974
                                      "port-available", available, NULL);
 
1975
                        g_signal_emit (G_OBJECT (control),
 
1976
                                       is_output ? signals[available ? OUTPUT_ADDED : OUTPUT_REMOVED] : signals[available ? INPUT_ADDED : INPUT_REMOVED],
 
1977
                                       0,
 
1978
                                       gvc_mixer_ui_device_get_id (device));
 
1979
               }
 
1980
               g_free (device_port_name);
 
1981
        }
 
1982
 
 
1983
        g_list_free (devices);
 
1984
}
 
1985
 
 
1986
static void
 
1987
create_ui_device_from_card (GvcMixerControl *control,
 
1988
                            GvcMixerCard    *card)
 
1989
{
 
1990
        GObject          *object;
 
1991
        GvcMixerUIDevice *in;
 
1992
        GvcMixerUIDevice *out;
 
1993
        const GList      *profiles;
 
1994
 
 
1995
        /* For now just create two devices and presume this device is multi directional
 
1996
         * Ensure to remove both on card removal (available to false by default) */
 
1997
        profiles = gvc_mixer_card_get_profiles (card);
 
1998
 
 
1999
        g_debug ("Portless card just registered - %i", gvc_mixer_card_get_index (card));
 
2000
 
 
2001
        object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
 
2002
                               "type", UIDeviceInput,
 
2003
                               "description", gvc_mixer_card_get_name (card),
 
2004
                               "origin", "", /* Leave it empty for these special cases */
 
2005
                               "port-name", NULL,
 
2006
                               "port-available", FALSE,
 
2007
                               "card", card,
 
2008
                               NULL);
 
2009
        in = GVC_MIXER_UI_DEVICE (object);
 
2010
        gvc_mixer_ui_device_set_profiles (in, profiles);
 
2011
 
 
2012
        g_hash_table_insert (control->priv->ui_inputs,
 
2013
                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (in)),
 
2014
                             g_object_ref (in));
 
2015
        object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
 
2016
                               "type", UIDeviceOutput,
 
2017
                               "description", gvc_mixer_card_get_name (card),
 
2018
                               "origin", "", /* Leave it empty for these special cases */
 
2019
                               "port-name", NULL,
 
2020
                               "port-available", FALSE,
 
2021
                               "card", card,
 
2022
                               NULL);
 
2023
        out = GVC_MIXER_UI_DEVICE (object);
 
2024
        gvc_mixer_ui_device_set_profiles (out, profiles);
 
2025
 
 
2026
        g_hash_table_insert (control->priv->ui_outputs,
 
2027
                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (out)),
 
2028
                             g_object_ref (out));
 
2029
}
 
2030
 
 
2031
/*
 
2032
 * At this point we can determine all devices available to us (besides network 'ports')
 
2033
 * This is done by the following:
 
2034
 *
 
2035
 * - gvc_mixer_card and gvc_mixer_card_ports are created and relevant setters are called.
 
2036
 * - First it checks to see if it's a portless card. Bluetooth devices are portless AFAIHS.
 
2037
 *        If so it creates two devices, an input and an output.
 
2038
 * - If it's a 'normal' card with ports it will create a new ui-device or
 
2039
 *   synchronise port availability with the existing device cached for that port on this card. */
 
2040
 
1114
2041
static void
1115
2042
update_card (GvcMixerControl      *control,
1116
2043
             const pa_card_info   *info)
1117
2044
{
 
2045
        const GList  *card_ports = NULL;
 
2046
        const GList  *m = NULL;
1118
2047
        GvcMixerCard *card;
1119
2048
        gboolean      is_new = FALSE;
1120
2049
#if 1
1145
2074
        card = g_hash_table_lookup (control->priv->cards,
1146
2075
                                    GUINT_TO_POINTER (info->index));
1147
2076
        if (card == NULL) {
1148
 
                GList *list = NULL;
 
2077
                GList *profile_list = NULL;
 
2078
                GList *port_list = NULL;
1149
2079
 
1150
2080
                for (i = 0; i < info->n_profiles; i++) {
 
2081
                        GvcMixerCardProfile *profile;
1151
2082
                        struct pa_card_profile_info pi = info->profiles[i];
1152
 
                        GvcMixerCardProfile *profile;
1153
2083
 
1154
2084
                        profile = g_new0 (GvcMixerCardProfile, 1);
1155
2085
                        profile->profile = g_strdup (pi.name);
1158
2088
                        profile->n_sinks = pi.n_sinks;
1159
2089
                        profile->n_sources = pi.n_sources;
1160
2090
                        profile->priority = pi.priority;
1161
 
                        list = g_list_prepend (list, profile);
 
2091
                        profile_list = g_list_prepend (profile_list, profile);
1162
2092
                }
1163
2093
                card = gvc_mixer_card_new (control->priv->pa_context,
1164
2094
                                           info->index);
1165
 
                gvc_mixer_card_set_profiles (card, list);
 
2095
                gvc_mixer_card_set_profiles (card, profile_list);
 
2096
 
 
2097
                for (i = 0; i < info->n_ports; i++) {
 
2098
                        GvcMixerCardPort *port;
 
2099
                        port = g_new0 (GvcMixerCardPort, 1);
 
2100
                        port->port = g_strdup (info->ports[i]->name);
 
2101
                        port->human_port = g_strdup (info->ports[i]->description);
 
2102
                        port->priority = info->ports[i]->priority;
 
2103
                        port->available = info->ports[i]->available;
 
2104
                        port->direction = info->ports[i]->direction;
 
2105
                        port->profiles = determine_profiles_for_port (info->ports[i], profile_list);
 
2106
                        port_list = g_list_prepend (port_list, port);
 
2107
                }
 
2108
                gvc_mixer_card_set_ports (card, port_list);
1166
2109
                is_new = TRUE;
1167
2110
        }
1168
2111
 
1175
2118
                                     GUINT_TO_POINTER (info->index),
1176
2119
                                     g_object_ref (card));
1177
2120
        }
 
2121
 
 
2122
        card_ports = gvc_mixer_card_get_ports (card);
 
2123
 
 
2124
        if (card_ports == NULL && is_new) {
 
2125
                g_debug ("Portless card just registered - %s", gvc_mixer_card_get_name (card));
 
2126
                create_ui_device_from_card (control, card);
 
2127
        }
 
2128
 
 
2129
        for (m = card_ports; m != NULL; m = m->next) {
 
2130
                GvcMixerCardPort *card_port;
 
2131
                card_port = m->data;
 
2132
                if (is_new)
 
2133
                        create_ui_device_from_port (control, card_port, card);
 
2134
                else {
 
2135
                        for (i = 0; i < info->n_ports; i++) {
 
2136
                                if (g_strcmp0 (card_port->port, info->ports[i]->name) == 0) {
 
2137
                                        if (card_port->available != info->ports[i]->available) {
 
2138
                                                card_port->available = info->ports[i]->available;
 
2139
                                                g_debug ("sync port availability on card %i, card port name '%s', new available value %i",
 
2140
                                                          gvc_mixer_card_get_index (card),
 
2141
                                                          card_port->port,
 
2142
                                                          card_port->available);
 
2143
                                                match_card_port_with_existing_device (control,
 
2144
                                                                                      card_port,
 
2145
                                                                                      card,
 
2146
                                                                                      card_port->available != PA_PORT_AVAILABLE_NO);
 
2147
                                        }
 
2148
                                }
 
2149
                        }
 
2150
                }
 
2151
        }
1178
2152
        g_signal_emit (G_OBJECT (control),
1179
2153
                       signals[CARD_ADDED],
1180
2154
                       0,
1341
2315
                g_warning ("Server info callback failure");
1342
2316
                return;
1343
2317
        }
1344
 
 
 
2318
        g_debug ("get server info");
1345
2319
        update_server (control, i);
1346
2320
        dec_outstanding (control);
1347
2321
}
1628
2602
remove_card (GvcMixerControl *control,
1629
2603
             guint            index)
1630
2604
{
 
2605
 
 
2606
        GList *devices, *d;
 
2607
 
 
2608
        devices = g_list_concat (g_hash_table_get_values (control->priv->ui_inputs),
 
2609
                                 g_hash_table_get_values (control->priv->ui_outputs));
 
2610
 
 
2611
        for (d = devices; d != NULL; d = d->next) {
 
2612
                GvcMixerCard *card;
 
2613
                GvcMixerUIDevice *device = d->data;
 
2614
 
 
2615
                g_object_get (G_OBJECT (device), "card", &card, NULL);
 
2616
 
 
2617
                if (gvc_mixer_card_get_index (card) == index) {
 
2618
                        g_signal_emit (G_OBJECT (control),
 
2619
                                       signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
 
2620
                                       0,
 
2621
                                       gvc_mixer_ui_device_get_id (device));
 
2622
                        g_debug ("Card removal remove device %s",
 
2623
                                 gvc_mixer_ui_device_get_description (device));
 
2624
                        g_hash_table_remove (gvc_mixer_ui_device_is_output (device) ? control->priv->ui_outputs : control->priv->ui_inputs,
 
2625
                                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)));
 
2626
                }
 
2627
        }
 
2628
 
 
2629
        g_list_free (devices);
 
2630
 
1631
2631
        g_hash_table_remove (control->priv->cards,
1632
2632
                             GUINT_TO_POINTER (index));
1633
2633
 
1641
2641
remove_sink (GvcMixerControl *control,
1642
2642
             guint            index)
1643
2643
{
1644
 
        GvcMixerStream *stream;
 
2644
        GvcMixerStream   *stream;
 
2645
        GvcMixerUIDevice *device;
1645
2646
 
1646
 
#if 0
1647
2647
        g_debug ("Removing sink: index=%u", index);
1648
 
#endif
1649
2648
 
1650
2649
        stream = g_hash_table_lookup (control->priv->sinks,
1651
2650
                                      GUINT_TO_POINTER (index));
1652
 
        if (stream == NULL) {
 
2651
        if (stream == NULL)
1653
2652
                return;
 
2653
 
 
2654
        device = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
2655
 
 
2656
        if (device != NULL) {
 
2657
                gvc_mixer_ui_device_invalidate_stream (device);
 
2658
                if (!gvc_mixer_ui_device_has_ports (device)) {
 
2659
                        g_signal_emit (G_OBJECT (control),
 
2660
                                       signals[OUTPUT_REMOVED],
 
2661
                                       0,
 
2662
                                       gvc_mixer_ui_device_get_id (device));
 
2663
                } else {
 
2664
                        GList *devices, *d;
 
2665
 
 
2666
                        devices = g_hash_table_get_values (control->priv->ui_outputs);
 
2667
 
 
2668
                        for (d = devices; d != NULL; d = d->next) {
 
2669
                                gint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
 
2670
                                device = d->data;
 
2671
                                g_object_get (G_OBJECT (device),
 
2672
                                             "stream-id", &stream_id,
 
2673
                                              NULL);
 
2674
                                if (stream_id == gvc_mixer_stream_get_id (stream))
 
2675
                                        gvc_mixer_ui_device_invalidate_stream (device);
 
2676
                        }
 
2677
 
 
2678
                        g_list_free (devices);
 
2679
                }
1654
2680
        }
 
2681
 
1655
2682
        g_hash_table_remove (control->priv->sinks,
1656
2683
                             GUINT_TO_POINTER (index));
1657
2684
 
1662
2689
remove_source (GvcMixerControl *control,
1663
2690
               guint            index)
1664
2691
{
1665
 
        GvcMixerStream *stream;
 
2692
        GvcMixerStream   *stream;
 
2693
        GvcMixerUIDevice *device;
1666
2694
 
1667
 
#if 0
1668
2695
        g_debug ("Removing source: index=%u", index);
1669
 
#endif
1670
2696
 
1671
2697
        stream = g_hash_table_lookup (control->priv->sources,
1672
2698
                                      GUINT_TO_POINTER (index));
1673
 
        if (stream == NULL) {
 
2699
        if (stream == NULL)
1674
2700
                return;
 
2701
 
 
2702
        device = gvc_mixer_control_lookup_device_from_stream (control, stream);
 
2703
 
 
2704
        if (device != NULL) {
 
2705
                gvc_mixer_ui_device_invalidate_stream (device);
 
2706
                if (!gvc_mixer_ui_device_has_ports (device)) {
 
2707
                        g_signal_emit (G_OBJECT (control),
 
2708
                                       signals[INPUT_REMOVED],
 
2709
                                       0,
 
2710
                                       gvc_mixer_ui_device_get_id (device));
 
2711
                } else {
 
2712
                        GList *devices, *d;
 
2713
 
 
2714
                        devices = g_hash_table_get_values (control->priv->ui_inputs);
 
2715
 
 
2716
                        for (d = devices; d != NULL; d = d->next) {
 
2717
                                gint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
 
2718
                                device = d->data;
 
2719
                                g_object_get (G_OBJECT (device),
 
2720
                                             "stream-id", &stream_id,
 
2721
                                              NULL);
 
2722
                                if (stream_id == gvc_mixer_stream_get_id (stream))
 
2723
                                        gvc_mixer_ui_device_invalidate_stream (device);
 
2724
                        }
 
2725
 
 
2726
                        g_list_free (devices);
 
2727
                }
1675
2728
        }
 
2729
 
1676
2730
        g_hash_table_remove (control->priv->sources,
1677
2731
                             GUINT_TO_POINTER (index));
1678
2732
 
1685
2739
{
1686
2740
        GvcMixerStream *stream;
1687
2741
 
1688
 
#if 0
1689
2742
        g_debug ("Removing sink input: index=%u", index);
1690
 
#endif
 
2743
 
1691
2744
        stream = g_hash_table_lookup (control->priv->sink_inputs,
1692
2745
                                      GUINT_TO_POINTER (index));
1693
2746
        if (stream == NULL) {
1705
2758
{
1706
2759
        GvcMixerStream *stream;
1707
2760
 
1708
 
#if 0
1709
2761
        g_debug ("Removing source output: index=%u", index);
1710
 
#endif
1711
2762
 
1712
2763
        stream = g_hash_table_lookup (control->priv->source_outputs,
1713
2764
                                      GUINT_TO_POINTER (index));
1810
2861
        pa_operation_unref (o);
1811
2862
 
1812
2863
        req_update_server_info (control, -1);
 
2864
        req_update_card (control, -1);
1813
2865
        req_update_client_info (control, -1);
1814
2866
        req_update_sink_info (control, -1);
1815
2867
        req_update_source_info (control, -1);
1816
2868
        req_update_sink_input_info (control, -1);
1817
2869
        req_update_source_output_info (control, -1);
1818
 
        req_update_card (control, -1);
 
2870
 
1819
2871
 
1820
2872
        control->priv->n_outstanding = 6;
1821
2873
 
2042
3094
                g_hash_table_destroy (control->priv->cards);
2043
3095
                control->priv->cards = NULL;
2044
3096
        }
 
3097
        if (control->priv->ui_outputs != NULL) {
 
3098
                g_hash_table_destroy (control->priv->ui_outputs);
 
3099
                control->priv->ui_outputs = NULL;
 
3100
        }
 
3101
        if (control->priv->ui_inputs != NULL) {
 
3102
                g_hash_table_destroy (control->priv->ui_inputs);
 
3103
                control->priv->ui_inputs = NULL;
 
3104
        }
2045
3105
 
2046
3106
        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
2047
3107
}
2098
3158
        self = GVC_MIXER_CONTROL (object);
2099
3159
 
2100
3160
        gvc_mixer_new_pa_context (self);
 
3161
        self->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
2101
3162
 
2102
3163
        return object;
2103
3164
}
2126
3187
                              G_TYPE_FROM_CLASS (klass),
2127
3188
                              G_SIGNAL_RUN_LAST,
2128
3189
                              G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
2129
 
                              NULL, NULL, NULL,
 
3190
                              NULL, NULL,
 
3191
                              g_cclosure_marshal_VOID__UINT,
2130
3192
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2131
3193
        signals [STREAM_ADDED] =
2132
3194
                g_signal_new ("stream-added",
2133
3195
                              G_TYPE_FROM_CLASS (klass),
2134
3196
                              G_SIGNAL_RUN_LAST,
2135
3197
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
2136
 
                              NULL, NULL, NULL,
 
3198
                              NULL, NULL,
 
3199
                              g_cclosure_marshal_VOID__UINT,
2137
3200
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2138
3201
        signals [STREAM_REMOVED] =
2139
3202
                g_signal_new ("stream-removed",
2140
3203
                              G_TYPE_FROM_CLASS (klass),
2141
3204
                              G_SIGNAL_RUN_LAST,
2142
3205
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
2143
 
                              NULL, NULL, NULL,
 
3206
                              NULL, NULL,
 
3207
                              g_cclosure_marshal_VOID__UINT,
2144
3208
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2145
3209
        signals [CARD_ADDED] =
2146
3210
                g_signal_new ("card-added",
2147
3211
                              G_TYPE_FROM_CLASS (klass),
2148
3212
                              G_SIGNAL_RUN_LAST,
2149
3213
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
2150
 
                              NULL, NULL, NULL,
 
3214
                              NULL, NULL,
 
3215
                              g_cclosure_marshal_VOID__UINT,
2151
3216
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2152
3217
        signals [CARD_REMOVED] =
2153
3218
                g_signal_new ("card-removed",
2154
3219
                              G_TYPE_FROM_CLASS (klass),
2155
3220
                              G_SIGNAL_RUN_LAST,
2156
3221
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
2157
 
                              NULL, NULL, NULL,
 
3222
                              NULL, NULL,
 
3223
                              g_cclosure_marshal_VOID__UINT,
2158
3224
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2159
3225
        signals [DEFAULT_SINK_CHANGED] =
2160
3226
                g_signal_new ("default-sink-changed",
2161
3227
                              G_TYPE_FROM_CLASS (klass),
2162
3228
                              G_SIGNAL_RUN_LAST,
2163
3229
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
2164
 
                              NULL, NULL, NULL,
 
3230
                              NULL, NULL,
 
3231
                              g_cclosure_marshal_VOID__UINT,
2165
3232
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2166
3233
        signals [DEFAULT_SOURCE_CHANGED] =
2167
3234
                g_signal_new ("default-source-changed",
2168
3235
                              G_TYPE_FROM_CLASS (klass),
2169
3236
                              G_SIGNAL_RUN_LAST,
2170
3237
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
2171
 
                              NULL, NULL, NULL,
2172
 
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2173
 
 
 
3238
                              NULL, NULL,
 
3239
                              g_cclosure_marshal_VOID__UINT,
 
3240
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3241
        signals [ACTIVE_OUTPUT_UPDATE] =
 
3242
                g_signal_new ("active-output-update",
 
3243
                              G_TYPE_FROM_CLASS (klass),
 
3244
                              G_SIGNAL_RUN_LAST,
 
3245
                              G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update),
 
3246
                              NULL, NULL,
 
3247
                              g_cclosure_marshal_VOID__UINT,
 
3248
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3249
        signals [ACTIVE_INPUT_UPDATE] =
 
3250
                g_signal_new ("active-input-update",
 
3251
                              G_TYPE_FROM_CLASS (klass),
 
3252
                              G_SIGNAL_RUN_LAST,
 
3253
                              G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update),
 
3254
                              NULL, NULL,
 
3255
                              g_cclosure_marshal_VOID__UINT,
 
3256
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3257
        signals [OUTPUT_ADDED] =
 
3258
                g_signal_new ("output-added",
 
3259
                              G_TYPE_FROM_CLASS (klass),
 
3260
                              G_SIGNAL_RUN_LAST,
 
3261
                              G_STRUCT_OFFSET (GvcMixerControlClass, output_added),
 
3262
                              NULL, NULL,
 
3263
                              g_cclosure_marshal_VOID__UINT,
 
3264
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3265
        signals [INPUT_ADDED] =
 
3266
                g_signal_new ("input-added",
 
3267
                              G_TYPE_FROM_CLASS (klass),
 
3268
                              G_SIGNAL_RUN_LAST,
 
3269
                              G_STRUCT_OFFSET (GvcMixerControlClass, input_added),
 
3270
                              NULL, NULL,
 
3271
                              g_cclosure_marshal_VOID__UINT,
 
3272
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3273
        signals [OUTPUT_REMOVED] =
 
3274
                g_signal_new ("output-removed",
 
3275
                              G_TYPE_FROM_CLASS (klass),
 
3276
                              G_SIGNAL_RUN_LAST,
 
3277
                              G_STRUCT_OFFSET (GvcMixerControlClass, output_removed),
 
3278
                              NULL, NULL,
 
3279
                              g_cclosure_marshal_VOID__UINT,
 
3280
                              G_TYPE_NONE, 1, G_TYPE_UINT);
 
3281
        signals [INPUT_REMOVED] =
 
3282
                g_signal_new ("input-removed",
 
3283
                              G_TYPE_FROM_CLASS (klass),
 
3284
                              G_SIGNAL_RUN_LAST,
 
3285
                              G_STRUCT_OFFSET (GvcMixerControlClass, input_removed),
 
3286
                              NULL, NULL,
 
3287
                              g_cclosure_marshal_VOID__UINT,
 
3288
                              G_TYPE_NONE, 1, G_TYPE_UINT);
2174
3289
        g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
2175
3290
}
2176
3291
 
 
3292
 
2177
3293
static void
2178
3294
gvc_mixer_control_init (GvcMixerControl *control)
2179
3295
{
2191
3307
        control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2192
3308
        control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2193
3309
        control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
 
3310
        control->priv->ui_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
 
3311
        control->priv->ui_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2194
3312
 
2195
3313
        control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
2196
3314
 
2223
3341
        return GVC_MIXER_CONTROL (control);
2224
3342
}
2225
3343
 
2226
 
/* FIXME: Remove when PA 0.9.23 is used */
2227
 
#ifndef PA_VOLUME_UI_MAX
2228
 
#define PA_VOLUME_UI_MAX pa_sw_volume_from_dB(+11.0)
2229
 
#endif
2230
 
 
2231
3344
gdouble
2232
3345
gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
2233
3346
{