gnome-shell-3.6.3.1/src/gvc/gvc-mixer-control.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found gvc/gvc-mixer-control.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   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         GvcMixerControlState state;
  86 };
  87 
  88 enum {
  89         STATE_CHANGED,
  90         STREAM_ADDED,
  91         STREAM_REMOVED,
  92         CARD_ADDED,
  93         CARD_REMOVED,
  94         DEFAULT_SINK_CHANGED,
  95         DEFAULT_SOURCE_CHANGED,
  96         LAST_SIGNAL
  97 };
  98 
  99 static guint signals [LAST_SIGNAL] = { 0, };
 100 
 101 static void     gvc_mixer_control_class_init (GvcMixerControlClass *klass);
 102 static void     gvc_mixer_control_init       (GvcMixerControl      *mixer_control);
 103 static void     gvc_mixer_control_finalize   (GObject              *object);
 104 
 105 G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
 106 
 107 pa_context *
 108 gvc_mixer_control_get_pa_context (GvcMixerControl *control)
 109 {
 110         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 111         return control->priv->pa_context;
 112 }
 113 
 114 /**
 115  * gvc_mixer_control_get_event_sink_input:
 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  * @control:
 234  *
 235  * Returns: (transfer none):
 236  */
 237 GvcMixerStream *
 238 gvc_mixer_control_get_default_sink (GvcMixerControl *control)
 239 {
 240         GvcMixerStream *stream;
 241 
 242         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 243 
 244         if (control->priv->default_sink_is_set) {
 245                 stream = g_hash_table_lookup (control->priv->all_streams,
 246                                               GUINT_TO_POINTER (control->priv->default_sink_id));
 247         } else {
 248                 stream = NULL;
 249         }
 250 
 251         return stream;
 252 }
 253 
 254 /**
 255  * gvc_mixer_control_get_default_source:
 256  * @control:
 257  *
 258  * Returns: (transfer none):
 259  */
 260 GvcMixerStream *
 261 gvc_mixer_control_get_default_source (GvcMixerControl *control)
 262 {
 263         GvcMixerStream *stream;
 264 
 265         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 266 
 267         if (control->priv->default_source_is_set) {
 268                 stream = g_hash_table_lookup (control->priv->all_streams,
 269                                               GUINT_TO_POINTER (control->priv->default_source_id));
 270         } else {
 271                 stream = NULL;
 272         }
 273 
 274         return stream;
 275 }
 276 
 277 static gpointer
 278 gvc_mixer_control_lookup_id (GHashTable *hash_table,
 279                              guint       id)
 280 {
 281         return g_hash_table_lookup (hash_table,
 282                                     GUINT_TO_POINTER (id));
 283 }
 284 
 285 /**
 286  * gvc_mixer_control_lookup_stream_id:
 287  * @control:
 288  * @id:
 289  *
 290  * Returns: (transfer none):
 291  */
 292 GvcMixerStream *
 293 gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
 294                                     guint            id)
 295 {
 296         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 297 
 298         return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
 299 }
 300 
 301 /**
 302  * gvc_mixer_control_lookup_card_id:
 303  * @control:
 304  * @id:
 305  *
 306  * Returns: (transfer none):
 307  */
 308 GvcMixerCard *
 309 gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
 310                                   guint            id)
 311 {
 312         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 313 
 314         return gvc_mixer_control_lookup_id (control->priv->cards, id);
 315 }
 316 
 317 static void
 318 listify_hash_values_hfunc (gpointer key,
 319                            gpointer value,
 320                            gpointer user_data)
 321 {
 322         GSList **list = user_data;
 323 
 324         *list = g_slist_prepend (*list, value);
 325 }
 326 
 327 static int
 328 gvc_name_collate (const char *namea,
 329                   const char *nameb)
 330 {
 331         if (nameb == NULL && namea == NULL)
 332                 return 0;
 333         if (nameb == NULL)
 334                 return 1;
 335         if (namea == NULL)
 336                 return -1;
 337 
 338         return g_utf8_collate (namea, nameb);
 339 }
 340 
 341 static int
 342 gvc_card_collate (GvcMixerCard *a,
 343                   GvcMixerCard *b)
 344 {
 345         const char *namea;
 346         const char *nameb;
 347 
 348         g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0);
 349         g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0);
 350 
 351         namea = gvc_mixer_card_get_name (a);
 352         nameb = gvc_mixer_card_get_name (b);
 353 
 354         return gvc_name_collate (namea, nameb);
 355 }
 356 
 357 /**
 358  * gvc_mixer_control_get_cards:
 359  * @control:
 360  *
 361  * Returns: (transfer container) (element-type Gvc.MixerCard):
 362  */
 363 GSList *
 364 gvc_mixer_control_get_cards (GvcMixerControl *control)
 365 {
 366         GSList *retval;
 367 
 368         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 369 
 370         retval = NULL;
 371         g_hash_table_foreach (control->priv->cards,
 372                               listify_hash_values_hfunc,
 373                               &retval);
 374         return g_slist_sort (retval, (GCompareFunc) gvc_card_collate);
 375 }
 376 
 377 static int
 378 gvc_stream_collate (GvcMixerStream *a,
 379                     GvcMixerStream *b)
 380 {
 381         const char *namea;
 382         const char *nameb;
 383 
 384         g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0);
 385         g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0);
 386 
 387         namea = gvc_mixer_stream_get_name (a);
 388         nameb = gvc_mixer_stream_get_name (b);
 389 
 390         return gvc_name_collate (namea, nameb);
 391 }
 392 
 393 /**
 394  * gvc_mixer_control_get_streams:
 395  * @control:
 396  *
 397  * Returns: (transfer container) (element-type Gvc.MixerStream):
 398  */
 399 GSList *
 400 gvc_mixer_control_get_streams (GvcMixerControl *control)
 401 {
 402         GSList *retval;
 403 
 404         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 405 
 406         retval = NULL;
 407         g_hash_table_foreach (control->priv->all_streams,
 408                               listify_hash_values_hfunc,
 409                               &retval);
 410         return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 411 }
 412 
 413 /**
 414  * gvc_mixer_control_get_sinks:
 415  * @control:
 416  *
 417  * Returns: (transfer container) (element-type Gvc.MixerSink):
 418  */
 419 GSList *
 420 gvc_mixer_control_get_sinks (GvcMixerControl *control)
 421 {
 422         GSList *retval;
 423 
 424         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 425 
 426         retval = NULL;
 427         g_hash_table_foreach (control->priv->sinks,
 428                               listify_hash_values_hfunc,
 429                               &retval);
 430         return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 431 }
 432 
 433 /**
 434  * gvc_mixer_control_get_sources:
 435  * @control:
 436  *
 437  * Returns: (transfer container) (element-type Gvc.MixerSource):
 438  */
 439 GSList *
 440 gvc_mixer_control_get_sources (GvcMixerControl *control)
 441 {
 442         GSList *retval;
 443 
 444         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 445 
 446         retval = NULL;
 447         g_hash_table_foreach (control->priv->sources,
 448                               listify_hash_values_hfunc,
 449                               &retval);
 450         return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 451 }
 452 
 453 /**
 454  * gvc_mixer_control_get_sink_inputs:
 455  * @control:
 456  *
 457  * Returns: (transfer container) (element-type Gvc.MixerSinkInput):
 458  */
 459 GSList *
 460 gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
 461 {
 462         GSList *retval;
 463 
 464         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 465 
 466         retval = NULL;
 467         g_hash_table_foreach (control->priv->sink_inputs,
 468                               listify_hash_values_hfunc,
 469                               &retval);
 470         return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 471 }
 472 
 473 /**
 474  * gvc_mixer_control_get_source_outputs:
 475  * @control:
 476  *
 477  * Returns: (transfer container) (element-type Gvc.MixerSourceOutput):
 478  */
 479 GSList *
 480 gvc_mixer_control_get_source_outputs (GvcMixerControl *control)
 481 {
 482         GSList *retval;
 483 
 484         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
 485 
 486         retval = NULL;
 487         g_hash_table_foreach (control->priv->source_outputs,
 488                               listify_hash_values_hfunc,
 489                               &retval);
 490         return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
 491 }
 492 
 493 static void
 494 dec_outstanding (GvcMixerControl *control)
 495 {
 496         if (control->priv->n_outstanding <= 0) {
 497                 return;
 498         }
 499 
 500         if (--control->priv->n_outstanding <= 0) {
 501                 control->priv->state = GVC_STATE_READY;
 502                 g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY);
 503         }
 504 }
 505 
 506 GvcMixerControlState
 507 gvc_mixer_control_get_state (GvcMixerControl *control)
 508 {
 509         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
 510 
 511         return control->priv->state;
 512 }
 513 
 514 
 515 static void
 516 _set_default_source (GvcMixerControl *control,
 517                      GvcMixerStream  *stream)
 518 {
 519         guint new_id;
 520 
 521         if (stream == NULL) {
 522                 control->priv->default_source_id = 0;
 523                 control->priv->default_source_is_set = FALSE;
 524                 g_signal_emit (control,
 525                                signals[DEFAULT_SOURCE_CHANGED],
 526                                0,
 527                                PA_INVALID_INDEX);
 528                 return;
 529         }
 530 
 531         new_id = gvc_mixer_stream_get_id (stream);
 532 
 533         if (control->priv->default_source_id != new_id) {
 534                 control->priv->default_source_id = new_id;
 535                 control->priv->default_source_is_set = TRUE;
 536                 g_signal_emit (control,
 537                                signals[DEFAULT_SOURCE_CHANGED],
 538                                0,
 539                                new_id);
 540         }
 541 }
 542 
 543 static void
 544 _set_default_sink (GvcMixerControl *control,
 545                    GvcMixerStream  *stream)
 546 {
 547         guint new_id;
 548 
 549         if (stream == NULL) {
 550                 /* Don't tell front-ends about an unset default
 551                  * sink if it's already unset */
 552                 if (control->priv->default_sink_is_set == FALSE)
 553                         return;
 554                 control->priv->default_sink_id = 0;
 555                 control->priv->default_sink_is_set = FALSE;
 556                 g_signal_emit (control,
 557                                signals[DEFAULT_SINK_CHANGED],
 558                                0,
 559                                PA_INVALID_INDEX);
 560                 return;
 561         }
 562 
 563         new_id = gvc_mixer_stream_get_id (stream);
 564 
 565         if (control->priv->default_sink_id != new_id) {
 566                 control->priv->default_sink_id = new_id;
 567                 control->priv->default_sink_is_set = TRUE;
 568                 g_signal_emit (control,
 569                                signals[DEFAULT_SINK_CHANGED],
 570                                0,
 571                                new_id);
 572         }
 573 }
 574 
 575 static gboolean
 576 _stream_has_name (gpointer        key,
 577                   GvcMixerStream *stream,
 578                   const char     *name)
 579 {
 580         const char *t_name;
 581 
 582         t_name = gvc_mixer_stream_get_name (stream);
 583 
 584         if (t_name != NULL
 585             && name != NULL
 586             && strcmp (t_name, name) == 0) {
 587                 return TRUE;
 588         }
 589 
 590         return FALSE;
 591 }
 592 
 593 static GvcMixerStream  *
 594 find_stream_for_name (GvcMixerControl *control,
 595                       const char      *name)
 596 {
 597         GvcMixerStream *stream;
 598 
 599         stream = g_hash_table_find (control->priv->all_streams,
 600                                     (GHRFunc)_stream_has_name,
 601                                     (char *)name);
 602         return stream;
 603 }
 604 
 605 static void
 606 update_default_source_from_name (GvcMixerControl *control,
 607                                  const char      *name)
 608 {
 609         gboolean changed = FALSE;
 610 
 611         if ((control->priv->default_source_name == NULL
 612              && name != NULL)
 613             || (control->priv->default_source_name != NULL
 614                 && name == NULL)
 615             || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) {
 616                 changed = TRUE;
 617         }
 618 
 619         if (changed) {
 620                 GvcMixerStream *stream;
 621 
 622                 g_free (control->priv->default_source_name);
 623                 control->priv->default_source_name = g_strdup (name);
 624 
 625                 stream = find_stream_for_name (control, name);
 626                 _set_default_source (control, stream);
 627         }
 628 }
 629 
 630 static void
 631 update_default_sink_from_name (GvcMixerControl *control,
 632                                const char      *name)
 633 {
 634         gboolean changed = FALSE;
 635 
 636         if ((control->priv->default_sink_name == NULL
 637              && name != NULL)
 638             || (control->priv->default_sink_name != NULL
 639                 && name == NULL)
 640             || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) {
 641                 changed = TRUE;
 642         }
 643 
 644         if (changed) {
 645                 GvcMixerStream *stream;
 646                 g_free (control->priv->default_sink_name);
 647                 control->priv->default_sink_name = g_strdup (name);
 648 
 649                 stream = find_stream_for_name (control, name);
 650                 _set_default_sink (control, stream);
 651         }
 652 }
 653 
 654 static void
 655 update_server (GvcMixerControl      *control,
 656                const pa_server_info *info)
 657 {
 658         if (info->default_source_name != NULL) {
 659                 update_default_source_from_name (control, info->default_source_name);
 660         }
 661         if (info->default_sink_name != NULL) {
 662                 update_default_sink_from_name (control, info->default_sink_name);
 663         }
 664 }
 665 
 666 static void
 667 remove_stream (GvcMixerControl *control,
 668                GvcMixerStream  *stream)
 669 {
 670         guint id;
 671 
 672         g_object_ref (stream);
 673 
 674         id = gvc_mixer_stream_get_id (stream);
 675 
 676         if (id == control->priv->default_sink_id) {
 677                 _set_default_sink (control, NULL);
 678         } else if (id == control->priv->default_source_id) {
 679                 _set_default_source (control, NULL);
 680         }
 681 
 682         g_hash_table_remove (control->priv->all_streams,
 683                              GUINT_TO_POINTER (id));
 684         g_signal_emit (G_OBJECT (control),
 685                        signals[STREAM_REMOVED],
 686                        0,
 687                        gvc_mixer_stream_get_id (stream));
 688         g_object_unref (stream);
 689 }
 690 
 691 static void
 692 add_stream (GvcMixerControl *control,
 693             GvcMixerStream  *stream)
 694 {
 695         g_hash_table_insert (control->priv->all_streams,
 696                              GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
 697                              stream);
 698         g_signal_emit (G_OBJECT (control),
 699                        signals[STREAM_ADDED],
 700                        0,
 701                        gvc_mixer_stream_get_id (stream));
 702 }
 703 
 704 static void
 705 set_icon_name_from_proplist (GvcMixerStream *stream,
 706                              pa_proplist    *l,
 707                              const char     *default_icon_name)
 708 {
 709         const char *t;
 710 
 711         if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) {
 712                 goto finish;
 713         }
 714 
 715         if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
 716                 goto finish;
 717         }
 718 
 719         if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
 720                 goto finish;
 721         }
 722 
 723         if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
 724                 goto finish;
 725         }
 726 
 727         if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
 728 
 729                 if (strcmp (t, "video") == 0 ||
 730                     strcmp (t, "phone") == 0) {
 731                         goto finish;
 732                 }
 733 
 734                 if (strcmp (t, "music") == 0) {
 735                         t = "audio";
 736                         goto finish;
 737                 }
 738 
 739                 if (strcmp (t, "game") == 0) {
 740                         t = "applications-games";
 741                         goto finish;
 742                 }
 743 
 744                 if (strcmp (t, "event") == 0) {
 745                         t = "dialog-information";
 746                         goto finish;
 747                 }
 748         }
 749 
 750         t = default_icon_name;
 751 
 752  finish:
 753         gvc_mixer_stream_set_icon_name (stream, t);
 754 }
 755 
 756 static void
 757 update_sink (GvcMixerControl    *control,
 758              const pa_sink_info *info)
 759 {
 760         GvcMixerStream *stream;
 761         gboolean        is_new;
 762         pa_volume_t     max_volume;
 763         GvcChannelMap  *map;
 764         char            map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];
 765 
 766         pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
 767 #if 1
 768         g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'",
 769                  info->index,
 770                  info->name,
 771                  info->description,
 772                  map_buff);
 773 #endif
 774 
 775         map = NULL;
 776         is_new = FALSE;
 777         stream = g_hash_table_lookup (control->priv->sinks,
 778                                       GUINT_TO_POINTER (info->index));
 779         if (stream == NULL) {
 780 #if PA_MICRO > 15
 781                 GList *list = NULL;
 782                 guint i;
 783 #endif /* PA_MICRO > 15 */
 784 
 785                 map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 786                 stream = gvc_mixer_sink_new (control->priv->pa_context,
 787                                              info->index,
 788                                              map);
 789 #if PA_MICRO > 15
 790                 for (i = 0; i < info->n_ports; i++) {
 791                         GvcMixerStreamPort *port;
 792 
 793                         port = g_new0 (GvcMixerStreamPort, 1);
 794                         port->port = g_strdup (info->ports[i]->name);
 795                         port->human_port = g_strdup (info->ports[i]->description);
 796                         port->priority = info->ports[i]->priority;
 797                         list = g_list_prepend (list, port);
 798                 }
 799                 gvc_mixer_stream_set_ports (stream, list);
 800 #endif /* PA_MICRO > 15 */
 801                 g_object_unref (map);
 802                 is_new = TRUE;
 803         } else if (gvc_mixer_stream_is_running (stream)) {
 804                 /* Ignore events if volume changes are outstanding */
 805                 g_debug ("Ignoring event, volume changes are outstanding");
 806                 return;
 807         }
 808 
 809         max_volume = pa_cvolume_max (&info->volume);
 810         gvc_mixer_stream_set_name (stream, info->name);
 811         gvc_mixer_stream_set_card_index (stream, info->card);
 812         gvc_mixer_stream_set_description (stream, info->description);
 813         set_icon_name_from_proplist (stream, info->proplist, "audio-card");
 814         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
 815         gvc_mixer_stream_set_is_muted (stream, info->mute);
 816         gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
 817         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 */
 822 
 823         if (is_new) {
 824                 g_hash_table_insert (control->priv->sinks,
 825                                      GUINT_TO_POINTER (info->index),
 826                                      g_object_ref (stream));
 827                 add_stream (control, stream);
 828         }
 829 
 830         if (control->priv->default_sink_name != NULL
 831             && info->name != NULL
 832             && strcmp (control->priv->default_sink_name, info->name) == 0) {
 833                 _set_default_sink (control, stream);
 834         }
 835 
 836         if (map == NULL)
 837                 map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);
 838         gvc_channel_map_volume_changed (map, &info->volume, FALSE);
 839 }
 840 
 841 static void
 842 update_source (GvcMixerControl      *control,
 843                const pa_source_info *info)
 844 {
 845         GvcMixerStream *stream;
 846         gboolean        is_new;
 847         pa_volume_t     max_volume;
 848 
 849 #if 1
 850         g_debug ("Updating source: index=%u name='%s' description='%s'",
 851                  info->index,
 852                  info->name,
 853                  info->description);
 854 #endif
 855 
 856         /* completely ignore monitors, they're not real sources */
 857         if (info->monitor_of_sink != PA_INVALID_INDEX) {
 858                 return;
 859         }
 860 
 861         is_new = FALSE;
 862 
 863         stream = g_hash_table_lookup (control->priv->sources,
 864                                       GUINT_TO_POINTER (info->index));
 865         if (stream == NULL) {
 866 #if PA_MICRO > 15
 867                 GList *list = NULL;
 868                 guint i;
 869 #endif /* PA_MICRO > 15 */
 870                 GvcChannelMap *map;
 871 
 872                 map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 873                 stream = gvc_mixer_source_new (control->priv->pa_context,
 874                                                info->index,
 875                                                map);
 876 #if PA_MICRO > 15
 877                 for (i = 0; i < info->n_ports; i++) {
 878                         GvcMixerStreamPort *port;
 879 
 880                         port = g_new0 (GvcMixerStreamPort, 1);
 881                         port->port = g_strdup (info->ports[i]->name);
 882                         port->human_port = g_strdup (info->ports[i]->description);
 883                         port->priority = info->ports[i]->priority;
 884                         list = g_list_prepend (list, port);
 885                 }
 886                 gvc_mixer_stream_set_ports (stream, list);
 887 #endif /* PA_MICRO > 15 */
 888 
 889                 g_object_unref (map);
 890                 is_new = TRUE;
 891         } else if (gvc_mixer_stream_is_running (stream)) {
 892                 /* Ignore events if volume changes are outstanding */
 893                 g_debug ("Ignoring event, volume changes are outstanding");
 894                 return;
 895         }
 896 
 897         max_volume = pa_cvolume_max (&info->volume);
 898 
 899         gvc_mixer_stream_set_name (stream, info->name);
 900         gvc_mixer_stream_set_card_index (stream, info->card);
 901         gvc_mixer_stream_set_description (stream, info->description);
 902         set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
 903         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
 904         gvc_mixer_stream_set_is_muted (stream, info->mute);
 905         gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
 906         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 */
 911 
 912         if (is_new) {
 913                 g_hash_table_insert (control->priv->sources,
 914                                      GUINT_TO_POINTER (info->index),
 915                                      g_object_ref (stream));
 916                 add_stream (control, stream);
 917         }
 918 
 919         if (control->priv->default_source_name != NULL
 920             && info->name != NULL
 921             && strcmp (control->priv->default_source_name, info->name) == 0) {
 922                 _set_default_source (control, stream);
 923         }
 924 }
 925 
 926 static void
 927 set_is_event_stream_from_proplist (GvcMixerStream *stream,
 928                                    pa_proplist    *l)
 929 {
 930         const char *t;
 931         gboolean is_event_stream;
 932 
 933         is_event_stream = FALSE;
 934 
 935         if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
 936                 if (g_str_equal (t, "event"))
 937                         is_event_stream = TRUE;
 938         }
 939 
 940         gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
 941 }
 942 
 943 static void
 944 set_application_id_from_proplist (GvcMixerStream *stream,
 945                                   pa_proplist    *l)
 946 {
 947         const char *t;
 948 
 949         if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
 950                 gvc_mixer_stream_set_application_id (stream, t);
 951         }
 952 }
 953 
 954 static void
 955 update_sink_input (GvcMixerControl          *control,
 956                    const pa_sink_input_info *info)
 957 {
 958         GvcMixerStream *stream;
 959         gboolean        is_new;
 960         pa_volume_t     max_volume;
 961         const char     *name;
 962 
 963 #if 0
 964         g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u",
 965                  info->index,
 966                  info->name,
 967                  info->client,
 968                  info->sink);
 969 #endif
 970 
 971         is_new = FALSE;
 972 
 973         stream = g_hash_table_lookup (control->priv->sink_inputs,
 974                                       GUINT_TO_POINTER (info->index));
 975         if (stream == NULL) {
 976                 GvcChannelMap *map;
 977                 map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
 978                 stream = gvc_mixer_sink_input_new (control->priv->pa_context,
 979                                                    info->index,
 980                                                    map);
 981                 g_object_unref (map);
 982                 is_new = TRUE;
 983         } else if (gvc_mixer_stream_is_running (stream)) {
 984                 /* Ignore events if volume changes are outstanding */
 985                 g_debug ("Ignoring event, volume changes are outstanding");
 986                 return;
 987         }
 988 
 989         max_volume = pa_cvolume_max (&info->volume);
 990 
 991         name = (const char *)g_hash_table_lookup (control->priv->clients,
 992                                                   GUINT_TO_POINTER (info->client));
 993         gvc_mixer_stream_set_name (stream, name);
 994         gvc_mixer_stream_set_description (stream, info->name);
 995 
 996         set_application_id_from_proplist (stream, info->proplist);
 997         set_is_event_stream_from_proplist (stream, info->proplist);
 998         set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
 999         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
1000         gvc_mixer_stream_set_is_muted (stream, info->mute);
1001         gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);
1002 
1003         if (is_new) {
1004                 g_hash_table_insert (control->priv->sink_inputs,
1005                                      GUINT_TO_POINTER (info->index),
1006                                      g_object_ref (stream));
1007                 add_stream (control, stream);
1008         }
1009 }
1010 
1011 static void
1012 update_source_output (GvcMixerControl             *control,
1013                       const pa_source_output_info *info)
1014 {
1015         GvcMixerStream *stream;
1016         gboolean        is_new;
1017         const char     *name;
1018 
1019 #if 1
1020         g_debug ("Updating source output: index=%u name='%s' client=%u source=%u",
1021                  info->index,
1022                  info->name,
1023                  info->client,
1024                  info->source);
1025 #endif
1026 
1027         is_new = FALSE;
1028         stream = g_hash_table_lookup (control->priv->source_outputs,
1029                                       GUINT_TO_POINTER (info->index));
1030         if (stream == NULL) {
1031                 GvcChannelMap *map;
1032                 map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
1033                 stream = gvc_mixer_source_output_new (control->priv->pa_context,
1034                                                       info->index,
1035                                                       map);
1036                 g_object_unref (map);
1037                 is_new = TRUE;
1038         }
1039 
1040         name = (const char *)g_hash_table_lookup (control->priv->clients,
1041                                                   GUINT_TO_POINTER (info->client));
1042 
1043         gvc_mixer_stream_set_name (stream, name);
1044         gvc_mixer_stream_set_description (stream, info->name);
1045         set_application_id_from_proplist (stream, info->proplist);
1046         set_is_event_stream_from_proplist (stream, info->proplist);
1047         set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
1048 
1049         if (is_new) {
1050                 g_hash_table_insert (control->priv->source_outputs,
1051                                      GUINT_TO_POINTER (info->index),
1052                                      g_object_ref (stream));
1053                 add_stream (control, stream);
1054         }
1055 }
1056 
1057 static void
1058 update_client (GvcMixerControl      *control,
1059                const pa_client_info *info)
1060 {
1061 #if 1
1062         g_debug ("Updating client: index=%u name='%s'",
1063                  info->index,
1064                  info->name);
1065 #endif
1066         g_hash_table_insert (control->priv->clients,
1067                              GUINT_TO_POINTER (info->index),
1068                              g_strdup (info->name));
1069 }
1070 
1071 static char *
1072 card_num_streams_to_status (guint sinks,
1073                             guint sources)
1074 {
1075         char *sinks_str;
1076         char *sources_str;
1077         char *ret;
1078 
1079         if (sinks == 0 && sources == 0) {
1080                 /* translators:
1081                  * The device has been disabled */
1082                 return g_strdup (_("Disabled"));
1083         }
1084         if (sinks == 0) {
1085                 sinks_str = NULL;
1086         } else {
1087                 /* translators:
1088                  * The number of sound outputs on a particular device */
1089                 sinks_str = g_strdup_printf (ngettext ("%u Output",
1090                                                        "%u Outputs",
1091                                                        sinks),
1092                                              sinks);
1093         }
1094         if (sources == 0) {
1095                 sources_str = NULL;
1096         } else {
1097                 /* translators:
1098                  * The number of sound inputs on a particular device */
1099                 sources_str = g_strdup_printf (ngettext ("%u Input",
1100                                                          "%u Inputs",
1101                                                          sources),
1102                                                sources);
1103         }
1104         if (sources_str == NULL)
1105                 return sinks_str;
1106         if (sinks_str == NULL)
1107                 return sources_str;
1108         ret = g_strdup_printf ("%s / %s", sinks_str, sources_str);
1109         g_free (sinks_str);
1110         g_free (sources_str);
1111         return ret;
1112 }
1113 
1114 static void
1115 update_card (GvcMixerControl      *control,
1116              const pa_card_info   *info)
1117 {
1118         GvcMixerCard *card;
1119         gboolean      is_new = FALSE;
1120 #if 1
1121         guint i;
1122         const char *key;
1123         void *state;
1124 
1125         g_debug ("Udpating card %s (index: %u driver: %s):",
1126                  info->name, info->index, info->driver);
1127 
1128         for (i = 0; i < info->n_profiles; i++) {
1129                 struct pa_card_profile_info pi = info->profiles[i];
1130                 gboolean is_default;
1131 
1132                 is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0);
1133                 g_debug ("\tProfile '%s': %d sources %d sinks%s",
1134                          pi.name, pi.n_sources, pi.n_sinks,
1135                          is_default ? " (Current)" : "");
1136         }
1137         state = NULL;
1138         key = pa_proplist_iterate (info->proplist, &state);
1139         while (key != NULL) {
1140                 g_debug ("\tProperty: '%s' = '%s'",
1141                         key, pa_proplist_gets (info->proplist, key));
1142                 key = pa_proplist_iterate (info->proplist, &state);
1143         }
1144 #endif
1145         card = g_hash_table_lookup (control->priv->cards,
1146                                     GUINT_TO_POINTER (info->index));
1147         if (card == NULL) {
1148                 GList *list = NULL;
1149 
1150                 for (i = 0; i < info->n_profiles; i++) {
1151                         struct pa_card_profile_info pi = info->profiles[i];
1152                         GvcMixerCardProfile *profile;
1153 
1154                         profile = g_new0 (GvcMixerCardProfile, 1);
1155                         profile->profile = g_strdup (pi.name);
1156                         profile->human_profile = g_strdup (pi.description);
1157                         profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources);
1158                         profile->n_sinks = pi.n_sinks;
1159                         profile->n_sources = pi.n_sources;
1160                         profile->priority = pi.priority;
1161                         list = g_list_prepend (list, profile);
1162                 }
1163                 card = gvc_mixer_card_new (control->priv->pa_context,
1164                                            info->index);
1165                 gvc_mixer_card_set_profiles (card, list);
1166                 is_new = TRUE;
1167         }
1168 
1169         gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description"));
1170         gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name"));
1171         gvc_mixer_card_set_profile (card, info->active_profile->name);
1172 
1173         if (is_new) {
1174                 g_hash_table_insert (control->priv->cards,
1175                                      GUINT_TO_POINTER (info->index),
1176                                      g_object_ref (card));
1177         }
1178         g_signal_emit (G_OBJECT (control),
1179                        signals[CARD_ADDED],
1180                        0,
1181                        info->index);
1182 }
1183 
1184 static void
1185 _pa_context_get_sink_info_cb (pa_context         *context,
1186                               const pa_sink_info *i,
1187                               int                 eol,
1188                               void               *userdata)
1189 {
1190         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1191 
1192         if (eol < 0) {
1193                 if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1194                         return;
1195                 }
1196 
1197                 g_warning ("Sink callback failure");
1198                 return;
1199         }
1200 
1201         if (eol > 0) {
1202                 dec_outstanding (control);
1203                 return;
1204         }
1205 
1206         update_sink (control, i);
1207 }
1208 
1209 static void
1210 _pa_context_get_source_info_cb (pa_context           *context,
1211                                 const pa_source_info *i,
1212                                 int                   eol,
1213                                 void                 *userdata)
1214 {
1215         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1216 
1217         if (eol < 0) {
1218                 if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1219                         return;
1220                 }
1221 
1222                 g_warning ("Source callback failure");
1223                 return;
1224         }
1225 
1226         if (eol > 0) {
1227                 dec_outstanding (control);
1228                 return;
1229         }
1230 
1231         update_source (control, i);
1232 }
1233 
1234 static void
1235 _pa_context_get_sink_input_info_cb (pa_context               *context,
1236                                     const pa_sink_input_info *i,
1237                                     int                       eol,
1238                                     void                     *userdata)
1239 {
1240         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1241 
1242         if (eol < 0) {
1243                 if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1244                         return;
1245                 }
1246 
1247                 g_warning ("Sink input callback failure");
1248                 return;
1249         }
1250 
1251         if (eol > 0) {
1252                 dec_outstanding (control);
1253                 return;
1254         }
1255 
1256         update_sink_input (control, i);
1257 }
1258 
1259 static void
1260 _pa_context_get_source_output_info_cb (pa_context                  *context,
1261                                        const pa_source_output_info *i,
1262                                        int                          eol,
1263                                        void                        *userdata)
1264 {
1265         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1266 
1267         if (eol < 0) {
1268                 if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1269                         return;
1270                 }
1271 
1272                 g_warning ("Source output callback failure");
1273                 return;
1274         }
1275 
1276         if (eol > 0)  {
1277                 dec_outstanding (control);
1278                 return;
1279         }
1280 
1281         update_source_output (control, i);
1282 }
1283 
1284 static void
1285 _pa_context_get_client_info_cb (pa_context           *context,
1286                                 const pa_client_info *i,
1287                                 int                   eol,
1288                                 void                 *userdata)
1289 {
1290         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1291 
1292         if (eol < 0) {
1293                 if (pa_context_errno (context) == PA_ERR_NOENTITY) {
1294                         return;
1295                 }
1296 
1297                 g_warning ("Client callback failure");
1298                 return;
1299         }
1300 
1301         if (eol > 0) {
1302                 dec_outstanding (control);
1303                 return;
1304         }
1305 
1306         update_client (control, i);
1307 }
1308 
1309 static void
1310 _pa_context_get_card_info_by_index_cb (pa_context *context,
1311                                        const pa_card_info *i,
1312                                        int eol,
1313                                        void *userdata)
1314 {
1315         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1316 
1317         if (eol < 0) {
1318                 if (pa_context_errno (context) == PA_ERR_NOENTITY)
1319                         return;
1320 
1321                 g_warning ("Card callback failure");
1322                 return;
1323         }
1324 
1325         if (eol > 0) {
1326                 dec_outstanding (control);
1327                 return;
1328         }
1329 
1330         update_card (control, i);
1331 }
1332 
1333 static void
1334 _pa_context_get_server_info_cb (pa_context           *context,
1335                                 const pa_server_info *i,
1336                                 void                 *userdata)
1337 {
1338         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1339 
1340         if (i == NULL) {
1341                 g_warning ("Server info callback failure");
1342                 return;
1343         }
1344 
1345         update_server (control, i);
1346         dec_outstanding (control);
1347 }
1348 
1349 static void
1350 remove_event_role_stream (GvcMixerControl *control)
1351 {
1352         g_debug ("Removing event role");
1353 }
1354 
1355 static void
1356 update_event_role_stream (GvcMixerControl                  *control,
1357                           const pa_ext_stream_restore_info *info)
1358 {
1359         GvcMixerStream *stream;
1360         gboolean        is_new;
1361         pa_volume_t     max_volume;
1362 
1363         if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
1364                 return;
1365         }
1366 
1367 #if 0
1368         g_debug ("Updating event role: name='%s' device='%s'",
1369                  info->name,
1370                  info->device);
1371 #endif
1372 
1373         is_new = FALSE;
1374 
1375         if (!control->priv->event_sink_input_is_set) {
1376                 pa_channel_map pa_map;
1377                 GvcChannelMap *map;
1378 
1379                 pa_map.channels = 1;
1380                 pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
1381                 map = gvc_channel_map_new_from_pa_channel_map (&pa_map);
1382 
1383                 stream = gvc_mixer_event_role_new (control->priv->pa_context,
1384                                                    info->device,
1385                                                    map);
1386                 control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
1387                 control->priv->event_sink_input_is_set = TRUE;
1388 
1389                 is_new = TRUE;
1390         } else {
1391                 stream = g_hash_table_lookup (control->priv->all_streams,
1392                                               GUINT_TO_POINTER (control->priv->event_sink_input_id));
1393         }
1394 
1395         max_volume = pa_cvolume_max (&info->volume);
1396 
1397         gvc_mixer_stream_set_name (stream, _("System Sounds"));
1398         gvc_mixer_stream_set_icon_name (stream, "multimedia-volume-control");
1399         gvc_mixer_stream_set_volume (stream, (guint)max_volume);
1400         gvc_mixer_stream_set_is_muted (stream, info->mute);
1401 
1402         if (is_new) {
1403                 add_stream (control, stream);
1404         }
1405 }
1406 
1407 static void
1408 _pa_ext_stream_restore_read_cb (pa_context                       *context,
1409                                 const pa_ext_stream_restore_info *i,
1410                                 int                               eol,
1411                                 void                             *userdata)
1412 {
1413         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1414 
1415         if (eol < 0) {
1416                 g_debug ("Failed to initialized stream_restore extension: %s",
1417                          pa_strerror (pa_context_errno (context)));
1418                 remove_event_role_stream (control);
1419                 return;
1420         }
1421 
1422         if (eol > 0) {
1423                 dec_outstanding (control);
1424                 /* If we don't have an event stream to restore, then
1425                  * set one up with a default 100% volume */
1426                 if (!control->priv->event_sink_input_is_set) {
1427                         pa_ext_stream_restore_info info;
1428 
1429                         memset (&info, 0, sizeof(info));
1430                         info.name = "sink-input-by-media-role:event";
1431                         info.volume.channels = 1;
1432                         info.volume.values[0] = PA_VOLUME_NORM;
1433                         update_event_role_stream (control, &info);
1434                 }
1435                 return;
1436         }
1437 
1438         update_event_role_stream (control, i);
1439 }
1440 
1441 static void
1442 _pa_ext_stream_restore_subscribe_cb (pa_context *context,
1443                                      void       *userdata)
1444 {
1445         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1446         pa_operation    *o;
1447 
1448         o = pa_ext_stream_restore_read (context,
1449                                         _pa_ext_stream_restore_read_cb,
1450                                         control);
1451         if (o == NULL) {
1452                 g_warning ("pa_ext_stream_restore_read() failed");
1453                 return;
1454         }
1455 
1456         pa_operation_unref (o);
1457 }
1458 
1459 static void
1460 req_update_server_info (GvcMixerControl *control,
1461                         int              index)
1462 {
1463         pa_operation *o;
1464 
1465         o = pa_context_get_server_info (control->priv->pa_context,
1466                                         _pa_context_get_server_info_cb,
1467                                         control);
1468         if (o == NULL) {
1469                 g_warning ("pa_context_get_server_info() failed");
1470                 return;
1471         }
1472         pa_operation_unref (o);
1473 }
1474 
1475 static void
1476 req_update_client_info (GvcMixerControl *control,
1477                         int              index)
1478 {
1479         pa_operation *o;
1480 
1481         if (index < 0) {
1482                 o = pa_context_get_client_info_list (control->priv->pa_context,
1483                                                      _pa_context_get_client_info_cb,
1484                                                      control);
1485         } else {
1486                 o = pa_context_get_client_info (control->priv->pa_context,
1487                                                 index,
1488                                                 _pa_context_get_client_info_cb,
1489                                                 control);
1490         }
1491 
1492         if (o == NULL) {
1493                 g_warning ("pa_context_client_info_list() failed");
1494                 return;
1495         }
1496         pa_operation_unref (o);
1497 }
1498 
1499 static void
1500 req_update_card (GvcMixerControl *control,
1501                  int              index)
1502 {
1503         pa_operation *o;
1504 
1505         if (index < 0) {
1506                 o = pa_context_get_card_info_list (control->priv->pa_context,
1507                                                    _pa_context_get_card_info_by_index_cb,
1508                                                    control);
1509         } else {
1510                 o = pa_context_get_card_info_by_index (control->priv->pa_context,
1511                                                        index,
1512                                                        _pa_context_get_card_info_by_index_cb,
1513                                                        control);
1514         }
1515 
1516         if (o == NULL) {
1517                 g_warning ("pa_context_get_card_info_by_index() failed");
1518                 return;
1519         }
1520         pa_operation_unref (o);
1521 }
1522 
1523 static void
1524 req_update_sink_info (GvcMixerControl *control,
1525                       int              index)
1526 {
1527         pa_operation *o;
1528 
1529         if (index < 0) {
1530                 o = pa_context_get_sink_info_list (control->priv->pa_context,
1531                                                    _pa_context_get_sink_info_cb,
1532                                                    control);
1533         } else {
1534                 o = pa_context_get_sink_info_by_index (control->priv->pa_context,
1535                                                        index,
1536                                                        _pa_context_get_sink_info_cb,
1537                                                        control);
1538         }
1539 
1540         if (o == NULL) {
1541                 g_warning ("pa_context_get_sink_info_list() failed");
1542                 return;
1543         }
1544         pa_operation_unref (o);
1545 }
1546 
1547 static void
1548 req_update_source_info (GvcMixerControl *control,
1549                         int              index)
1550 {
1551         pa_operation *o;
1552 
1553         if (index < 0) {
1554                 o = pa_context_get_source_info_list (control->priv->pa_context,
1555                                                      _pa_context_get_source_info_cb,
1556                                                      control);
1557         } else {
1558                 o = pa_context_get_source_info_by_index(control->priv->pa_context,
1559                                                         index,
1560                                                         _pa_context_get_source_info_cb,
1561                                                         control);
1562         }
1563 
1564         if (o == NULL) {
1565                 g_warning ("pa_context_get_source_info_list() failed");
1566                 return;
1567         }
1568         pa_operation_unref (o);
1569 }
1570 
1571 static void
1572 req_update_sink_input_info (GvcMixerControl *control,
1573                             int              index)
1574 {
1575         pa_operation *o;
1576 
1577         if (index < 0) {
1578                 o = pa_context_get_sink_input_info_list (control->priv->pa_context,
1579                                                          _pa_context_get_sink_input_info_cb,
1580                                                          control);
1581         } else {
1582                 o = pa_context_get_sink_input_info (control->priv->pa_context,
1583                                                     index,
1584                                                     _pa_context_get_sink_input_info_cb,
1585                                                     control);
1586         }
1587 
1588         if (o == NULL) {
1589                 g_warning ("pa_context_get_sink_input_info_list() failed");
1590                 return;
1591         }
1592         pa_operation_unref (o);
1593 }
1594 
1595 static void
1596 req_update_source_output_info (GvcMixerControl *control,
1597                                int              index)
1598 {
1599         pa_operation *o;
1600 
1601         if (index < 0) {
1602                 o = pa_context_get_source_output_info_list (control->priv->pa_context,
1603                                                             _pa_context_get_source_output_info_cb,
1604                                                             control);
1605         } else {
1606                 o = pa_context_get_source_output_info (control->priv->pa_context,
1607                                                        index,
1608                                                        _pa_context_get_source_output_info_cb,
1609                                                        control);
1610         }
1611 
1612         if (o == NULL) {
1613                 g_warning ("pa_context_get_source_output_info_list() failed");
1614                 return;
1615         }
1616         pa_operation_unref (o);
1617 }
1618 
1619 static void
1620 remove_client (GvcMixerControl *control,
1621                guint            index)
1622 {
1623         g_hash_table_remove (control->priv->clients,
1624                              GUINT_TO_POINTER (index));
1625 }
1626 
1627 static void
1628 remove_card (GvcMixerControl *control,
1629              guint            index)
1630 {
1631         g_hash_table_remove (control->priv->cards,
1632                              GUINT_TO_POINTER (index));
1633 
1634         g_signal_emit (G_OBJECT (control),
1635                        signals[CARD_REMOVED],
1636                        0,
1637                        index);
1638 }
1639 
1640 static void
1641 remove_sink (GvcMixerControl *control,
1642              guint            index)
1643 {
1644         GvcMixerStream *stream;
1645 
1646 #if 0
1647         g_debug ("Removing sink: index=%u", index);
1648 #endif
1649 
1650         stream = g_hash_table_lookup (control->priv->sinks,
1651                                       GUINT_TO_POINTER (index));
1652         if (stream == NULL) {
1653                 return;
1654         }
1655         g_hash_table_remove (control->priv->sinks,
1656                              GUINT_TO_POINTER (index));
1657 
1658         remove_stream (control, stream);
1659 }
1660 
1661 static void
1662 remove_source (GvcMixerControl *control,
1663                guint            index)
1664 {
1665         GvcMixerStream *stream;
1666 
1667 #if 0
1668         g_debug ("Removing source: index=%u", index);
1669 #endif
1670 
1671         stream = g_hash_table_lookup (control->priv->sources,
1672                                       GUINT_TO_POINTER (index));
1673         if (stream == NULL) {
1674                 return;
1675         }
1676         g_hash_table_remove (control->priv->sources,
1677                              GUINT_TO_POINTER (index));
1678 
1679         remove_stream (control, stream);
1680 }
1681 
1682 static void
1683 remove_sink_input (GvcMixerControl *control,
1684                    guint            index)
1685 {
1686         GvcMixerStream *stream;
1687 
1688 #if 0
1689         g_debug ("Removing sink input: index=%u", index);
1690 #endif
1691         stream = g_hash_table_lookup (control->priv->sink_inputs,
1692                                       GUINT_TO_POINTER (index));
1693         if (stream == NULL) {
1694                 return;
1695         }
1696         g_hash_table_remove (control->priv->sink_inputs,
1697                              GUINT_TO_POINTER (index));
1698 
1699         remove_stream (control, stream);
1700 }
1701 
1702 static void
1703 remove_source_output (GvcMixerControl *control,
1704                       guint            index)
1705 {
1706         GvcMixerStream *stream;
1707 
1708 #if 0
1709         g_debug ("Removing source output: index=%u", index);
1710 #endif
1711 
1712         stream = g_hash_table_lookup (control->priv->source_outputs,
1713                                       GUINT_TO_POINTER (index));
1714         if (stream == NULL) {
1715                 return;
1716         }
1717         g_hash_table_remove (control->priv->source_outputs,
1718                              GUINT_TO_POINTER (index));
1719 
1720         remove_stream (control, stream);
1721 }
1722 
1723 static void
1724 _pa_context_subscribe_cb (pa_context                  *context,
1725                           pa_subscription_event_type_t t,
1726                           uint32_t                     index,
1727                           void                        *userdata)
1728 {
1729         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1730 
1731         switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
1732         case PA_SUBSCRIPTION_EVENT_SINK:
1733                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1734                         remove_sink (control, index);
1735                 } else {
1736                         req_update_sink_info (control, index);
1737                 }
1738                 break;
1739 
1740         case PA_SUBSCRIPTION_EVENT_SOURCE:
1741                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1742                         remove_source (control, index);
1743                 } else {
1744                         req_update_source_info (control, index);
1745                 }
1746                 break;
1747 
1748         case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
1749                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1750                         remove_sink_input (control, index);
1751                 } else {
1752                         req_update_sink_input_info (control, index);
1753                 }
1754                 break;
1755 
1756         case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
1757                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1758                         remove_source_output (control, index);
1759                 } else {
1760                         req_update_source_output_info (control, index);
1761                 }
1762                 break;
1763 
1764         case PA_SUBSCRIPTION_EVENT_CLIENT:
1765                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1766                         remove_client (control, index);
1767                 } else {
1768                         req_update_client_info (control, index);
1769                 }
1770                 break;
1771 
1772         case PA_SUBSCRIPTION_EVENT_SERVER:
1773                 req_update_server_info (control, index);
1774                 break;
1775 
1776         case PA_SUBSCRIPTION_EVENT_CARD:
1777                 if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
1778                         remove_card (control, index);
1779                 } else {
1780                         req_update_card (control, index);
1781                 }
1782                 break;
1783         }
1784 }
1785 
1786 static void
1787 gvc_mixer_control_ready (GvcMixerControl *control)
1788 {
1789         pa_operation *o;
1790 
1791         pa_context_set_subscribe_callback (control->priv->pa_context,
1792                                            _pa_context_subscribe_cb,
1793                                            control);
1794         o = pa_context_subscribe (control->priv->pa_context,
1795                                   (pa_subscription_mask_t)
1796                                   (PA_SUBSCRIPTION_MASK_SINK|
1797                                    PA_SUBSCRIPTION_MASK_SOURCE|
1798                                    PA_SUBSCRIPTION_MASK_SINK_INPUT|
1799                                    PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1800                                    PA_SUBSCRIPTION_MASK_CLIENT|
1801                                    PA_SUBSCRIPTION_MASK_SERVER|
1802                                    PA_SUBSCRIPTION_MASK_CARD),
1803                                   NULL,
1804                                   NULL);
1805 
1806         if (o == NULL) {
1807                 g_warning ("pa_context_subscribe() failed");
1808                 return;
1809         }
1810         pa_operation_unref (o);
1811 
1812         req_update_server_info (control, -1);
1813         req_update_client_info (control, -1);
1814         req_update_sink_info (control, -1);
1815         req_update_source_info (control, -1);
1816         req_update_sink_input_info (control, -1);
1817         req_update_source_output_info (control, -1);
1818         req_update_card (control, -1);
1819 
1820         control->priv->n_outstanding = 6;
1821 
1822         /* This call is not always supported */
1823         o = pa_ext_stream_restore_read (control->priv->pa_context,
1824                                         _pa_ext_stream_restore_read_cb,
1825                                         control);
1826         if (o != NULL) {
1827                 pa_operation_unref (o);
1828                 control->priv->n_outstanding++;
1829 
1830                 pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
1831                                                         _pa_ext_stream_restore_subscribe_cb,
1832                                                         control);
1833 
1834                 o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
1835                                                      1,
1836                                                      NULL,
1837                                                      NULL);
1838                 if (o != NULL) {
1839                         pa_operation_unref (o);
1840                 }
1841 
1842         } else {
1843                 g_debug ("Failed to initialized stream_restore extension: %s",
1844                          pa_strerror (pa_context_errno (control->priv->pa_context)));
1845         }
1846 }
1847 
1848 static void
1849 gvc_mixer_new_pa_context (GvcMixerControl *self)
1850 {
1851         pa_proplist     *proplist;
1852 
1853         g_return_if_fail (self);
1854         g_return_if_fail (!self->priv->pa_context);
1855 
1856         proplist = pa_proplist_new ();
1857         pa_proplist_sets (proplist,
1858                           PA_PROP_APPLICATION_NAME,
1859                           self->priv->name);
1860         pa_proplist_sets (proplist,
1861                           PA_PROP_APPLICATION_ID,
1862                           "org.gnome.VolumeControl");
1863         pa_proplist_sets (proplist,
1864                           PA_PROP_APPLICATION_ICON_NAME,
1865                           "multimedia-volume-control");
1866         pa_proplist_sets (proplist,
1867                           PA_PROP_APPLICATION_VERSION,
1868                           PACKAGE_VERSION);
1869 
1870         self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
1871 
1872         pa_proplist_free (proplist);
1873         g_assert (self->priv->pa_context);
1874 }
1875 
1876 static void
1877 remove_all_streams (GvcMixerControl *control, GHashTable *hash_table)
1878 {
1879         GHashTableIter iter;
1880         gpointer key, value;
1881 
1882         g_hash_table_iter_init (&iter, hash_table);
1883         while (g_hash_table_iter_next (&iter, &key, &value)) {
1884                 remove_stream (control, value);
1885                 g_hash_table_iter_remove (&iter);
1886         }
1887 }
1888 
1889 static gboolean
1890 idle_reconnect (gpointer data)
1891 {
1892         GvcMixerControl *control = GVC_MIXER_CONTROL (data);
1893         GHashTableIter iter;
1894         gpointer key, value;
1895 
1896         g_return_val_if_fail (control, FALSE);
1897 
1898         if (control->priv->pa_context) {
1899                 pa_context_unref (control->priv->pa_context);
1900                 control->priv->pa_context = NULL;
1901                 gvc_mixer_new_pa_context (control);
1902         }
1903 
1904         remove_all_streams (control, control->priv->sinks);
1905         remove_all_streams (control, control->priv->sources);
1906         remove_all_streams (control, control->priv->sink_inputs);
1907         remove_all_streams (control, control->priv->source_outputs);
1908 
1909         g_hash_table_iter_init (&iter, control->priv->clients);
1910         while (g_hash_table_iter_next (&iter, &key, &value))
1911                 g_hash_table_iter_remove (&iter);
1912 
1913         gvc_mixer_control_open (control); /* cannot fail */
1914 
1915         control->priv->reconnect_id = 0;
1916         return FALSE;
1917 }
1918 
1919 static void
1920 _pa_context_state_cb (pa_context *context,
1921                       void       *userdata)
1922 {
1923         GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
1924 
1925         switch (pa_context_get_state (context)) {
1926         case PA_CONTEXT_UNCONNECTED:
1927         case PA_CONTEXT_CONNECTING:
1928         case PA_CONTEXT_AUTHORIZING:
1929         case PA_CONTEXT_SETTING_NAME:
1930                 break;
1931 
1932         case PA_CONTEXT_READY:
1933                 gvc_mixer_control_ready (control);
1934                 break;
1935 
1936         case PA_CONTEXT_FAILED:
1937                 control->priv->state = GVC_STATE_FAILED;
1938                 g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED);
1939                 if (control->priv->reconnect_id == 0)
1940                         control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
1941                 break;
1942 
1943         case PA_CONTEXT_TERMINATED:
1944         default:
1945                 /* FIXME: */
1946                 break;
1947         }
1948 }
1949 
1950 gboolean
1951 gvc_mixer_control_open (GvcMixerControl *control)
1952 {
1953         int res;
1954 
1955         g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
1956         g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
1957         g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE);
1958 
1959         pa_context_set_state_callback (control->priv->pa_context,
1960                                        _pa_context_state_cb,
1961                                        control);
1962 
1963         control->priv->state = GVC_STATE_CONNECTING;
1964         g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING);
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 
1982         control->priv->state = GVC_STATE_CLOSED;
1983         g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED);
1984         return TRUE;
1985 }
1986 
1987 static void
1988 gvc_mixer_control_dispose (GObject *object)
1989 {
1990         GvcMixerControl *control = GVC_MIXER_CONTROL (object);
1991 
1992         if (control->priv->reconnect_id != 0) {
1993                 g_source_remove (control->priv->reconnect_id);
1994                 control->priv->reconnect_id = 0;
1995         }
1996 
1997         if (control->priv->pa_context != NULL) {
1998                 pa_context_unref (control->priv->pa_context);
1999                 control->priv->pa_context = NULL;
2000         }
2001 
2002         if (control->priv->default_source_name != NULL) {
2003                 g_free (control->priv->default_source_name);
2004                 control->priv->default_source_name = NULL;
2005         }
2006         if (control->priv->default_sink_name != NULL) {
2007                 g_free (control->priv->default_sink_name);
2008                 control->priv->default_sink_name = NULL;
2009         }
2010 
2011         if (control->priv->pa_mainloop != NULL) {
2012                 pa_glib_mainloop_free (control->priv->pa_mainloop);
2013                 control->priv->pa_mainloop = NULL;
2014         }
2015 
2016         if (control->priv->all_streams != NULL) {
2017                 g_hash_table_destroy (control->priv->all_streams);
2018                 control->priv->all_streams = NULL;
2019         }
2020 
2021         if (control->priv->sinks != NULL) {
2022                 g_hash_table_destroy (control->priv->sinks);
2023                 control->priv->sinks = NULL;
2024         }
2025         if (control->priv->sources != NULL) {
2026                 g_hash_table_destroy (control->priv->sources);
2027                 control->priv->sources = NULL;
2028         }
2029         if (control->priv->sink_inputs != NULL) {
2030                 g_hash_table_destroy (control->priv->sink_inputs);
2031                 control->priv->sink_inputs = NULL;
2032         }
2033         if (control->priv->source_outputs != NULL) {
2034                 g_hash_table_destroy (control->priv->source_outputs);
2035                 control->priv->source_outputs = NULL;
2036         }
2037         if (control->priv->clients != NULL) {
2038                 g_hash_table_destroy (control->priv->clients);
2039                 control->priv->clients = NULL;
2040         }
2041         if (control->priv->cards != NULL) {
2042                 g_hash_table_destroy (control->priv->cards);
2043                 control->priv->cards = NULL;
2044         }
2045 
2046         G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
2047 }
2048 
2049 static void
2050 gvc_mixer_control_set_property (GObject       *object,
2051                                 guint          prop_id,
2052                                 const GValue  *value,
2053                                 GParamSpec    *pspec)
2054 {
2055         GvcMixerControl *self = GVC_MIXER_CONTROL (object);
2056 
2057         switch (prop_id) {
2058         case PROP_NAME:
2059                 g_free (self->priv->name);
2060                 self->priv->name = g_value_dup_string (value);
2061                 g_object_notify (G_OBJECT (self), "name");
2062                 break;
2063         default:
2064                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2065                 break;
2066         }
2067 }
2068 
2069 static void
2070 gvc_mixer_control_get_property (GObject     *object,
2071                                 guint        prop_id,
2072                                 GValue      *value,
2073                                 GParamSpec  *pspec)
2074 {
2075         GvcMixerControl *self = GVC_MIXER_CONTROL (object);
2076 
2077         switch (prop_id) {
2078         case PROP_NAME:
2079                 g_value_set_string (value, self->priv->name);
2080                 break;
2081         default:
2082                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2083                 break;
2084         }
2085 }
2086 
2087 
2088 static GObject *
2089 gvc_mixer_control_constructor (GType                  type,
2090                                guint                  n_construct_properties,
2091                                GObjectConstructParam *construct_params)
2092 {
2093         GObject         *object;
2094         GvcMixerControl *self;
2095 
2096         object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
2097 
2098         self = GVC_MIXER_CONTROL (object);
2099 
2100         gvc_mixer_new_pa_context (self);
2101 
2102         return object;
2103 }
2104 
2105 static void
2106 gvc_mixer_control_class_init (GvcMixerControlClass *klass)
2107 {
2108         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
2109 
2110         object_class->constructor = gvc_mixer_control_constructor;
2111         object_class->dispose = gvc_mixer_control_dispose;
2112         object_class->finalize = gvc_mixer_control_finalize;
2113         object_class->set_property = gvc_mixer_control_set_property;
2114         object_class->get_property = gvc_mixer_control_get_property;
2115 
2116         g_object_class_install_property (object_class,
2117                                          PROP_NAME,
2118                                          g_param_spec_string ("name",
2119                                                               "Name",
2120                                                               "Name to display for this mixer control",
2121                                                               NULL,
2122                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
2123 
2124         signals [STATE_CHANGED] =
2125                 g_signal_new ("state-changed",
2126                               G_TYPE_FROM_CLASS (klass),
2127                               G_SIGNAL_RUN_LAST,
2128                               G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
2129                               NULL, NULL, NULL,
2130                               G_TYPE_NONE, 1, G_TYPE_UINT);
2131         signals [STREAM_ADDED] =
2132                 g_signal_new ("stream-added",
2133                               G_TYPE_FROM_CLASS (klass),
2134                               G_SIGNAL_RUN_LAST,
2135                               G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
2136                               NULL, NULL, NULL,
2137                               G_TYPE_NONE, 1, G_TYPE_UINT);
2138         signals [STREAM_REMOVED] =
2139                 g_signal_new ("stream-removed",
2140                               G_TYPE_FROM_CLASS (klass),
2141                               G_SIGNAL_RUN_LAST,
2142                               G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
2143                               NULL, NULL, NULL,
2144                               G_TYPE_NONE, 1, G_TYPE_UINT);
2145         signals [CARD_ADDED] =
2146                 g_signal_new ("card-added",
2147                               G_TYPE_FROM_CLASS (klass),
2148                               G_SIGNAL_RUN_LAST,
2149                               G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
2150                               NULL, NULL, NULL,
2151                               G_TYPE_NONE, 1, G_TYPE_UINT);
2152         signals [CARD_REMOVED] =
2153                 g_signal_new ("card-removed",
2154                               G_TYPE_FROM_CLASS (klass),
2155                               G_SIGNAL_RUN_LAST,
2156                               G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
2157                               NULL, NULL, NULL,
2158                               G_TYPE_NONE, 1, G_TYPE_UINT);
2159         signals [DEFAULT_SINK_CHANGED] =
2160                 g_signal_new ("default-sink-changed",
2161                               G_TYPE_FROM_CLASS (klass),
2162                               G_SIGNAL_RUN_LAST,
2163                               G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
2164                               NULL, NULL, NULL,
2165                               G_TYPE_NONE, 1, G_TYPE_UINT);
2166         signals [DEFAULT_SOURCE_CHANGED] =
2167                 g_signal_new ("default-source-changed",
2168                               G_TYPE_FROM_CLASS (klass),
2169                               G_SIGNAL_RUN_LAST,
2170                               G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
2171                               NULL, NULL, NULL,
2172                               G_TYPE_NONE, 1, G_TYPE_UINT);
2173 
2174         g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
2175 }
2176 
2177 static void
2178 gvc_mixer_control_init (GvcMixerControl *control)
2179 {
2180         control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control);
2181 
2182         control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
2183         g_assert (control->priv->pa_mainloop);
2184 
2185         control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
2186         g_assert (control->priv->pa_api);
2187 
2188         control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2189         control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2190         control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2191         control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2192         control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2193         control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
2194 
2195         control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
2196 
2197         control->priv->state = GVC_STATE_CLOSED;
2198 }
2199 
2200 static void
2201 gvc_mixer_control_finalize (GObject *object)
2202 {
2203         GvcMixerControl *mixer_control;
2204 
2205         g_return_if_fail (object != NULL);
2206         g_return_if_fail (GVC_IS_MIXER_CONTROL (object));
2207 
2208         mixer_control = GVC_MIXER_CONTROL (object);
2209         g_free (mixer_control->priv->name);
2210         mixer_control->priv->name = NULL;
2211 
2212         g_return_if_fail (mixer_control->priv != NULL);
2213         G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
2214 }
2215 
2216 GvcMixerControl *
2217 gvc_mixer_control_new (const char *name)
2218 {
2219         GObject *control;
2220         control = g_object_new (GVC_TYPE_MIXER_CONTROL,
2221                                 "name", name,
2222                                 NULL);
2223         return GVC_MIXER_CONTROL (control);
2224 }
2225 
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 gdouble
2232 gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
2233 {
2234 	return (gdouble) PA_VOLUME_NORM;
2235 }
2236 
2237 gdouble
2238 gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control)
2239 {
2240 	return (gdouble) PA_VOLUME_UI_MAX;
2241 }