File libgnome-volume-control-0.gitmodule.obscpio of Package gnome-shell

07070100000000000081A400000000000000000000000166437C9400000067000000000000000000000000000000000000002F00000000libgnome-volume-control-0.gitmodule/.gitignore.deps/
.libs/
.dirstamp
Makefile.in
Makefile
*.la
*.lo
*.o
*.gir
*.typelib
test-audio-device-selection
07070100000001000041ED00000000000000000000000266437C9400000000000000000000000000000000000000000000002F00000000libgnome-volume-control-0.gitmodule/.gitlab-ci07070100000002000081A400000000000000000000000166437C9400000149000000000000000000000000000000000000003300000000libgnome-volume-control-0.gitmodule/.gitlab-ci.ymlstages:
- test

build-fedora:
  image: fedora:latest
  stage: test
  before_script:
    - dnf install -y redhat-rpm-config gcc clang meson pulseaudio-libs-devel alsa-lib-devel gtk3-devel
  script:
    - cd .gitlab-ci
    - meson _build
    - ninja -C _build
    - rm -rf _build
    - CC=clang meson _build
    - ninja -C _build

07070100000003000081A400000000000000000000000166437C9400000246000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/.gitlab-ci/meson.buildproject('gnome-volume-control-ci', 'c',
  version: '1.0.0',
  meson_version: '>= 0.47.0',
  license: 'GPLv2+'
)

prefix = get_option('prefix')

datadir = join_paths(prefix, get_option('datadir'))
libdir = join_paths(prefix, get_option('libdir'))

pkgdatadir = join_paths(datadir, meson.project_name())
pkglibdir = join_paths(libdir, meson.project_name())

libgvc = subproject('gvc',
  default_options: [
    'package_name=' + meson.project_name(),
    'package_version=' + meson.project_version(),
    'pkgdatadir=' + pkgdatadir,
    'pkglibdir=' + pkglibdir,
    'alsa=true'
  ]
)
07070100000004000041ED00000000000000000000000266437C9400000000000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/.gitlab-ci/subprojects070701000000050000A1FF00000000000000000000000166437C9400000006000000000000000000000000000000000000003F00000000libgnome-volume-control-0.gitmodule/.gitlab-ci/subprojects/gvc../../07070100000006000081A400000000000000000000000166437C9400000218000000000000000000000000000000000000002E00000000libgnome-volume-control-0.gitmodule/README.md# libgnome-volume-control

libgnome-volume-control is a copy library that's supposed to be used as
a git sub-module. If your project uses some of libgnome-volume-control's
strings in a user-facing manner, don't forget to add those files to your
POTFILES.in for translation.

## Projects using libgnome-volume-control

- [gnome-shell](https://gitlab.gnome.org/GNOME/gnome-shell)
- [gnome-settings-daemon](https://gitlab.gnome.org/GNOME/gnome-settings-daemon)
- [gnome-control-center](https://gitlab.gnome.org/GNOME/gnome-control-center)
07070100000007000081A400000000000000000000000166437C940000062E000000000000000000000000000000000000003E00000000libgnome-volume-control-0.gitmodule/gvc-channel-map-private.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_CHANNEL_MAP_PRIVATE_H
#define __GVC_CHANNEL_MAP_PRIVATE_H

#include <glib-object.h>
#include <pulse/pulseaudio.h>

G_BEGIN_DECLS

GvcChannelMap *         gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map);
const pa_channel_map *  gvc_channel_map_get_pa_channel_map      (const GvcChannelMap  *map);

void                    gvc_channel_map_volume_changed          (GvcChannelMap    *map,
                                                                 const pa_cvolume *cv,
                                                                 gboolean          set);
const pa_cvolume *      gvc_channel_map_get_cvolume             (const GvcChannelMap  *map);

G_END_DECLS

#endif /* __GVC_CHANNEL_MAP_PRIVATE_H */
07070100000008000081A400000000000000000000000166437C9400001C10000000000000000000000000000000000000003600000000libgnome-volume-control-0.gitmodule/gvc-channel-map.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-channel-map.h"
#include "gvc-channel-map-private.h"

struct GvcChannelMapPrivate
{
        pa_channel_map        pa_map;
        gboolean              pa_volume_is_set;
        pa_cvolume            pa_volume;
        gdouble               extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */
        gboolean              can_balance;
        gboolean              can_fade;
};

enum {
        VOLUME_CHANGED,
        LAST_SIGNAL
};

static guint signals [LAST_SIGNAL] = { 0, };

static void     gvc_channel_map_finalize   (GObject            *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT)

guint
gvc_channel_map_get_num_channels (const GvcChannelMap *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0);

        if (!pa_channel_map_valid(&map->priv->pa_map))
                return 0;

        return map->priv->pa_map.channels;
}

const gdouble *
gvc_channel_map_get_volume (GvcChannelMap *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);

        if (!pa_channel_map_valid(&map->priv->pa_map))
                return NULL;

        map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume);
        if (gvc_channel_map_can_balance (map))
                map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map);
        else
                map->priv->extern_volume[BALANCE] = 0;
        if (gvc_channel_map_can_fade (map))
                map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map);
        else
                map->priv->extern_volume[FADE] = 0;
        if (gvc_channel_map_has_lfe (map))
                map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE);
        else
                map->priv->extern_volume[LFE] = 0;

        return map->priv->extern_volume;
}

gboolean
gvc_channel_map_can_balance (const GvcChannelMap  *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);

        return map->priv->can_balance;
}

gboolean
gvc_channel_map_can_fade (const GvcChannelMap  *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);

        return map->priv->can_fade;
}

const char *
gvc_channel_map_get_mapping (const GvcChannelMap  *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);

        if (!pa_channel_map_valid(&map->priv->pa_map))
                return NULL;

        return pa_channel_map_to_pretty_name (&map->priv->pa_map);
}

/**
 * gvc_channel_map_has_position: (skip)
 * @map:
 * @position:
 *
 * Returns:
 */
gboolean
gvc_channel_map_has_position (const GvcChannelMap  *map,
                              pa_channel_position_t position)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);

        return pa_channel_map_has_position (&(map->priv->pa_map), position);
}

const pa_channel_map *
gvc_channel_map_get_pa_channel_map (const GvcChannelMap  *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);

        if (!pa_channel_map_valid(&map->priv->pa_map))
                return NULL;

        return &map->priv->pa_map;
}

const pa_cvolume *
gvc_channel_map_get_cvolume (const GvcChannelMap  *map)
{
        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);

        if (!pa_channel_map_valid(&map->priv->pa_map))
                return NULL;

        return &map->priv->pa_volume;
}

static void
gvc_channel_map_class_init (GvcChannelMapClass *klass)
{
        GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->finalize = gvc_channel_map_finalize;

        signals [VOLUME_CHANGED] =
                g_signal_new ("volume-changed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}

void
gvc_channel_map_volume_changed (GvcChannelMap     *map,
                                const pa_cvolume  *cv,
                                gboolean           set)
{
        g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
        g_return_if_fail (cv != NULL);
        g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map));

        if (pa_cvolume_equal(cv, &map->priv->pa_volume))
                return;

        map->priv->pa_volume = *cv;

        if (map->priv->pa_volume_is_set == FALSE) {
                map->priv->pa_volume_is_set = TRUE;
                return;
        }
        g_signal_emit (map, signals[VOLUME_CHANGED], 0, set);
}

static void
gvc_channel_map_init (GvcChannelMap *map)
{
        map->priv = gvc_channel_map_get_instance_private (map);
        map->priv->pa_volume_is_set = FALSE;
}

static void
gvc_channel_map_finalize (GObject *object)
{
        GvcChannelMap *channel_map;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_CHANNEL_MAP (object));

        channel_map = GVC_CHANNEL_MAP (object);

        g_return_if_fail (channel_map->priv != NULL);

        G_OBJECT_CLASS (gvc_channel_map_parent_class)->finalize (object);
}

GvcChannelMap *
gvc_channel_map_new (void)
{
        GObject *map;
        map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL);
        return GVC_CHANNEL_MAP (map);
}

static void
set_from_pa_map (GvcChannelMap        *map,
                 const pa_channel_map *pa_map)
{
        g_assert (pa_channel_map_valid(pa_map));

        map->priv->can_balance = pa_channel_map_can_balance (pa_map);
        map->priv->can_fade = pa_channel_map_can_fade (pa_map);

        map->priv->pa_map = *pa_map;
        pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM);
}

GvcChannelMap *
gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *pa_map)
{
        GObject *map;
        map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL);

        set_from_pa_map (GVC_CHANNEL_MAP (map), pa_map);

        return GVC_CHANNEL_MAP (map);
}
07070100000009000081A400000000000000000000000166437C9400000B78000000000000000000000000000000000000003600000000libgnome-volume-control-0.gitmodule/gvc-channel-map.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_CHANNEL_MAP_H
#define __GVC_CHANNEL_MAP_H

#include <glib-object.h>
#include <gvc-pulseaudio-fake.h>

G_BEGIN_DECLS

#define GVC_TYPE_CHANNEL_MAP         (gvc_channel_map_get_type ())
#define GVC_CHANNEL_MAP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMap))
#define GVC_CHANNEL_MAP_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass))
#define GVC_IS_CHANNEL_MAP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_MAP))
#define GVC_IS_CHANNEL_MAP_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_MAP))
#define GVC_CHANNEL_MAP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass))

typedef struct GvcChannelMapPrivate GvcChannelMapPrivate;

typedef struct
{
        GObject               parent;
        GvcChannelMapPrivate *priv;
} GvcChannelMap;

typedef struct
{
        GObjectClass           parent_class;
        void (*volume_changed) (GvcChannelMap *channel_map, gboolean set);
} GvcChannelMapClass;

enum {
        VOLUME,
        BALANCE,
        FADE,
        LFE,
        NUM_TYPES
};

GType                   gvc_channel_map_get_type                (void);

GvcChannelMap *         gvc_channel_map_new                     (void);
guint                   gvc_channel_map_get_num_channels        (const GvcChannelMap  *map);
const gdouble *         gvc_channel_map_get_volume              (GvcChannelMap  *map);
gboolean                gvc_channel_map_can_balance             (const GvcChannelMap  *map);
gboolean                gvc_channel_map_can_fade                (const GvcChannelMap  *map);
gboolean                gvc_channel_map_has_position            (const GvcChannelMap  *map,
                                                                 pa_channel_position_t position);
#define                 gvc_channel_map_has_lfe(x)              gvc_channel_map_has_position (x, PA_CHANNEL_POSITION_LFE)

const char *            gvc_channel_map_get_mapping             (const GvcChannelMap  *map);

G_END_DECLS

#endif /* __GVC_CHANNEL_MAP_H */
0707010000000A000081A400000000000000000000000166437C94000005DF000000000000000000000000000000000000003D00000000libgnome-volume-control-0.gitmodule/gvc-mixer-card-private.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008-2009 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_CARD_PRIVATE_H
#define __GVC_MIXER_CARD_PRIVATE_H

#include <pulse/pulseaudio.h>
#include "gvc-mixer-card.h"

G_BEGIN_DECLS

GvcMixerCard *        gvc_mixer_card_new               (pa_context   *context,
                                                        guint         index);
pa_context *          gvc_mixer_card_get_pa_context    (GvcMixerCard *card);

void gvc_mixer_card_add_port (GvcMixerCard     *card,
                              GvcMixerCardPort *port);

void gvc_mixer_card_remove_port (GvcMixerCard     *card,
                                 GvcMixerCardPort *port);

G_END_DECLS

#endif /* __GVC_MIXER_CARD_PRIVATE_H */
0707010000000B000081A400000000000000000000000166437C9400004A30000000000000000000000000000000000000003500000000libgnome-volume-control-0.gitmodule/gvc-mixer-card.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 * Copyright (C) 2009 Bastien Nocera
 * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-card.h"
#include "gvc-mixer-card-private.h"

static guint32 card_serial = 1;

struct GvcMixerCardPrivate
{
        pa_context    *pa_context;
        guint          id;
        guint          index;
        char          *name;
        char          *icon_name;
        char          *profile;
        char          *target_profile;
        char          *human_profile;
        GList         *profiles;
        pa_operation  *profile_op;
        GList         *ports;
};

enum
{
        PROP_0,
        PROP_ID,
        PROP_PA_CONTEXT,
        PROP_INDEX,
        PROP_NAME,
        PROP_ICON_NAME,
        PROP_PROFILE,
        PROP_HUMAN_PROFILE,
        N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };

static void     gvc_mixer_card_finalize   (GObject            *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerCard, gvc_mixer_card, G_TYPE_OBJECT)

static guint32
get_next_card_serial (void)
{
        guint32 serial;

        serial = card_serial++;

        if ((gint32)card_serial < 0) {
                card_serial = 1;
        }

        return serial;
}

pa_context *
gvc_mixer_card_get_pa_context (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
        return card->priv->pa_context;
}

guint
gvc_mixer_card_get_index (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
        return card->priv->index;
}

guint
gvc_mixer_card_get_id (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0);
        return card->priv->id;
}

const char *
gvc_mixer_card_get_name (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
        return card->priv->name;
}

gboolean
gvc_mixer_card_set_name (GvcMixerCard *card,
                         const char     *name)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);

        g_free (card->priv->name);
        card->priv->name = g_strdup (name);
        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_NAME]);

        return TRUE;
}

const char *
gvc_mixer_card_get_icon_name (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
        return card->priv->icon_name;
}

gboolean
gvc_mixer_card_set_icon_name (GvcMixerCard *card,
                              const char     *icon_name)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);

        g_free (card->priv->icon_name);
        card->priv->icon_name = g_strdup (icon_name);
        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_ICON_NAME]);

        return TRUE;
}

/**
 * gvc_mixer_card_get_profile: (skip)
 * @card:
 *
 * Returns:
 */
GvcMixerCardProfile *
gvc_mixer_card_get_profile (GvcMixerCard *card)
{
        GList *l;

        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
        g_return_val_if_fail (card->priv->profiles != NULL, NULL);

        for (l = card->priv->profiles; l != NULL; l = l->next) {
                GvcMixerCardProfile *p = l->data;
                if (g_str_equal (card->priv->profile, p->profile)) {
                        return p;
                }
        }

        g_assert_not_reached ();

        return NULL;
}

gboolean
gvc_mixer_card_set_profile (GvcMixerCard *card,
                            const char     *profile)
{
        GList *l;

        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
        g_return_val_if_fail (card->priv->profiles != NULL, FALSE);

        if (g_strcmp0 (card->priv->profile, profile) == 0)
                return TRUE;

        g_free (card->priv->profile);
        card->priv->profile = g_strdup (profile);

        g_free (card->priv->human_profile);
        card->priv->human_profile = NULL;

        for (l = card->priv->profiles; l != NULL; l = l->next) {
                GvcMixerCardProfile *p = l->data;
                if (g_str_equal (card->priv->profile, p->profile)) {
                        card->priv->human_profile = g_strdup (p->human_profile);
                        break;
                }
        }

        g_object_notify_by_pspec (G_OBJECT (card), obj_props[PROP_PROFILE]);

        return TRUE;
}

static void
_pa_context_set_card_profile_by_index_cb (pa_context                       *context,
                                          int                               success,
                                          void                             *userdata)
{
        GvcMixerCard *card = GVC_MIXER_CARD (userdata);

        g_assert (card->priv->target_profile);

        if (success > 0) {
                gvc_mixer_card_set_profile (card, card->priv->target_profile);
        } else {
                g_debug ("Failed to switch profile on '%s' from '%s' to '%s'",
                         card->priv->name,
                         card->priv->profile,
                         card->priv->target_profile);
        }
        g_free (card->priv->target_profile);
        card->priv->target_profile = NULL;

        pa_operation_unref (card->priv->profile_op);
        card->priv->profile_op = NULL;
}

/**
 * gvc_mixer_card_change_profile:
 * @card: a #GvcMixerCard
 * @profile: (allow-none): the profile to change to or %NULL.
 *
 * Change the profile in use on this card.
 *
 * Returns: %TRUE if profile successfully changed or already using this profile.
 */
gboolean
gvc_mixer_card_change_profile (GvcMixerCard *card,
                               const char *profile)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
        g_return_val_if_fail (card->priv->profiles != NULL, FALSE);

        /* Same profile, or already requested? */
        if (g_strcmp0 (card->priv->profile, profile) == 0)
                return TRUE;
        if (g_strcmp0 (profile, card->priv->target_profile) == 0)
                return TRUE;
        if (card->priv->profile_op != NULL) {
                pa_operation_cancel (card->priv->profile_op);
                pa_operation_unref (card->priv->profile_op);
                card->priv->profile_op = NULL;
        }

        if (card->priv->profile != NULL) {
                g_free (card->priv->target_profile);
                card->priv->target_profile = g_strdup (profile);

                card->priv->profile_op = pa_context_set_card_profile_by_index (card->priv->pa_context,
                                                                               card->priv->index,
                                                                               card->priv->target_profile,
                                                                               _pa_context_set_card_profile_by_index_cb,
                                                                               card);

                if (card->priv->profile_op == NULL) {
                        g_warning ("pa_context_set_card_profile_by_index() failed");
                        return FALSE;
                }
        } else {
                g_assert (card->priv->human_profile == NULL);
                card->priv->profile = g_strdup (profile);
        }

        return TRUE;
}

/**
 * gvc_mixer_card_get_profiles:
 *
 * Return value: (transfer none) (element-type GvcMixerCardProfile):
 */
const GList *
gvc_mixer_card_get_profiles (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
        return card->priv->profiles;
}

/**
 * gvc_mixer_card_get_ports:
 *
 * Return value: (transfer none) (element-type GvcMixerCardPort):
 */
const GList *
gvc_mixer_card_get_ports (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);
        return card->priv->ports;
}

/**
 * gvc_mixer_card_profile_compare:
 *
 * Return value: 1 if @a has a higher priority, -1 if @b has a higher
 * priority, 0 if @a and @b have the same priority.
 */
int
gvc_mixer_card_profile_compare (GvcMixerCardProfile *a,
                                GvcMixerCardProfile *b)
{
        if (a->priority == b->priority)
                return 0;
        if (a->priority > b->priority)
                return 1;
        return -1;
}

static void
free_profile (GvcMixerCardProfile *p)
{
        g_free (p->profile);
        g_free (p->human_profile);
        g_free (p->status);
        g_free (p);
}

/**
 * gvc_mixer_card_set_profiles:
 * @profiles: (transfer full) (element-type GvcMixerCardProfile):
 */
gboolean
gvc_mixer_card_set_profiles (GvcMixerCard *card,
                             GList        *profiles)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);

        g_list_free_full (card->priv->profiles, (GDestroyNotify) free_profile);
        card->priv->profiles = g_list_sort (profiles, (GCompareFunc) gvc_mixer_card_profile_compare);

        return TRUE;
}

/**
 * gvc_mixer_card_get_gicon:
 * @card:
 *
 * Return value: (transfer full):
 */
GIcon *
gvc_mixer_card_get_gicon (GvcMixerCard *card)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL);

        if (card->priv->icon_name == NULL)
                return NULL;

        return g_themed_icon_new_with_default_fallbacks (card->priv->icon_name);
}

static void
free_port (GvcMixerCardPort *port)
{
        g_free (port->port);
        g_free (port->human_port);
        g_free (port->icon_name);
        g_list_free (port->profiles);

        g_free (port);
}

/**
 * gvc_mixer_card_set_ports:
 * @ports: (transfer full) (element-type GvcMixerCardPort):
 */
gboolean
gvc_mixer_card_set_ports (GvcMixerCard *card,
                          GList        *ports)
{
        g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
        g_return_val_if_fail (card->priv->ports == NULL, FALSE);

        g_list_free_full (card->priv->ports, (GDestroyNotify) free_port);
        card->priv->ports = ports;

        return TRUE;
}

void
gvc_mixer_card_add_port (GvcMixerCard     *card,
                         GvcMixerCardPort *port)
{
        card->priv->ports = g_list_prepend (card->priv->ports, port);
}

void
gvc_mixer_card_remove_port (GvcMixerCard     *card,
                            GvcMixerCardPort *port)
{
        card->priv->ports = g_list_remove (card->priv->ports, port);
        free_port (port);
}

static void
gvc_mixer_card_set_property (GObject       *object,
                             guint          prop_id,
                             const GValue  *value,
                             GParamSpec    *pspec)
{
        GvcMixerCard *self = GVC_MIXER_CARD (object);

        switch (prop_id) {
        case PROP_PA_CONTEXT:
                self->priv->pa_context = g_value_get_pointer (value);
                break;
        case PROP_INDEX:
                self->priv->index = g_value_get_ulong (value);
                break;
        case PROP_ID:
                self->priv->id = g_value_get_ulong (value);
                break;
        case PROP_NAME:
                gvc_mixer_card_set_name (self, g_value_get_string (value));
                break;
        case PROP_ICON_NAME:
                gvc_mixer_card_set_icon_name (self, g_value_get_string (value));
                break;
        case PROP_PROFILE:
                gvc_mixer_card_set_profile (self, g_value_get_string (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gvc_mixer_card_get_property (GObject     *object,
                             guint        prop_id,
                             GValue      *value,
                             GParamSpec  *pspec)
{
        GvcMixerCard *self = GVC_MIXER_CARD (object);

        switch (prop_id) {
        case PROP_PA_CONTEXT:
                g_value_set_pointer (value, self->priv->pa_context);
                break;
        case PROP_INDEX:
                g_value_set_ulong (value, self->priv->index);
                break;
        case PROP_ID:
                g_value_set_ulong (value, self->priv->id);
                break;
        case PROP_NAME:
                g_value_set_string (value, self->priv->name);
                break;
        case PROP_ICON_NAME:
                g_value_set_string (value, self->priv->icon_name);
                break;
        case PROP_PROFILE:
                g_value_set_string (value, self->priv->profile);
                break;
        case PROP_HUMAN_PROFILE:
                g_value_set_string (value, self->priv->human_profile);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static GObject *
gvc_mixer_card_constructor (GType                  type,
                            guint                  n_construct_properties,
                            GObjectConstructParam *construct_params)
{
        GObject       *object;
        GvcMixerCard *self;

        object = G_OBJECT_CLASS (gvc_mixer_card_parent_class)->constructor (type, n_construct_properties, construct_params);

        self = GVC_MIXER_CARD (object);

        self->priv->id = get_next_card_serial ();

        return object;
}

static void
gvc_mixer_card_class_init (GvcMixerCardClass *klass)
{
        GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->constructor = gvc_mixer_card_constructor;
        gobject_class->finalize = gvc_mixer_card_finalize;

        gobject_class->set_property = gvc_mixer_card_set_property;
        gobject_class->get_property = gvc_mixer_card_get_property;

        obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
                                                    "Index",
                                                    "The index for this card",
                                                    0, G_MAXULONG, 0,
                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_ID] = g_param_spec_ulong ("id",
                                                 "id",
                                                 "The id for this card",
                                                 0, G_MAXULONG, 0,
                                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
                                                           "PulseAudio context",
                                                           "The PulseAudio context for this card",
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_NAME] = g_param_spec_string ("name",
                                                    "Name",
                                                    "Name to display for this card",
                                                    NULL,
                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
                                                         "Icon Name",
                                                         "Name of icon to display for this card",
                                                         NULL,
                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_PROFILE] = g_param_spec_string ("profile",
                                                       "Profile",
                                                       "Name of current profile for this card",
                                                       NULL,
                                                       G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_HUMAN_PROFILE] = g_param_spec_string ("human-profile",
                                                             "Profile (Human readable)",
                                                             "Name of current profile for this card in human readable form",
                                                             NULL,
                                                             G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
}

static void
gvc_mixer_card_init (GvcMixerCard *card)
{
        card->priv = gvc_mixer_card_get_instance_private (card);
}

GvcMixerCard *
gvc_mixer_card_new (pa_context *context,
                    guint       index)
{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_CARD,
                               "index", index,
                               "pa-context", context,
                               NULL);
        return GVC_MIXER_CARD (object);
}

static void
gvc_mixer_card_finalize (GObject *object)
{
        GvcMixerCard *mixer_card;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_CARD (object));

        mixer_card = GVC_MIXER_CARD (object);

        g_return_if_fail (mixer_card->priv != NULL);

        g_free (mixer_card->priv->name);
        mixer_card->priv->name = NULL;

        g_free (mixer_card->priv->icon_name);
        mixer_card->priv->icon_name = NULL;

        g_free (mixer_card->priv->target_profile);
        mixer_card->priv->target_profile = NULL;

        g_free (mixer_card->priv->profile);
        mixer_card->priv->profile = NULL;

        g_free (mixer_card->priv->human_profile);
        mixer_card->priv->human_profile = NULL;

        g_list_free_full (mixer_card->priv->profiles, (GDestroyNotify) free_profile);
        mixer_card->priv->profiles = NULL;

        g_list_free_full (mixer_card->priv->ports, (GDestroyNotify) free_port);
        mixer_card->priv->ports = NULL;

        G_OBJECT_CLASS (gvc_mixer_card_parent_class)->finalize (object);
}

0707010000000C000081A400000000000000000000000166437C9400000FFC000000000000000000000000000000000000003500000000libgnome-volume-control-0.gitmodule/gvc-mixer-card.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008-2009 Red Hat, Inc.
 * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_CARD_H
#define __GVC_MIXER_CARD_H

#include <glib-object.h>
#include <gio/gio.h>

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_CARD         (gvc_mixer_card_get_type ())
#define GVC_MIXER_CARD(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CARD, GvcMixerCard))
#define GVC_MIXER_CARD_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CARD, GvcMixerCardClass))
#define GVC_IS_MIXER_CARD(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CARD))
#define GVC_IS_MIXER_CARD_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CARD))
#define GVC_MIXER_CARD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardClass))

typedef struct GvcMixerCardPrivate GvcMixerCardPrivate;

typedef struct
{
        GObject                parent;
        GvcMixerCardPrivate   *priv;
} GvcMixerCard;

typedef struct
{
        GObjectClass           parent_class;

        /* vtable */
} GvcMixerCardClass;

typedef struct
{
        char  *profile;
        char  *human_profile;
        char  *status;
        guint  priority;
        guint  n_sinks, n_sources;
} GvcMixerCardProfile;

typedef struct
{
        char  *port;
        char  *human_port;
        char  *icon_name;
        guint  priority;
        gint   available;
        gint   direction;
        GList *profiles;
} GvcMixerCardPort;

GType                 gvc_mixer_card_get_type          (void);

guint                 gvc_mixer_card_get_id            (GvcMixerCard *card);
guint                 gvc_mixer_card_get_index         (GvcMixerCard *card);
const char *          gvc_mixer_card_get_name          (GvcMixerCard *card);
const char *          gvc_mixer_card_get_icon_name     (GvcMixerCard *card);
GvcMixerCardProfile * gvc_mixer_card_get_profile       (GvcMixerCard *card);
const GList *         gvc_mixer_card_get_profiles      (GvcMixerCard *card);
const GList *         gvc_mixer_card_get_ports         (GvcMixerCard *card);
gboolean              gvc_mixer_card_change_profile    (GvcMixerCard *card,
                                                        const char *profile);
GIcon *               gvc_mixer_card_get_gicon         (GvcMixerCard *card);

int                   gvc_mixer_card_profile_compare   (GvcMixerCardProfile *a,
                                                        GvcMixerCardProfile *b);

/* private */
gboolean              gvc_mixer_card_set_name          (GvcMixerCard *card,
                                                        const char   *name);
gboolean              gvc_mixer_card_set_icon_name     (GvcMixerCard *card,
                                                        const char   *name);
gboolean              gvc_mixer_card_set_profile       (GvcMixerCard *card,
                                                        const char   *profile);
gboolean              gvc_mixer_card_set_profiles      (GvcMixerCard *card,
                                                        GList        *profiles);
gboolean              gvc_mixer_card_set_ports         (GvcMixerCard *stream,
                                                        GList        *ports);

G_END_DECLS

#endif /* __GVC_MIXER_CARD_H */
0707010000000D000081A400000000000000000000000166437C94000004A4000000000000000000000000000000000000004000000000libgnome-volume-control-0.gitmodule/gvc-mixer-control-private.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_CONTROL_PRIVATE_H
#define __GVC_MIXER_CONTROL_PRIVATE_H

#include <glib-object.h>
#include <pulse/pulseaudio.h>
#include "gvc-mixer-stream.h"
#include "gvc-mixer-card.h"

G_BEGIN_DECLS

pa_context *        gvc_mixer_control_get_pa_context      (GvcMixerControl *control);

G_END_DECLS

#endif /* __GVC_MIXER_CONTROL_PRIVATE_H */
0707010000000E000081A400000000000000000000000166437C9400023BF2000000000000000000000000000000000000003800000000libgnome-volume-control-0.gitmodule/gvc-mixer-control.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2006-2008 Lennart Poettering
 * Copyright (C) 2008 Sjoerd Simons <sjoerd@luon.net>
 * Copyright (C) 2008 William Jon McCann
 * Copyright (C) 2012 Conor Curran
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
#include <pulse/ext-stream-restore.h>

#ifdef HAVE_ALSA
#include <alsa/asoundlib.h>
#endif /* HAVE_ALSA */

#include "gvc-mixer-control.h"
#include "gvc-mixer-sink.h"
#include "gvc-mixer-source.h"
#include "gvc-mixer-sink-input.h"
#include "gvc-mixer-source-output.h"
#include "gvc-mixer-event-role.h"
#include "gvc-mixer-card.h"
#include "gvc-mixer-card-private.h"
#include "gvc-channel-map-private.h"
#include "gvc-mixer-control-private.h"
#include "gvc-mixer-ui-device.h"

#define RECONNECT_DELAY 5

enum {
        PROP_0,
        PROP_NAME,
        N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };

struct GvcMixerControlPrivate
{
        pa_glib_mainloop *pa_mainloop;
        pa_mainloop_api  *pa_api;
        pa_context       *pa_context;
        guint             server_protocol_version;
        int               n_outstanding;
        guint             reconnect_id;
        char             *name;

        gboolean          default_sink_is_set;
        guint             default_sink_id;
        char             *default_sink_name;
        gboolean          default_source_is_set;
        guint             default_source_id;
        char             *default_source_name;

        gboolean          event_sink_input_is_set;
        guint             event_sink_input_id;

        GHashTable       *all_streams;
        GHashTable       *sinks; /* fixed outputs */
        GHashTable       *sources; /* fixed inputs */
        GHashTable       *sink_inputs; /* routable output streams */
        GHashTable       *source_outputs; /* routable input streams */
        GHashTable       *clients;
        GHashTable       *cards;

        GvcMixerStream   *new_default_sink_stream; /* new default sink stream, used in gvc_mixer_control_set_default_sink () */
        GvcMixerStream   *new_default_source_stream; /* new default source stream, used in gvc_mixer_control_set_default_source () */

        GHashTable       *ui_outputs; /* UI visible outputs */
        GHashTable       *ui_inputs;  /* UI visible inputs */

        /* When we change profile on a device that is not the server default sink,
         * it will jump back to the default sink set by the server to prevent the
         * audio setup from being 'outputless'.
         *
         * All well and good but then when we get the new stream created for the
         * new profile how do we know that this is the intended default or selected
         * device the user wishes to use. */
        guint            profile_swapping_device_id;

#ifdef HAVE_ALSA
        int      headset_card;
        gboolean has_headsetmic;
        gboolean has_headphonemic;
        gboolean headset_plugged_in;
        char    *headphones_name;
        char    *headsetmic_name;
        char    *headphonemic_name;
        char    *internalspk_name;
        char    *internalmic_name;
#endif /* HAVE_ALSA */

        GvcMixerControlState state;
};

enum {
        STATE_CHANGED,
        STREAM_ADDED,
        STREAM_REMOVED,
        STREAM_CHANGED,
        CARD_ADDED,
        CARD_REMOVED,
        DEFAULT_SINK_CHANGED,
        DEFAULT_SOURCE_CHANGED,
        ACTIVE_OUTPUT_UPDATE,
        ACTIVE_INPUT_UPDATE,
        OUTPUT_ADDED,
        INPUT_ADDED,
        OUTPUT_REMOVED,
        INPUT_REMOVED,
        AUDIO_DEVICE_SELECTION_NEEDED,
        LAST_SIGNAL
};

static guint signals [LAST_SIGNAL] = { 0, };

static void     gvc_mixer_control_finalize   (GObject              *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)

pa_context *
gvc_mixer_control_get_pa_context (GvcMixerControl *control)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
        return control->priv->pa_context;
}

/**
 * gvc_mixer_control_get_event_sink_input:
 * @control:
 *
 * Returns: (transfer none):
 */
GvcMixerStream *
gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
{
        GvcMixerStream *stream;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        stream = g_hash_table_lookup (control->priv->all_streams,
                                      GUINT_TO_POINTER (control->priv->event_sink_input_id));

        return stream;
}

static void
gvc_mixer_control_stream_restore_cb (pa_context *c,
				     GvcMixerStream *new_stream,
                                     const pa_ext_stream_restore_info *info,
                                     GvcMixerControl *control)
{
        pa_operation *o;
        pa_ext_stream_restore_info new_info;

        if (new_stream == NULL)
                return;

        new_info.name = info->name;
        new_info.channel_map = info->channel_map;
        new_info.volume = info->volume;
        new_info.mute = info->mute;

        new_info.device = gvc_mixer_stream_get_name (new_stream);

        o = pa_ext_stream_restore_write (control->priv->pa_context,
                                         PA_UPDATE_REPLACE,
                                         &new_info, 1,
                                         TRUE, NULL, NULL);

        if (o == NULL) {
                g_warning ("pa_ext_stream_restore_write() failed: %s",
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
                return;
        }

        g_debug ("Changed default device for %s to %s", info->name, new_info.device);

        pa_operation_unref (o);
}

static void
gvc_mixer_control_stream_restore_sink_cb (pa_context *c,
                                          const pa_ext_stream_restore_info *info,
                                          int eol,
                                          void *userdata)
{
        GvcMixerControl *control = (GvcMixerControl *) userdata;
        if (eol || info == NULL || !g_str_has_prefix(info->name, "sink-input-by"))
                return;
        gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_sink_stream, info, control);
}

static void
gvc_mixer_control_stream_restore_source_cb (pa_context *c,
                                            const pa_ext_stream_restore_info *info,
                                            int eol,
                                            void *userdata)
{
        GvcMixerControl *control = (GvcMixerControl *) userdata;
        if (eol || info == NULL || !g_str_has_prefix(info->name, "source-output-by"))
                return;
        gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_source_stream, info, control);
}

/**
 * gvc_mixer_control_lookup_device_from_stream:
 * @control:
 * @stream:
 *
 * Returns: (transfer none): a #GvcUIDevice or %NULL
 */
GvcMixerUIDevice *
gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control,
                                             GvcMixerStream *stream)
{
        GList                   *devices, *d;
        gboolean                 is_network_stream;
        const GList             *ports;
        GvcMixerUIDevice        *ret;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);

        if (GVC_IS_MIXER_SOURCE (stream))
               devices = g_hash_table_get_values (control->priv->ui_inputs);
        else
               devices = g_hash_table_get_values (control->priv->ui_outputs);

        ret = NULL;
        ports = gvc_mixer_stream_get_ports (stream);
        is_network_stream = (ports == NULL);

        for (d = devices; d != NULL; d = d->next) {
                GvcMixerUIDevice *device = d->data;
                guint stream_id = G_MAXUINT;

                g_object_get (G_OBJECT (device),
                             "stream-id", &stream_id,
                              NULL);

                if (is_network_stream &&
                    stream_id == gvc_mixer_stream_get_id (stream)) {
                        g_debug ("lookup device from stream - %s - it is a network_stream ",
                                 gvc_mixer_ui_device_get_description (device));
                        ret = device;
                        break;
                } else if (!is_network_stream) {
                        const GvcMixerStreamPort *port;
                        port = gvc_mixer_stream_get_port (stream);

                        if (stream_id == gvc_mixer_stream_get_id (stream) &&
                            g_strcmp0 (gvc_mixer_ui_device_get_port (device),
                                       port->port) == 0) {
                                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'",
                                         gvc_mixer_ui_device_get_description (device),
                                         gvc_mixer_ui_device_get_port (device),
                                         stream_id,
                                         port->port,
                                         gvc_mixer_stream_get_id (stream),
                                         gvc_mixer_stream_get_description (stream));
                                ret = device;
                                break;
                        }
                }
        }

        g_debug ("gvc_mixer_control_lookup_device_from_stream - Could not find a device for stream '%s'",gvc_mixer_stream_get_description (stream));

        g_list_free (devices);

        return ret;
}

gboolean
gvc_mixer_control_set_default_sink (GvcMixerControl *control,
                                    GvcMixerStream  *stream)
{
        pa_operation *o;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_debug ("about to set default sink on server");
        o = pa_context_set_default_sink (control->priv->pa_context,
                                         gvc_mixer_stream_get_name (stream),
                                         NULL,
                                         NULL);
        if (o == NULL) {
                g_warning ("pa_context_set_default_sink() failed: %s",
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
                return FALSE;
        }

        pa_operation_unref (o);

        control->priv->new_default_sink_stream = stream;
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_sink_stream);

        o = pa_ext_stream_restore_read (control->priv->pa_context,
                                        gvc_mixer_control_stream_restore_sink_cb,
                                        control);

        if (o == NULL) {
                g_warning ("pa_ext_stream_restore_read() failed: %s",
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
                return FALSE;
        }

        pa_operation_unref (o);

        return TRUE;
}

gboolean
gvc_mixer_control_set_default_source (GvcMixerControl *control,
                                      GvcMixerStream  *stream)
{
        GvcMixerUIDevice* input;
        pa_operation *o;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        o = pa_context_set_default_source (control->priv->pa_context,
                                           gvc_mixer_stream_get_name (stream),
                                           NULL,
                                           NULL);
        if (o == NULL) {
                g_warning ("pa_context_set_default_source() failed");
                return FALSE;
        }

        pa_operation_unref (o);

        control->priv->new_default_source_stream = stream;
        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_source_stream);

        o = pa_ext_stream_restore_read (control->priv->pa_context,
                                        gvc_mixer_control_stream_restore_source_cb,
                                        control);

        if (o == NULL) {
                g_warning ("pa_ext_stream_restore_read() failed: %s",
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
                return FALSE;
        }

        pa_operation_unref (o);

        /* source change successful, update the UI. */
        input = gvc_mixer_control_lookup_device_from_stream (control, stream);
        g_signal_emit (G_OBJECT (control),
                       signals[ACTIVE_INPUT_UPDATE],
                       0,
                       gvc_mixer_ui_device_get_id (input));

        return TRUE;
}

/**
 * gvc_mixer_control_get_default_sink:
 * @control:
 *
 * Returns: (transfer none):
 */
GvcMixerStream *
gvc_mixer_control_get_default_sink (GvcMixerControl *control)
{
        GvcMixerStream *stream;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        if (control->priv->default_sink_is_set) {
                stream = g_hash_table_lookup (control->priv->all_streams,
                                              GUINT_TO_POINTER (control->priv->default_sink_id));
        } else {
                stream = NULL;
        }

        return stream;
}

/**
 * gvc_mixer_control_get_default_source:
 * @control:
 *
 * Returns: (transfer none):
 */
GvcMixerStream *
gvc_mixer_control_get_default_source (GvcMixerControl *control)
{
        GvcMixerStream *stream;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        if (control->priv->default_source_is_set) {
                stream = g_hash_table_lookup (control->priv->all_streams,
                                              GUINT_TO_POINTER (control->priv->default_source_id));
        } else {
                stream = NULL;
        }

        return stream;
}

static gpointer
gvc_mixer_control_lookup_id (GHashTable *hash_table,
                             guint       id)
{
        return g_hash_table_lookup (hash_table,
                                    GUINT_TO_POINTER (id));
}

/**
 * gvc_mixer_control_lookup_stream_id:
 * @control:
 * @id:
 *
 * Returns: (transfer none):
 */
GvcMixerStream *
gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
                                    guint            id)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
}

/**
 * gvc_mixer_control_lookup_card_id:
 * @control:
 * @id:
 *
 * Returns: (transfer none):
 */
GvcMixerCard *
gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
                                  guint            id)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        return gvc_mixer_control_lookup_id (control->priv->cards, id);
}

/**
 * gvc_mixer_control_lookup_output_id:
 * @control:
 * @id:
 *
 * Returns: (transfer none):
 */
GvcMixerUIDevice *
gvc_mixer_control_lookup_output_id (GvcMixerControl *control,
                                    guint            id)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        return gvc_mixer_control_lookup_id (control->priv->ui_outputs, id);
}

/**
 * gvc_mixer_control_lookup_input_id:
 * @control:
 * @id:
 *
 * Returns: (transfer none):
 */
GvcMixerUIDevice *
gvc_mixer_control_lookup_input_id (GvcMixerControl *control,
                                    guint            id)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        return gvc_mixer_control_lookup_id (control->priv->ui_inputs, id);
}

/**
 * gvc_mixer_control_get_stream_from_device:
 * @control:
 * @device:
 *
 * Returns: (transfer none):
 */
GvcMixerStream *
gvc_mixer_control_get_stream_from_device (GvcMixerControl *control,
                                          GvcMixerUIDevice *device)
{
        gint stream_id;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        stream_id = gvc_mixer_ui_device_get_stream_id (device);

        if (stream_id == GVC_MIXER_UI_DEVICE_INVALID) {
                g_debug ("gvc_mixer_control_get_stream_from_device - device has a null stream");
                return NULL;
        }
        return gvc_mixer_control_lookup_stream_id (control, stream_id);
}

/**
 * gvc_mixer_control_change_profile_on_selected_device:
 * @control:
 * @device:
 * @profile: (allow-none): Can be %NULL if any profile present on this port is okay
 *
 * Returns: This method will attempt to swap the profile on the card of
 * the device with given profile name.  If successfull it will set the
 * preferred profile on that device so as we know the next time the user
 * moves to that device it should have this profile active.
 */
gboolean
gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl  *control,
                                                     GvcMixerUIDevice *device,
                                                     const gchar      *profile)
{
        const gchar         *best_profile;
        GvcMixerCardProfile *current_profile;
        GvcMixerCard        *card;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);

        g_object_get (G_OBJECT (device), "card", &card, NULL);
        current_profile = gvc_mixer_card_get_profile (card);

        if (current_profile)
                best_profile = gvc_mixer_ui_device_get_best_profile (device, profile, current_profile->profile);
        else
                best_profile = profile;

        g_assert (best_profile);

        g_debug ("Selected '%s', moving to profile '%s' on card '%s' on stream id %i",
                profile ? profile : "(any)", best_profile,
                gvc_mixer_card_get_name (card),
                gvc_mixer_ui_device_get_stream_id (device));

        g_debug ("default sink name = %s and default sink id %u",
                 control->priv->default_sink_name,
                 control->priv->default_sink_id);

        control->priv->profile_swapping_device_id = gvc_mixer_ui_device_get_id (device);

        if (gvc_mixer_card_change_profile (card, best_profile)) {
                gvc_mixer_ui_device_set_user_preferred_profile (device, best_profile);
                return TRUE;
        }
        return FALSE;
}

/**
 * gvc_mixer_control_change_output:
 * @control:
 * @output:
 * This method is called from the UI when the user selects a previously unselected device.
 * - Firstly it queries the stream from the device.
 *   - 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)
 *   In the scenario of a NULL stream on the device
 *        - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
 *        - It then caches this device in control->priv->cached_desired_output_id so that when the update_sink triggered
 *          from when we attempt to change profile we will know exactly what device to highlight on that stream.
 *        - It attempts to swap the profile on the card from that device and returns.
 * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
 * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
 *   it will attempt to change the port on that stream to be same as the device. If this fails it will return.
 * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active output device.
 */
void
gvc_mixer_control_change_output (GvcMixerControl *control,
                                 GvcMixerUIDevice* output)
{
        GvcMixerStream           *stream;
        GvcMixerStream           *default_stream;
        const GvcMixerStreamPort *active_port;
        const gchar              *output_port;

        g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (output));

        g_debug ("control change output");

        stream = gvc_mixer_control_get_stream_from_device (control, output);
        if (stream == NULL) {
                gvc_mixer_control_change_profile_on_selected_device (control,
                        output, NULL);
                return;
        }

        if (!gvc_mixer_ui_device_has_ports (output)) {
                g_debug ("Did we try to move to a software/bluetooth sink ?");
                if (gvc_mixer_control_set_default_sink (control, stream)) {
                        /* sink change was successful,  update the UI.*/
                        g_signal_emit (G_OBJECT (control),
                                       signals[ACTIVE_OUTPUT_UPDATE],
                                       0,
                                       gvc_mixer_ui_device_get_id (output));
                }
                else {
                        g_warning ("Failed to set default sink with stream from output %s",
                                   gvc_mixer_ui_device_get_description (output));
                }
                return;
        }

        active_port = gvc_mixer_stream_get_port (stream);
        output_port = gvc_mixer_ui_device_get_port (output);
        /* First ensure the correct port is active on the sink */
        if (g_strcmp0 (active_port->port, output_port) != 0) {
                g_debug ("Port change, switch to = %s", output_port);
                if (gvc_mixer_stream_change_port (stream, output_port) == FALSE) {
                        g_warning ("Could not change port !");
                        return;
                }
        }

        default_stream = gvc_mixer_control_get_default_sink (control);

        /* Finally if we are not on the correct stream, swap over. */
        if (stream != default_stream) {
                GvcMixerUIDevice* device;

                g_debug ("Attempting to swap over to stream %s ",
                         gvc_mixer_stream_get_description (stream));
                if (gvc_mixer_control_set_default_sink (control, stream)) {
                        device = gvc_mixer_control_lookup_device_from_stream (control, stream);
                        g_signal_emit (G_OBJECT (control),
                                       signals[ACTIVE_OUTPUT_UPDATE],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));
                } else {
                        /* If the move failed for some reason reset the UI. */
                        device = gvc_mixer_control_lookup_device_from_stream (control, default_stream);
                        g_signal_emit (G_OBJECT (control),
                                       signals[ACTIVE_OUTPUT_UPDATE],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));
                }
        }
}


/**
 * gvc_mixer_control_change_input:
 * @control:
 * @input:
 * This method is called from the UI when the user selects a previously unselected device.
 * - Firstly it queries the stream from the device.
 *   - 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)
 *   In the scenario of a NULL stream on the device
 *        - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device.
 *        - It then caches this device in control->priv->cached_desired_input_id so that when the update_source triggered
 *          from when we attempt to change profile we will know exactly what device to highlight on that stream.
 *        - It attempts to swap the profile on the card from that device and returns.
 * - Next, it handles network or bluetooth streams that only require their stream to be made the default.
 * - Next it deals with port changes so if the stream's active port is not the same as the port on the device
 *   it will attempt to change the port on that stream to be same as the device. If this fails it will return.
 * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active input device.
 */
void
gvc_mixer_control_change_input (GvcMixerControl *control,
                                GvcMixerUIDevice* input)
{
        GvcMixerStream           *stream;
        GvcMixerStream           *default_stream;
        const GvcMixerStreamPort *active_port;
        const gchar              *input_port;

        g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (input));

        stream = gvc_mixer_control_get_stream_from_device (control, input);
        if (stream == NULL) {
                gvc_mixer_control_change_profile_on_selected_device (control,
                        input, NULL);
                return;
        }

        if (!gvc_mixer_ui_device_has_ports (input)) {
                g_debug ("Did we try to move to a software/bluetooth source ?");
                if (! gvc_mixer_control_set_default_source (control, stream)) {
                        g_warning ("Failed to set default source with stream from input %s",
                                   gvc_mixer_ui_device_get_description (input));
                }
                return;
        }

        active_port = gvc_mixer_stream_get_port (stream);
        input_port = gvc_mixer_ui_device_get_port (input);
        /* First ensure the correct port is active on the sink */
        if (g_strcmp0 (active_port->port, input_port) != 0) {
                g_debug ("Port change, switch to = %s", input_port);
                if (gvc_mixer_stream_change_port (stream, input_port) == FALSE) {
                        g_warning ("Could not change port!");
                        return;
                }
        }

        default_stream = gvc_mixer_control_get_default_source (control);

        /* Finally if we are not on the correct stream, swap over. */
        if (stream != default_stream) {
                g_debug ("change-input - attempting to swap over to stream %s",
                         gvc_mixer_stream_get_description (stream));
                gvc_mixer_control_set_default_source (control, stream);
        }
}


static void
listify_hash_values_hfunc (gpointer key,
                           gpointer value,
                           gpointer user_data)
{
        GSList **list = user_data;

        *list = g_slist_prepend (*list, value);
}

static int
gvc_name_collate (const char *namea,
                  const char *nameb)
{
        if (nameb == NULL && namea == NULL)
                return 0;
        if (nameb == NULL)
                return 1;
        if (namea == NULL)
                return -1;

        return g_utf8_collate (namea, nameb);
}

static int
gvc_card_collate (GvcMixerCard *a,
                  GvcMixerCard *b)
{
        const char *namea;
        const char *nameb;

        g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0);
        g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0);

        namea = gvc_mixer_card_get_name (a);
        nameb = gvc_mixer_card_get_name (b);

        return gvc_name_collate (namea, nameb);
}

/**
 * gvc_mixer_control_get_cards:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerCard):
 */
GSList *
gvc_mixer_control_get_cards (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->cards,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_card_collate);
}

static int
gvc_stream_collate (GvcMixerStream *a,
                    GvcMixerStream *b)
{
        const char *namea;
        const char *nameb;

        g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0);
        g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0);

        namea = gvc_mixer_stream_get_name (a);
        nameb = gvc_mixer_stream_get_name (b);

        return gvc_name_collate (namea, nameb);
}

/**
 * gvc_mixer_control_get_streams:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerStream):
 */
GSList *
gvc_mixer_control_get_streams (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->all_streams,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
}

/**
 * gvc_mixer_control_get_sinks:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerSink):
 */
GSList *
gvc_mixer_control_get_sinks (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->sinks,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
}

/**
 * gvc_mixer_control_get_sources:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerSource):
 */
GSList *
gvc_mixer_control_get_sources (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->sources,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
}

/**
 * gvc_mixer_control_get_sink_inputs:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerSinkInput):
 */
GSList *
gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->sink_inputs,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
}

/**
 * gvc_mixer_control_get_source_outputs:
 * @control:
 *
 * Returns: (transfer container) (element-type Gvc.MixerSourceOutput):
 */
GSList *
gvc_mixer_control_get_source_outputs (GvcMixerControl *control)
{
        GSList *retval;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);

        retval = NULL;
        g_hash_table_foreach (control->priv->source_outputs,
                              listify_hash_values_hfunc,
                              &retval);
        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
}

static void
dec_outstanding (GvcMixerControl *control)
{
        if (control->priv->n_outstanding <= 0) {
                return;
        }

        if (--control->priv->n_outstanding <= 0) {
                control->priv->state = GVC_STATE_READY;
                g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY);
        }
}

GvcMixerControlState
gvc_mixer_control_get_state (GvcMixerControl *control)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), GVC_STATE_CLOSED);

        return control->priv->state;
}

static void
on_default_source_port_notify (GObject        *object,
                               GParamSpec     *pspec,
                               GvcMixerControl *control)
{
        char             *port;
        GvcMixerUIDevice *input;

        g_object_get (object, "port", &port, NULL);
        input = gvc_mixer_control_lookup_device_from_stream (control,
                                                             GVC_MIXER_STREAM (object));

        g_debug ("on_default_source_port_notify - moved to port '%s' which SHOULD ?? correspond to output '%s'",
                 port,
                 gvc_mixer_ui_device_get_description (input));

        g_signal_emit (G_OBJECT (control),
                       signals[ACTIVE_INPUT_UPDATE],
                       0,
                       gvc_mixer_ui_device_get_id (input));

        g_free (port);
}


static void
_set_default_source (GvcMixerControl *control,
                     GvcMixerStream  *stream)
{
        guint new_id;

        if (stream == NULL) {
                if (!control->priv->default_source_is_set)
                        return;

                g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control),
                                                      on_default_source_port_notify,
                                                      control);

                control->priv->default_source_id = 0;
                control->priv->default_source_is_set = FALSE;
                g_signal_emit (control,
                               signals[DEFAULT_SOURCE_CHANGED],
                               0,
                               PA_INVALID_INDEX);
                return;
        }

        new_id = gvc_mixer_stream_get_id (stream);

        if (control->priv->default_source_id != new_id) {
                GvcMixerUIDevice *input;

                if (control->priv->default_source_is_set) {
                        g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control),
                                                              on_default_source_port_notify,
                                                              control);
                }

                g_signal_connect (stream,
                                  "notify::port",
                                  G_CALLBACK (on_default_source_port_notify),
                                  control);

                control->priv->default_source_id = new_id;
                control->priv->default_source_is_set = TRUE;
                g_signal_emit (control,
                               signals[DEFAULT_SOURCE_CHANGED],
                               0,
                               new_id);

                input = gvc_mixer_control_lookup_device_from_stream (control, stream);

                g_signal_emit (G_OBJECT (control),
                               signals[ACTIVE_INPUT_UPDATE],
                               0,
                               gvc_mixer_ui_device_get_id (input));
        }
}

static void
on_default_sink_port_notify (GObject        *object,
                             GParamSpec     *pspec,
                             GvcMixerControl *control)
{
        char             *port;
        GvcMixerUIDevice *output;

        g_object_get (object, "port", &port, NULL);

        output = gvc_mixer_control_lookup_device_from_stream (control,
                                                              GVC_MIXER_STREAM (object));
        if (output != NULL) {
                g_debug ("on_default_sink_port_notify - moved to port %s - which SHOULD correspond to output %s",
                         port,
                         gvc_mixer_ui_device_get_description (output));
                g_signal_emit (G_OBJECT (control),
                               signals[ACTIVE_OUTPUT_UPDATE],
                               0,
                               gvc_mixer_ui_device_get_id (output));
        }
        g_free (port);
}

static void
_set_default_sink (GvcMixerControl *control,
                   GvcMixerStream  *stream)
{
        guint new_id;

        if (stream == NULL) {
                /* Don't tell front-ends about an unset default
                 * sink if it's already unset */
                if (control->priv->default_sink_is_set == FALSE)
                        return;

                g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control),
                                                      on_default_sink_port_notify,
                                                      control);

                control->priv->default_sink_id = 0;
                control->priv->default_sink_is_set = FALSE;
                g_signal_emit (control,
                               signals[DEFAULT_SINK_CHANGED],
                               0,
                               PA_INVALID_INDEX);
                return;
        }

        new_id = gvc_mixer_stream_get_id (stream);

        if (control->priv->default_sink_id != new_id) {
                GvcMixerUIDevice *output;
                if (control->priv->default_sink_is_set) {
                        g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control),
                                                              on_default_sink_port_notify,
                                                              control);
                }

                control->priv->default_sink_id = new_id;

                control->priv->default_sink_is_set = TRUE;
                g_signal_emit (control,
                               signals[DEFAULT_SINK_CHANGED],
                               0,
                               new_id);

                g_signal_connect (stream,
                                  "notify::port",
                                  G_CALLBACK (on_default_sink_port_notify),
                                  control);

                output = gvc_mixer_control_lookup_device_from_stream (control, stream);

                g_debug ("active_sink change");

                g_signal_emit (G_OBJECT (control),
                               signals[ACTIVE_OUTPUT_UPDATE],
                               0,
                               gvc_mixer_ui_device_get_id (output));
        }
}

static gboolean
_stream_has_name (gpointer        key,
                  GvcMixerStream *stream,
                  const char     *name)
{
        const char *t_name;

        t_name = gvc_mixer_stream_get_name (stream);

        if (t_name != NULL
            && name != NULL
            && strcmp (t_name, name) == 0) {
                return TRUE;
        }

        return FALSE;
}

static GvcMixerStream *
find_stream_for_name (GvcMixerControl *control,
                      const char      *name)
{
        GvcMixerStream *stream;

        stream = g_hash_table_find (control->priv->all_streams,
                                    (GHRFunc)_stream_has_name,
                                    (char *)name);
        return stream;
}

static void
update_default_source_from_name (GvcMixerControl *control,
                                 const char      *name)
{
        gboolean changed = FALSE;

        if ((control->priv->default_source_name == NULL
             && name != NULL)
            || (control->priv->default_source_name != NULL
                && name == NULL)
            || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) {
                changed = TRUE;
        }

        if (changed) {
                GvcMixerStream *stream;

                g_free (control->priv->default_source_name);
                control->priv->default_source_name = g_strdup (name);

                stream = find_stream_for_name (control, name);
                _set_default_source (control, stream);
        }
}

static void
update_default_sink_from_name (GvcMixerControl *control,
                               const char      *name)
{
        gboolean changed = FALSE;

        if ((control->priv->default_sink_name == NULL
             && name != NULL)
            || (control->priv->default_sink_name != NULL
                && name == NULL)
            || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) {
                changed = TRUE;
        }

        if (changed) {
                GvcMixerStream *stream;
                g_free (control->priv->default_sink_name);
                control->priv->default_sink_name = g_strdup (name);

                stream = find_stream_for_name (control, name);
                _set_default_sink (control, stream);
        }
}

static void
update_server (GvcMixerControl      *control,
               const pa_server_info *info)
{
        if (info->default_source_name != NULL) {
                update_default_source_from_name (control, info->default_source_name);
        }
        if (info->default_sink_name != NULL) {
                g_debug ("update server");
                update_default_sink_from_name (control, info->default_sink_name);
        }
}

static void
remove_stream (GvcMixerControl *control,
               GvcMixerStream  *stream)
{
        guint id;

        g_object_ref (stream);

        id = gvc_mixer_stream_get_id (stream);

        if (id == control->priv->default_sink_id) {
                _set_default_sink (control, NULL);
        } else if (id == control->priv->default_source_id) {
                _set_default_source (control, NULL);
        }

        g_hash_table_remove (control->priv->all_streams,
                             GUINT_TO_POINTER (id));
        g_signal_emit (G_OBJECT (control),
                       signals[STREAM_REMOVED],
                       0,
                       gvc_mixer_stream_get_id (stream));
        g_object_unref (stream);
}

static void
add_stream (GvcMixerControl *control,
            GvcMixerStream  *stream)
{
        g_hash_table_insert (control->priv->all_streams,
                             GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
                             stream);
        g_signal_emit (G_OBJECT (control),
                       signals[STREAM_ADDED],
                       0,
                       gvc_mixer_stream_get_id (stream));
}

/* This method will match individual stream ports against its corresponding device
 * It does this by:
 * - 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
 *   and the port-name on the device is the same as the streamport-name.
 * This should always find a match and is used exclusively by sync_devices().
 */
static gboolean
match_stream_with_devices (GvcMixerControl    *control,
                           GvcMixerStreamPort *stream_port,
                           GvcMixerStream     *stream)
{
        GList                   *devices, *d;
        guint                    stream_card_id;
        guint                    stream_id;
        gboolean                 in_possession = FALSE;

        stream_id      =  gvc_mixer_stream_get_id (stream);
        stream_card_id =  gvc_mixer_stream_get_card_index (stream);

        devices  = g_hash_table_get_values (GVC_IS_MIXER_SOURCE (stream) ? control->priv->ui_inputs : control->priv->ui_outputs);

        for (d = devices; d != NULL; d = d->next) {
                GvcMixerUIDevice *device;
                guint             device_stream_id;
                gchar            *device_port_name;
                gchar            *origin;
                gchar            *description;
                GvcMixerCard     *card;
                guint             card_id;

                device = d->data;
                g_object_get (G_OBJECT (device),
                             "stream-id", &device_stream_id,
                             "card", &card,
                             "origin", &origin,
                             "description", &description,
                             "port-name", &device_port_name,
                              NULL);

                if (card == NULL) {
                        if (device_stream_id == stream_id) {
                                g_debug ("Matched stream %u with card-less device '%s', with stream already setup",
                                         stream_id, description);
                                in_possession = TRUE;
                        }
                } else {
                        card_id = gvc_mixer_card_get_index (card);

                        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",
                                 description,
                                 origin,
                                 device_port_name,
                                 card,
                                 stream_port->port,
                                 stream_card_id);

                        if (stream_card_id == card_id &&
                            g_strcmp0 (device_port_name, stream_port->port) == 0) {
                                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",
                                         description,
                                         origin,
                                         gvc_mixer_ui_device_get_id (device),
                                         stream_id);

                                g_object_set (G_OBJECT (device),
                                              "stream-id", stream_id,
                                              NULL);
                                in_possession = TRUE;
                        }
                }

                g_free (device_port_name);
                g_free (origin);
                g_free (description);

                if (in_possession == TRUE)
                        break;
        }

        g_list_free (devices);
        return in_possession;
}

/*
 * This method attempts to match a sink or source with its relevant UI device.
 * GvcMixerStream can represent both a sink or source.
 * Using static card port introspection implies that we know beforehand what
 * outputs and inputs are available to the user.
 * But that does not mean that all of these inputs and outputs are available to be used.
 * For instance we might be able to see that there is a HDMI port available but if
 * we are on the default analog stereo output profile there is no valid sink for
 * that HDMI device. We first need to change profile and when update_sink() is called
 * only then can we match the new hdmi sink with its corresponding device.
 *
 * Firstly it checks to see if the incoming stream has no ports.
 * - If a stream has no ports and no valid card id, it goes ahead and makes a new
 *   device (software/network devices are only detectable at the sink/source level)
 * If the stream has ports it will match each port against the stream using match_stream_with_devices().
 *
 * This method should always find a match.
 */
static void
sync_devices (GvcMixerControl *control,
              GvcMixerStream* stream)
{
        /* Go through ports to see what outputs can be created. */
        const GList *stream_ports;
        const GList *n = NULL;
        gboolean     is_output = !GVC_IS_MIXER_SOURCE (stream);

        stream_ports = gvc_mixer_stream_get_ports (stream);

        if (stream_ports == NULL) {
                GvcMixerUIDevice *device;
                GObject *object;

                object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
                                       "stream-id", gvc_mixer_stream_get_id (stream),
                                       "description", gvc_mixer_stream_get_description (stream),
                                       "origin", "", /* Leave it empty for these special cases */
                                       "port-name", NULL,
                                       "port-available", TRUE,
                                       "icon-name", gvc_mixer_stream_get_icon_name (stream),
                                        NULL);
                device = GVC_MIXER_UI_DEVICE (object);

                g_hash_table_insert (is_output ? control->priv->ui_outputs : control->priv->ui_inputs,
                                     GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)),
                                     g_object_ref (device));

                g_signal_emit (G_OBJECT (control),
                               signals[is_output ? OUTPUT_ADDED : INPUT_ADDED],
                               0,
                               gvc_mixer_ui_device_get_id (device));

                return;
        }

        /* Go ahead and make sure to match each port against a previously created device */
        for (n = stream_ports; n != NULL; n = n->next) {

                GvcMixerStreamPort *stream_port;
                stream_port = n->data;

                if (match_stream_with_devices (control, stream_port, stream))
                        continue;

                g_warning ("Sync_devices: Failed to match stream id: %u, description: '%s', origin: '%s'",
                           gvc_mixer_stream_get_id (stream),
                           stream_port->human_port,
                           gvc_mixer_stream_get_description (stream));
        }
}

static void
set_icon_name_from_proplist (GvcMixerStream *stream,
                             pa_proplist    *l,
                             const char     *default_icon_name)
{
        const char *t;

        if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) {
                goto finish;
        }

        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
                goto finish;
        }

        if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
                goto finish;
        }

        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
                goto finish;
        }

        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {

                if (strcmp (t, "video") == 0 ||
                    strcmp (t, "phone") == 0) {
                        goto finish;
                }

                if (strcmp (t, "music") == 0) {
                        t = "audio";
                        goto finish;
                }

                if (strcmp (t, "game") == 0) {
                        t = "applications-games";
                        goto finish;
                }

                if (strcmp (t, "event") == 0) {
                        t = "dialog-information";
                        goto finish;
                }
        }

        t = default_icon_name;

 finish:
        gvc_mixer_stream_set_icon_name (stream, t);
}

static GvcMixerStreamState
translate_pa_state (pa_sink_state_t state) {
        switch (state) {
        case PA_SINK_RUNNING:
                return GVC_STREAM_STATE_RUNNING;
        case PA_SINK_IDLE:
                return GVC_STREAM_STATE_IDLE;
        case PA_SINK_SUSPENDED:
                return GVC_STREAM_STATE_SUSPENDED;
        case PA_SINK_INIT:
        case PA_SINK_INVALID_STATE:
        case PA_SINK_UNLINKED:
        default:
                return GVC_STREAM_STATE_INVALID;
        }
}

/*
 * Called when anything changes with a sink.
 */
static void
update_sink (GvcMixerControl    *control,
             const pa_sink_info *info)
{
        GvcMixerStream  *stream;
        gboolean        is_new;
        pa_volume_t     max_volume;
        GvcChannelMap   *map;
        char            map_buff[PA_CHANNEL_MAP_SNPRINT_MAX];

        pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map);
#if 1
        g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'",
                 info->index,
                 info->name,
                 info->description,
                 map_buff);
#endif

        map = NULL;
        is_new = FALSE;
        stream = g_hash_table_lookup (control->priv->sinks,
                                      GUINT_TO_POINTER (info->index));

        if (stream == NULL) {
                GList *list = NULL;
                guint i;

                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
                stream = gvc_mixer_sink_new (control->priv->pa_context,
                                             info->index,
                                             map);

                for (i = 0; i < info->n_ports; i++) {
                        GvcMixerStreamPort *port;

                        port = g_slice_new0 (GvcMixerStreamPort);
                        port->port = g_strdup (info->ports[i]->name);
                        port->human_port = g_strdup (info->ports[i]->description);
                        port->priority = info->ports[i]->priority;
                        port->available = info->ports[i]->available != PA_PORT_AVAILABLE_NO;

                        list = g_list_prepend (list, port);
                }
                gvc_mixer_stream_set_ports (stream, list);

                g_object_unref (map);
                is_new = TRUE;

        } else if (gvc_mixer_stream_is_running (stream)) {
                /* Ignore events if volume changes are outstanding */
                g_debug ("Ignoring event, volume changes are outstanding");
                return;
        }

        max_volume = pa_cvolume_max (&info->volume);
        gvc_mixer_stream_set_name (stream, info->name);
        gvc_mixer_stream_set_card_index (stream, info->card);
        gvc_mixer_stream_set_description (stream, info->description);
        set_icon_name_from_proplist (stream, info->proplist, "audio-card");
        gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
        gvc_mixer_stream_set_sysfs_path (stream, pa_proplist_gets (info->proplist, "sysfs.path"));
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
        gvc_mixer_stream_set_is_muted (stream, info->mute);
        gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME));
        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
        gvc_mixer_stream_set_state (stream, translate_pa_state (info->state));

        /* Messy I know but to set the port everytime regardless of whether it has changed will cost us a
         * port change notify signal which causes the frontend to resync.
         * Only update the UI when something has changed. */
        if (info->active_port != NULL) {
                if (is_new)
                        gvc_mixer_stream_set_port (stream, info->active_port->name);
                else {
                        const GvcMixerStreamPort *active_port;
                        active_port = gvc_mixer_stream_get_port (stream);
                        if (active_port == NULL ||
                            g_strcmp0 (active_port->port, info->active_port->name) != 0) {
                                g_debug ("update sink - apparently a port update");
                                gvc_mixer_stream_set_port (stream, info->active_port->name);
                        }
                }
        }

        if (is_new) {
                g_debug ("update sink - is new");

                g_hash_table_insert (control->priv->sinks,
                                     GUINT_TO_POINTER (info->index),
                                     g_object_ref (stream));
                add_stream (control, stream);
                /* Always sink on a new stream to able to assign the right stream id
                 * to the appropriate outputs (multiple potential outputs per stream). */
                sync_devices (control, stream);
        } else {
                g_signal_emit (G_OBJECT (control),
                               signals[STREAM_CHANGED],
                               0,
                               gvc_mixer_stream_get_id (stream));
        }

        /*
         * When we change profile on a device that is not the server default sink,
         * it will jump back to the default sink set by the server to prevent the audio setup from being 'outputless'.
         * All well and good but then when we get the new stream created for the new profile how do we know
         * that this is the intended default or selected device the user wishes to use.
         * This is messy but it's the only reliable way that it can be done without ripping the whole thing apart.
         */
        if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
                GvcMixerUIDevice *dev = NULL;
                dev = gvc_mixer_control_lookup_output_id (control, control->priv->profile_swapping_device_id);
                if (dev != NULL) {
                        /* now check to make sure this new stream is the same stream just matched and set on the device object */
                        if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
                                g_debug ("Looks like we profile swapped on a non server default sink");
                                gvc_mixer_control_set_default_sink (control, stream);
                                control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
                        }
                }
        }

        if (control->priv->default_sink_name != NULL
            && info->name != NULL
            && strcmp (control->priv->default_sink_name, info->name) == 0) {
                _set_default_sink (control, stream);
        }

        if (map == NULL)
                map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream);

        gvc_channel_map_volume_changed (map, &info->volume, FALSE);
}

static void
update_source (GvcMixerControl      *control,
               const pa_source_info *info)
{
        GvcMixerStream *stream;
        gboolean        is_new;
        pa_volume_t     max_volume;

#if 1
        g_debug ("Updating source: index=%u name='%s' description='%s'",
                 info->index,
                 info->name,
                 info->description);
#endif

        /* completely ignore monitors, they're not real sources */
        if (info->monitor_of_sink != PA_INVALID_INDEX) {
                return;
        }

        is_new = FALSE;

        stream = g_hash_table_lookup (control->priv->sources,
                                      GUINT_TO_POINTER (info->index));
        if (stream == NULL) {
                GList *list = NULL;
                guint i;
                GvcChannelMap *map;

                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
                stream = gvc_mixer_source_new (control->priv->pa_context,
                                               info->index,
                                               map);

                for (i = 0; i < info->n_ports; i++) {
                        GvcMixerStreamPort *port;

                        port = g_slice_new0 (GvcMixerStreamPort);
                        port->port = g_strdup (info->ports[i]->name);
                        port->human_port = g_strdup (info->ports[i]->description);
                        port->priority = info->ports[i]->priority;
                        list = g_list_prepend (list, port);
                }
                gvc_mixer_stream_set_ports (stream, list);

                g_object_unref (map);
                is_new = TRUE;
        } else if (gvc_mixer_stream_is_running (stream)) {
                /* Ignore events if volume changes are outstanding */
                g_debug ("Ignoring event, volume changes are outstanding");
                return;
        }

        max_volume = pa_cvolume_max (&info->volume);

        gvc_mixer_stream_set_name (stream, info->name);
        gvc_mixer_stream_set_card_index (stream, info->card);
        gvc_mixer_stream_set_description (stream, info->description);
        set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");
        gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR));
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
        gvc_mixer_stream_set_is_muted (stream, info->mute);
        gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME));
        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
        g_debug ("update source");

        if (info->active_port != NULL) {
                if (is_new)
                        gvc_mixer_stream_set_port (stream, info->active_port->name);
                else {
                        const GvcMixerStreamPort *active_port;
                        active_port = gvc_mixer_stream_get_port (stream);
                        if (active_port == NULL ||
                            g_strcmp0 (active_port->port, info->active_port->name) != 0) {
                                g_debug ("update source - apparently a port update");
                                gvc_mixer_stream_set_port (stream, info->active_port->name);
                        }
                }
        }

        if (is_new) {
                g_hash_table_insert (control->priv->sources,
                                     GUINT_TO_POINTER (info->index),
                                     g_object_ref (stream));
                add_stream (control, stream);
                sync_devices (control, stream);
        } else {
                g_signal_emit (G_OBJECT (control),
                               signals[STREAM_CHANGED],
                               0,
                               gvc_mixer_stream_get_id (stream));
        }

        if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) {
                GvcMixerUIDevice *dev = NULL;

                dev = gvc_mixer_control_lookup_input_id (control, control->priv->profile_swapping_device_id);

                if (dev != NULL) {
                        /* now check to make sure this new stream is the same stream just matched and set on the device object */
                        if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) {
                                g_debug ("Looks like we profile swapped on a non server default source");
                                gvc_mixer_control_set_default_source (control, stream);
                                control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;
                        }
                }
        }
        if (control->priv->default_source_name != NULL
            && info->name != NULL
            && strcmp (control->priv->default_source_name, info->name) == 0) {
                _set_default_source (control, stream);
        }
}

static void
set_is_event_stream_from_proplist (GvcMixerStream *stream,
                                   pa_proplist    *l)
{
        const char *t;
        gboolean is_event_stream;

        is_event_stream = FALSE;

        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
                if (g_str_equal (t, "event"))
                        is_event_stream = TRUE;
        }

        gvc_mixer_stream_set_is_event_stream (stream, is_event_stream);
}

static void
set_application_id_from_proplist (GvcMixerStream *stream,
                                  pa_proplist    *l)
{
        const char *t;

        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) {
                gvc_mixer_stream_set_application_id (stream, t);
        }
}

static void
update_sink_input (GvcMixerControl          *control,
                   const pa_sink_input_info *info)
{
        GvcMixerStream *stream;
        gboolean        is_new;
        pa_volume_t     max_volume;
        const char     *name;

#if 0
        g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u",
                 info->index,
                 info->name,
                 info->client,
                 info->sink);
#endif

        is_new = FALSE;

        stream = g_hash_table_lookup (control->priv->sink_inputs,
                                      GUINT_TO_POINTER (info->index));
        if (stream == NULL) {
                GvcChannelMap *map;
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
                stream = gvc_mixer_sink_input_new (control->priv->pa_context,
                                                   info->index,
                                                   map);
                g_object_unref (map);
                is_new = TRUE;
        } else if (gvc_mixer_stream_is_running (stream)) {
                /* Ignore events if volume changes are outstanding */
                g_debug ("Ignoring event, volume changes are outstanding");
                return;
        }

        max_volume = pa_cvolume_max (&info->volume);

        name = (const char *)g_hash_table_lookup (control->priv->clients,
                                                  GUINT_TO_POINTER (info->client));
        gvc_mixer_stream_set_name (stream, name);
        gvc_mixer_stream_set_description (stream, info->name);

        set_application_id_from_proplist (stream, info->proplist);
        set_is_event_stream_from_proplist (stream, info->proplist);
        set_icon_name_from_proplist (stream, info->proplist, "application-x-executable");
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
        gvc_mixer_stream_set_is_muted (stream, info->mute);
        gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX);

        if (is_new) {
                g_hash_table_insert (control->priv->sink_inputs,
                                     GUINT_TO_POINTER (info->index),
                                     g_object_ref (stream));
                add_stream (control, stream);
        } else {
                g_signal_emit (G_OBJECT (control),
                               signals[STREAM_CHANGED],
                               0,
                               gvc_mixer_stream_get_id (stream));
        }
}

static void
update_source_output (GvcMixerControl             *control,
                      const pa_source_output_info *info)
{
        GvcMixerStream *stream;
        gboolean        is_new;
        pa_volume_t     max_volume;
        const char     *name;

#if 1
        g_debug ("Updating source output: index=%u name='%s' client=%u source=%u",
                 info->index,
                 info->name,
                 info->client,
                 info->source);
#endif

        is_new = FALSE;
        stream = g_hash_table_lookup (control->priv->source_outputs,
                                      GUINT_TO_POINTER (info->index));
        if (stream == NULL) {
                GvcChannelMap *map;
                map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
                stream = gvc_mixer_source_output_new (control->priv->pa_context,
                                                      info->index,
                                                      map);
                g_object_unref (map);
                is_new = TRUE;
        }

        name = (const char *)g_hash_table_lookup (control->priv->clients,
                                                  GUINT_TO_POINTER (info->client));

        max_volume = pa_cvolume_max (&info->volume);

        gvc_mixer_stream_set_name (stream, name);
        gvc_mixer_stream_set_description (stream, info->name);
        set_application_id_from_proplist (stream, info->proplist);
        set_is_event_stream_from_proplist (stream, info->proplist);
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
        gvc_mixer_stream_set_is_muted (stream, info->mute);
        set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone");

        if (is_new) {
                g_hash_table_insert (control->priv->source_outputs,
                                     GUINT_TO_POINTER (info->index),
                                     g_object_ref (stream));
                add_stream (control, stream);
        } else {
                g_signal_emit (G_OBJECT (control),
                               signals[STREAM_CHANGED],
                               0,
                               gvc_mixer_stream_get_id (stream));
        }
}

static void
update_client (GvcMixerControl      *control,
               const pa_client_info *info)
{
#if 1
        g_debug ("Updating client: index=%u name='%s'",
                 info->index,
                 info->name);
#endif
        g_hash_table_insert (control->priv->clients,
                             GUINT_TO_POINTER (info->index),
                             g_strdup (info->name));
}

static char *
card_num_streams_to_status (guint sinks,
                            guint sources)
{
        char *sinks_str;
        char *sources_str;
        char *ret;

        if (sinks == 0 && sources == 0) {
                /* translators:
                 * The device has been disabled */
                return g_strdup (_("Disabled"));
        }
        if (sinks == 0) {
                sinks_str = NULL;
        } else {
                /* translators:
                 * The number of sound outputs on a particular device */
                sinks_str = g_strdup_printf (ngettext ("%u Output",
                                                       "%u Outputs",
                                                       sinks),
                                             sinks);
        }
        if (sources == 0) {
                sources_str = NULL;
        } else {
                /* translators:
                 * The number of sound inputs on a particular device */
                sources_str = g_strdup_printf (ngettext ("%u Input",
                                                         "%u Inputs",
                                                         sources),
                                               sources);
        }
        if (sources_str == NULL)
                return sinks_str;
        if (sinks_str == NULL)
                return sources_str;
        ret = g_strdup_printf ("%s / %s", sinks_str, sources_str);
        g_free (sinks_str);
        g_free (sources_str);
        return ret;
}

/*
 * A utility method to gather which card profiles are relevant to the port .
 */
static GList *
determine_profiles_for_port (pa_card_port_info *port,
                             const GList       *card_profiles)
{
        guint i;
        GList *supported_profiles = NULL;
        const GList *p;
        for (i = 0; i < port->n_profiles; i++) {
                for (p = card_profiles; p != NULL; p = p->next) {
                        GvcMixerCardProfile *prof;
                        prof = p->data;
                        if (g_strcmp0 (port->profiles[i]->name, prof->profile) == 0)
                                supported_profiles = g_list_append (supported_profiles, prof);
                }
        }
        g_debug ("%i profiles supported on port %s",
                 g_list_length (supported_profiles),
                 port->description);
        return g_list_sort (supported_profiles, (GCompareFunc) gvc_mixer_card_profile_compare);
}

static gboolean
is_card_port_an_output (GvcMixerCardPort* port)
{
        return port->direction == PA_DIRECTION_OUTPUT ? TRUE : FALSE;
}

/*
 * This method will create a ui device for the given port.
 */
static void
update_ui_device_on_port_added (GvcMixerControl  *control,
                                GvcMixerCardPort *port,
                                GvcMixerCard     *card)
{
        GvcMixerUIDeviceDirection  direction;
        GObject                   *object;
        GvcMixerUIDevice          *uidevice;
        gboolean                   available = port->available != PA_PORT_AVAILABLE_NO;

        direction = (is_card_port_an_output (port) == TRUE) ? UIDeviceOutput : UIDeviceInput;

        object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE,
                               "type", (guint)direction,
                               "card", card,
                               "port-name", port->port,
                               "description", port->human_port,
                               "origin", gvc_mixer_card_get_name (card),
                               "port-available", available,
                               "icon-name", port->icon_name,
                               NULL);

        uidevice = GVC_MIXER_UI_DEVICE (object);
        gvc_mixer_ui_device_set_profiles (uidevice, port->profiles);

        g_hash_table_insert (is_card_port_an_output (port) ? control->priv->ui_outputs : control->priv->ui_inputs,
                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (uidevice)),
                             uidevice);


        if (available) {
                g_signal_emit (G_OBJECT (control),
                               signals[is_card_port_an_output (port) ? OUTPUT_ADDED : INPUT_ADDED],
                               0,
                               gvc_mixer_ui_device_get_id (uidevice));
        }

        g_debug ("update_ui_device_on_port_added, direction %u, description '%s', origin '%s', port available %i", 
                 direction,
                 port->human_port,
                 gvc_mixer_card_get_name (card),
                 available);
}

static void
update_ui_device_on_port_changed (GvcMixerControl   *control,
                                  GvcMixerCardPort  *card_port,
                                  pa_card_port_info *new_port_info,
                                  GvcMixerCard      *card)
{
        GList                   *d;
        GList                   *devices;
        GvcMixerUIDevice        *device;
        gboolean                 is_output = is_card_port_an_output (card_port);

        devices  = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);

        for (d = devices; d != NULL; d = d->next) {
                GvcMixerCard *device_card;
                gchar        *device_port_name;

                device = d->data;
                g_object_get (G_OBJECT (device),
                             "card", &device_card,
                             "port-name", &device_port_name,
                              NULL);

                if (g_strcmp0 (card_port->port, device_port_name) == 0 &&
                        device_card == card) {
                        gboolean was_available;
                        gboolean is_available;
                        const GList *card_profiles = gvc_mixer_card_get_profiles (card);

                        was_available = card_port->available != PA_PORT_AVAILABLE_NO;
                        is_available = new_port_info->available != PA_PORT_AVAILABLE_NO;

                        g_debug ("Found the relevant device %s, update its port availability flag to %i, is_output %i",
                                 device_port_name,
                                 is_available,
                                 is_output);

                        card_port->available = new_port_info->available;

                        g_list_free (card_port->profiles);
                        card_port->profiles = determine_profiles_for_port (new_port_info, card_profiles);

                        gvc_mixer_ui_device_set_profiles (device, card_port->profiles);

                        if (is_available != was_available) {
                                g_object_set (G_OBJECT (device),
                                              "port-available", is_available, NULL);
                                g_signal_emit (G_OBJECT (control),
                                               is_output ? signals[is_available ? OUTPUT_ADDED : OUTPUT_REMOVED]
                                                         : signals[is_available ? INPUT_ADDED : INPUT_REMOVED],
                                               0,
                                               gvc_mixer_ui_device_get_id (device));
                        }
               }
               g_free (device_port_name);
        }

        g_list_free (devices);
}

static void
maybe_remove_ui_device (GvcMixerControl  *control,
                        GvcMixerUIDevice *device)
{
        /* We add UIDevices for ports or for streams, so remove them if the device now
         * has neither.
         */
        if (gvc_mixer_ui_device_get_stream_id (device) == GVC_MIXER_UI_DEVICE_INVALID &&
            !gvc_mixer_ui_device_has_ports (device)) {
                gboolean is_output = gvc_mixer_ui_device_is_output (device);

                g_debug ("Removing UIDevice %s",
                         gvc_mixer_ui_device_get_description (device));

                g_hash_table_remove (is_output ? control->priv->ui_outputs : control->priv->ui_inputs,
                                     GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)));
        }
}

static void
update_ui_device_on_port_removed (GvcMixerControl  *control,
                                  GvcMixerCardPort *card_port,
                                  GvcMixerCard     *card)
{
        GList *d;
        GList *devices;
        gboolean is_output = is_card_port_an_output (card_port);

        devices  = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs);
        for (d = devices; d != NULL; d = d->next) {
                GvcMixerUIDevice *device = d->data;
                GvcMixerCard *device_card;
                gchar *device_port_name;

                g_object_get (G_OBJECT (device),
                             "card", &device_card,
                             "port-name", &device_port_name,
                              NULL);

                if (g_strcmp0 (card_port->port, device_port_name) == 0 && device_card == card) {
                        g_object_set (G_OBJECT (device),
                                      "card", NULL,
                                      "port-name", NULL,
                                       NULL);

                        g_signal_emit (G_OBJECT (control),
                                       signals[is_output ? OUTPUT_REMOVED : INPUT_REMOVED],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));

                        maybe_remove_ui_device (control, device);
                }

               g_free (device_port_name);
        }

        g_list_free (devices);
}

#ifdef HAVE_ALSA
typedef struct {
        char *port_name_to_set;
        guint32 headset_card;
} PortStatusData;

static void
port_status_data_free (PortStatusData *data)
{
        if (data == NULL)
                return;
        g_free (data->port_name_to_set);
        g_free (data);
}

/*
 We need to re-enumerate sources and sinks every time the user makes a choice,
 because they can change due to use interaction in other software (or policy
 changes inside PulseAudio). Enumeration means PulseAudio will do a series of
 callbacks, one for every source/sink.
 Set the port when we find the correct source/sink.
 */

static void
sink_info_cb (pa_context         *c,
              const pa_sink_info *i,
              int                 eol,
              void               *userdata)
{
        PortStatusData *data = userdata;
        pa_operation *o;
        guint j;
        const char *s;

        if (eol != 0) {
                port_status_data_free (data);
                return;
        }

        if (i->card != data->headset_card)
                return;

        s = data->port_name_to_set;

        if (i->active_port &&
            strcmp (i->active_port->name, s) == 0)
                return;

        for (j = 0; j < i->n_ports; j++)
                if (strcmp (i->ports[j]->name, s) == 0)
                        break;

        if (j >= i->n_ports)
                return;

        o = pa_context_set_sink_port_by_index (c, i->index, s, NULL, NULL);
        g_clear_pointer (&o, pa_operation_unref);
}

static void
source_info_cb (pa_context           *c,
                const pa_source_info *i,
                int                   eol,
                void                 *userdata)
{
        PortStatusData *data = userdata;
        pa_operation *o;
        guint j;
        const char *s;

        if (eol != 0) {
                port_status_data_free (data);
                return;
        }

        if (i->card != data->headset_card)
                return;

        s = data->port_name_to_set;

        for (j = 0; j < i->n_ports; j++) {
                if (g_str_equal (i->ports[j]->name, s)) {
                        o = pa_context_set_default_source (c,
                                                           i->name,
                                                           NULL,
                                                           NULL);
                        if (o == NULL) {
                                g_warning ("pa_context_set_default_source() failed");
                                return;
                        }
                }
	}

        if (i->active_port && strcmp (i->active_port->name, s) == 0)
                return;

        for (j = 0; j < i->n_ports; j++)
                if (strcmp (i->ports[j]->name, s) == 0)
                        break;

        if (j >= i->n_ports)
                return;

        o = pa_context_set_source_port_by_index(c, i->index, s, NULL, NULL);
        g_clear_pointer (&o, pa_operation_unref);
}

static void
gvc_mixer_control_set_port_status_for_headset (GvcMixerControl *control,
                                               guint            id,
                                               const char      *port_name,
                                               gboolean         is_output)
{
        pa_operation *o;
        PortStatusData *data;

        if (port_name == NULL)
                return;

        data = g_new0 (PortStatusData, 1);
        data->port_name_to_set = g_strdup (port_name);
        data->headset_card = id;

        if (is_output)
                o = pa_context_get_sink_info_list (control->priv->pa_context, sink_info_cb, data);
        else
                o = pa_context_get_source_info_list (control->priv->pa_context, source_info_cb, data);

        g_clear_pointer (&o, pa_operation_unref);
}
#endif /* HAVE_ALSA */

static void
free_priv_port_names (GvcMixerControl    *control)
{
#ifdef HAVE_ALSA
        g_clear_pointer (&control->priv->headphones_name, g_free);
        g_clear_pointer (&control->priv->headsetmic_name, g_free);
        g_clear_pointer (&control->priv->headphonemic_name, g_free);
        g_clear_pointer (&control->priv->internalspk_name, g_free);
        g_clear_pointer (&control->priv->internalmic_name, g_free);
#endif
}

void
gvc_mixer_control_set_headset_port (GvcMixerControl      *control,
                                    guint                 id,
                                    GvcHeadsetPortChoice  choice)
{
        g_return_if_fail (GVC_IS_MIXER_CONTROL (control));

#ifdef HAVE_ALSA
        switch (choice) {
        case GVC_HEADSET_PORT_CHOICE_HEADPHONES:
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE);
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalmic_name, FALSE);
                break;
        case GVC_HEADSET_PORT_CHOICE_HEADSET:
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE);
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headsetmic_name, FALSE);
                break;
        case GVC_HEADSET_PORT_CHOICE_MIC:
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalspk_name, TRUE);
                gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphonemic_name, FALSE);
                break;
        case GVC_HEADSET_PORT_CHOICE_NONE:
        default:
                g_assert_not_reached ();
        }
#else
        g_warning ("BUG: libgnome-volume-control compiled without ALSA support");
#endif /* HAVE_ALSA */
}

#ifdef HAVE_ALSA
typedef struct {
        const pa_card_port_info *headphones;
        const pa_card_port_info *headsetmic;
        const pa_card_port_info *headphonemic;
        const pa_card_port_info *internalmic;
        const pa_card_port_info *internalspk;
} headset_ports;

/*
   In PulseAudio without ucm, ports will show up with the following names:
   Headphones - analog-output-headphones
   Headset mic - analog-input-headset-mic (was: analog-input-microphone-headset)
   Jack in mic-in mode - analog-input-headphone-mic (was: analog-input-microphone)

   However, since regular mics also show up as analog-input-microphone,
   we need to check for certain controls on alsa mixer level too, to know
   if we deal with a separate mic jack, or a multi-function jack with a
   mic-in mode (also called "headphone mic").
   We check for the following names:

   Headphone Mic Jack - indicates headphone and mic-in mode share the same jack,
     i e, not two separate jacks. Hardware cannot distinguish between a
     headphone and a mic.
   Headset Mic Phantom Jack - indicates headset jack where hardware can not
     distinguish between headphones and headsets
   Headset Mic Jack - indicates headset jack where hardware can distinguish
     between headphones and headsets. There is no use popping up a dialog in
     this case, unless we already need to do this for the mic-in mode.

   From the PA_PROCOTOL_VERSION=34, The device_port structure adds 2 members
   availability_group and type, with the help of these 2 members, we could
   consolidate the port checking and port setting for non-ucm and with-ucm
   cases.
*/

#define HEADSET_PORT_SET(dst, src) \
        do { \
                if (!(dst) || (dst)->priority < (src)->priority) \
                        dst = src; \
        } while (0)

#define GET_PORT_NAME(x) (x ? g_strdup (x->name) : NULL)

static headset_ports *
get_headset_ports (GvcMixerControl    *control,
                   const pa_card_info *c)
{
        headset_ports *h;
        guint i;

        h = g_new0 (headset_ports, 1);

        for (i = 0; i < c->n_ports; i++) {
                pa_card_port_info *p = c->ports[i];
                if (control->priv->server_protocol_version < 34) {
                        if (g_str_equal (p->name, "analog-output-headphones"))
                                h->headphones = p;
                        else if (g_str_equal (p->name, "analog-input-headset-mic"))
                                h->headsetmic = p;
                        else if (g_str_equal (p->name, "analog-input-headphone-mic"))
                                h->headphonemic = p;
                        else if (g_str_equal (p->name, "analog-input-internal-mic"))
                                h->internalmic = p;
                        else if (g_str_equal (p->name, "analog-output-speaker"))
                                h->internalspk = p;
                } else {
#if (PA_PROTOCOL_VERSION >= 34)
                        /* in the first loop, set only headphones */
                        /* the microphone ports are assigned in the second loop */
                        if (p->type == PA_DEVICE_PORT_TYPE_HEADPHONES) {
                                if (p->availability_group)
                                        HEADSET_PORT_SET (h->headphones, p);
                        } else if (p->type == PA_DEVICE_PORT_TYPE_SPEAKER) {
                                HEADSET_PORT_SET (h->internalspk, p);
                        } else if (p->type == PA_DEVICE_PORT_TYPE_MIC) {
                                if (!p->availability_group)
                                        HEADSET_PORT_SET (h->internalmic, p);
                        }
#else
                        g_warning_once ("libgnome-volume-control running against PulseAudio %u, "
                                        "but compiled against older %d, report a bug to your distribution",
                                        control->priv->server_protocol_version,
                                        PA_PROTOCOL_VERSION);
#endif
                }
        }

#if (PA_PROTOCOL_VERSION >= 34)
        if (h->headphones && (control->priv->server_protocol_version >= 34)) {
                for (i = 0; i < c->n_ports; i++) {
                        pa_card_port_info *p = c->ports[i];
                        if (g_strcmp0(h->headphones->availability_group, p->availability_group))
                                continue;
                        if (p->direction != PA_DIRECTION_INPUT)
                                continue;
                        if (p->type == PA_DEVICE_PORT_TYPE_HEADSET)
                                HEADSET_PORT_SET (h->headsetmic, p);
                        else if (p->type == PA_DEVICE_PORT_TYPE_MIC)
                                HEADSET_PORT_SET (h->headphonemic, p);
                }
        }
#endif

        return h;
}

static gboolean
verify_alsa_card (int       cardindex,
                  gboolean *headsetmic,
                  gboolean *headphonemic)
{
        char *ctlstr;
        snd_hctl_t *hctl;
        snd_ctl_elem_id_t *id;
        int err;

        *headsetmic = FALSE;
        *headphonemic = FALSE;

        ctlstr = g_strdup_printf ("hw:%i", cardindex);
        if ((err = snd_hctl_open (&hctl, ctlstr, 0)) < 0) {
                g_warning ("snd_hctl_open failed: %s", snd_strerror(err));
                g_free (ctlstr);
                return FALSE;
        }
        g_free (ctlstr);

        if ((err = snd_hctl_load (hctl)) < 0) {
                g_warning ("snd_hctl_load failed: %s", snd_strerror(err));
                snd_hctl_close (hctl);
                return FALSE;
        }

        snd_ctl_elem_id_alloca (&id);

        snd_ctl_elem_id_clear (id);
        snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
        snd_ctl_elem_id_set_name (id, "Headphone Mic Jack");
        if (snd_hctl_find_elem (hctl, id))
                *headphonemic = TRUE;

        snd_ctl_elem_id_clear (id);
        snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
        snd_ctl_elem_id_set_name (id, "Headset Mic Phantom Jack");
        if (snd_hctl_find_elem (hctl, id))
                *headsetmic = TRUE;

        if (*headphonemic) {
                snd_ctl_elem_id_clear (id);
                snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_CARD);
                snd_ctl_elem_id_set_name (id, "Headset Mic Jack");
                if (snd_hctl_find_elem (hctl, id))
                        *headsetmic = TRUE;
        }

        snd_hctl_close (hctl);
        return *headsetmic || *headphonemic;
}

static void
check_audio_device_selection_needed (GvcMixerControl    *control,
                                     const pa_card_info *info)
{
        headset_ports *h;
        gboolean start_dialog, stop_dialog;

        start_dialog = FALSE;
        stop_dialog = FALSE;
        h = get_headset_ports (control, info);

        if (!h->headphones ||
            (!h->headsetmic && !h->headphonemic)) {
                /* Not a headset jack */
                goto out;
        }

        if (control->priv->headset_card != (int) info->index) {
                int cardindex;
                gboolean hsmic = TRUE;
                gboolean hpmic = TRUE;
                const char *s;

                s = pa_proplist_gets (info->proplist, "alsa.card");
                if (!s)
                        goto out;

                cardindex = strtol (s, NULL, 10);
                if (cardindex == 0 && strcmp(s, "0") != 0)
                        goto out;

                if (control->priv->server_protocol_version < 34) {
                        if (!verify_alsa_card(cardindex, &hsmic, &hpmic))
                                goto out;
                }

                control->priv->headset_card = info->index;
                control->priv->has_headsetmic = hsmic && h->headsetmic;
                control->priv->has_headphonemic = hpmic && h->headphonemic;
        } else {
                start_dialog = (h->headphones->available != PA_PORT_AVAILABLE_NO) && !control->priv->headset_plugged_in;
                stop_dialog = (h->headphones->available == PA_PORT_AVAILABLE_NO) && control->priv->headset_plugged_in;
        }

        control->priv->headset_plugged_in = h->headphones->available != PA_PORT_AVAILABLE_NO;
        free_priv_port_names (control);
        control->priv->headphones_name = GET_PORT_NAME(h->headphones);
        control->priv->headsetmic_name = GET_PORT_NAME(h->headsetmic);
        control->priv->headphonemic_name = GET_PORT_NAME(h->headphonemic);
        control->priv->internalspk_name = GET_PORT_NAME(h->internalspk);
        control->priv->internalmic_name = GET_PORT_NAME(h->internalmic);

        if (!start_dialog &&
            !stop_dialog)
                goto out;

        if (stop_dialog) {
                g_signal_emit (G_OBJECT (control),
                               signals[AUDIO_DEVICE_SELECTION_NEEDED],
                               0,
                               info->index,
                               FALSE,
                               GVC_HEADSET_PORT_CHOICE_NONE);
        } else {
                GvcHeadsetPortChoice choices;

                choices = GVC_HEADSET_PORT_CHOICE_HEADPHONES;
                if (control->priv->has_headsetmic)
                        choices |= GVC_HEADSET_PORT_CHOICE_HEADSET;
                if (control->priv->has_headphonemic)
                        choices |= GVC_HEADSET_PORT_CHOICE_MIC;

                g_signal_emit (G_OBJECT (control),
                               signals[AUDIO_DEVICE_SELECTION_NEEDED],
                               0,
                               info->index,
                               TRUE,
                               choices);
        }

out:
        g_free (h);
}
#endif /* HAVE_ALSA */

/*
 * At this point we can determine all devices available to us (besides network 'ports')
 * This is done by the following:
 *
 * - gvc_mixer_card and gvc_mixer_card_ports are created and relevant setters are called.
 * - If it's a 'normal' card with ports it will create a new ui-device or
 *   synchronise port availability with the existing device cached for that port on this card. */

static void
update_card (GvcMixerControl      *control,
             const pa_card_info   *info)
{
        const GList  *m = NULL;
        GvcMixerCard *card;
        gboolean      is_new = FALSE;
        GList *profile_list = NULL;
        GList *old_ports;
#if 1
        guint i;
        const char *key;
        void *state;

        g_debug ("Updating card %s (index: %u driver: %s):",
                 info->name, info->index, info->driver);

        for (i = 0; i < info->n_profiles; i++) {
                struct pa_card_profile_info pi = info->profiles[i];
                gboolean is_default;

                is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0);
                g_debug ("\tProfile '%s': %d sources %d sinks%s",
                         pi.name, pi.n_sources, pi.n_sinks,
                         is_default ? " (Current)" : "");
        }
        state = NULL;
        key = pa_proplist_iterate (info->proplist, &state);
        while (key != NULL) {
                g_debug ("\tProperty: '%s' = '%s'",
                        key, pa_proplist_gets (info->proplist, key));
                key = pa_proplist_iterate (info->proplist, &state);
        }
#endif
        card = g_hash_table_lookup (control->priv->cards,
                                    GUINT_TO_POINTER (info->index));
        if (card == NULL) {
                card = gvc_mixer_card_new (control->priv->pa_context,
                                           info->index);
                is_new = TRUE;
        }

        for (i = 0; i < info->n_profiles; i++) {
                GvcMixerCardProfile *profile;
                struct pa_card_profile_info pi = info->profiles[i];

                profile = g_new0 (GvcMixerCardProfile, 1);
                profile->profile = g_strdup (pi.name);
                profile->human_profile = g_strdup (pi.description);
                profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources);
                profile->n_sinks = pi.n_sinks;
                profile->n_sources = pi.n_sources;
                profile->priority = pi.priority;
                profile_list = g_list_prepend (profile_list, profile);
        }

        gvc_mixer_card_set_profiles (card, profile_list);
        gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description"));
        gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name"));
        gvc_mixer_card_set_profile (card, info->active_profile->name);

        if (is_new) {
                g_hash_table_insert (control->priv->cards,
                                     GUINT_TO_POINTER (info->index),
                                     card);
        }

        old_ports = g_list_copy ((GList *)gvc_mixer_card_get_ports (card));
        for (m = old_ports; m; m = m->next) {
                GvcMixerCardPort *card_port = m->data;
                gboolean found = FALSE;

                for (i = 0; i < info->n_ports; i++) {
                        pa_card_port_info *port = info->ports[i];

                        if (g_strcmp0 (card_port->port, port->name) == 0)
                                found = TRUE;
                }

                if (!found) {
                        update_ui_device_on_port_removed (control, card_port, card);
                        gvc_mixer_card_remove_port (card, card_port);
                }
        }
        g_clear_pointer (&old_ports, g_list_free);

        for (i = 0; i < info->n_ports; i++) {
                pa_card_port_info *port = info->ports[i];
                gboolean found = FALSE;

                for (m = gvc_mixer_card_get_ports (card); m; m = m->next) {
                        GvcMixerCardPort *card_port = m->data;

                        if (g_strcmp0 (card_port->port, port->name) == 0) {
                                found = TRUE;
                                update_ui_device_on_port_changed (control, card_port, port, card);
                        }
                }

                if (!found) {
                        GvcMixerCardPort *card_port;

                        card_port = g_new0 (GvcMixerCardPort, 1);
                        card_port->port = g_strdup (port->name);
                        card_port->human_port = g_strdup (port->description);
                        card_port->priority = port->priority;
                        card_port->available = port->available;
                        card_port->direction = port->direction;
                        card_port->icon_name = g_strdup (pa_proplist_gets (port->proplist, "device.icon_name"));
                        card_port->profiles = determine_profiles_for_port (port, profile_list);

                        gvc_mixer_card_add_port (card, card_port);
                        update_ui_device_on_port_added (control, card_port, card);
                }
        }

#ifdef HAVE_ALSA
        check_audio_device_selection_needed (control, info);
#endif /* HAVE_ALSA */

        g_signal_emit (G_OBJECT (control),
                       signals[CARD_ADDED],
                       0,
                       info->index);
}

static void
_pa_context_get_sink_info_cb (pa_context         *context,
                              const pa_sink_info *i,
                              int                 eol,
                              void               *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
                        return;
                }

                g_warning ("Sink callback failure");
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                return;
        }

        update_sink (control, i);
}

static void
_pa_context_get_source_info_cb (pa_context           *context,
                                const pa_source_info *i,
                                int                   eol,
                                void                 *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
                        return;
                }

                g_warning ("Source callback failure");
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                return;
        }

        update_source (control, i);
}

static void
_pa_context_get_sink_input_info_cb (pa_context               *context,
                                    const pa_sink_input_info *i,
                                    int                       eol,
                                    void                     *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
                        return;
                }

                g_warning ("Sink input callback failure");
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                return;
        }

        update_sink_input (control, i);
}

static void
_pa_context_get_source_output_info_cb (pa_context                  *context,
                                       const pa_source_output_info *i,
                                       int                          eol,
                                       void                        *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
                        return;
                }

                g_warning ("Source output callback failure");
                return;
        }

        if (eol > 0)  {
                dec_outstanding (control);
                return;
        }

        update_source_output (control, i);
}

static void
_pa_context_get_client_info_cb (pa_context           *context,
                                const pa_client_info *i,
                                int                   eol,
                                void                 *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
                        return;
                }

                g_warning ("Client callback failure");
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                return;
        }

        update_client (control, i);
}

static void
_pa_context_get_card_info_by_index_cb (pa_context *context,
                                       const pa_card_info *i,
                                       int eol,
                                       void *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                if (pa_context_errno (context) == PA_ERR_NOENTITY)
                        return;

                g_warning ("Card callback failure");
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                return;
        }

        update_card (control, i);
}

static void
_pa_context_get_server_info_cb (pa_context           *context,
                                const pa_server_info *i,
                                void                 *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (i == NULL) {
                g_warning ("Server info callback failure");
                return;
        }
        g_debug ("get server info");
        update_server (control, i);
        dec_outstanding (control);
}

static void
remove_event_role_stream (GvcMixerControl *control)
{
        g_debug ("Removing event role");
}

static void
update_event_role_stream (GvcMixerControl                  *control,
                          const pa_ext_stream_restore_info *info)
{
        GvcMixerStream *stream;
        gboolean        is_new;
        pa_volume_t     max_volume;

        if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
                return;
        }

#if 0
        g_debug ("Updating event role: name='%s' device='%s'",
                 info->name,
                 info->device);
#endif

        is_new = FALSE;

        if (!control->priv->event_sink_input_is_set) {
                pa_channel_map pa_map;
                GvcChannelMap *map;

                pa_map.channels = 1;
                pa_map.map[0] = PA_CHANNEL_POSITION_MONO;
                map = gvc_channel_map_new_from_pa_channel_map (&pa_map);

                stream = gvc_mixer_event_role_new (control->priv->pa_context,
                                                   info->device,
                                                   map);
                control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
                control->priv->event_sink_input_is_set = TRUE;

                is_new = TRUE;
        } else {
                stream = g_hash_table_lookup (control->priv->all_streams,
                                              GUINT_TO_POINTER (control->priv->event_sink_input_id));
        }

        /* 0 channels here means there is no valid volume in
         * pa_ext_stream_restore_info() and in the saved stream entry of
         * module-stream-restore in PulseAudio. */
        if (info->volume.channels == 0)
                max_volume = PA_VOLUME_NORM;
        else
                max_volume = pa_cvolume_max (&info->volume);

        gvc_mixer_stream_set_name (stream, _("System Sounds"));
        gvc_mixer_stream_set_icon_name (stream, "audio-x-generic");
        gvc_mixer_stream_set_volume (stream, (guint)max_volume);
        gvc_mixer_stream_set_is_muted (stream, info->mute);

        if (is_new) {
                add_stream (control, stream);
        }
}

static void
_pa_ext_stream_restore_read_cb (pa_context                       *context,
                                const pa_ext_stream_restore_info *i,
                                int                               eol,
                                void                             *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        if (eol < 0) {
                g_debug ("Failed to initialized stream_restore extension: %s",
                         pa_strerror (pa_context_errno (context)));
                remove_event_role_stream (control);
                return;
        }

        if (eol > 0) {
                dec_outstanding (control);
                /* If we don't have an event stream to restore, then
                 * set one up with a default 100% volume */
                if (!control->priv->event_sink_input_is_set) {
                        pa_ext_stream_restore_info info;

                        memset (&info, 0, sizeof(info));
                        info.name = "sink-input-by-media-role:event";
                        info.volume.channels = 1;
                        info.volume.values[0] = PA_VOLUME_NORM;
                        update_event_role_stream (control, &info);
                }
                return;
        }

        update_event_role_stream (control, i);
}

static void
_pa_ext_stream_restore_subscribe_cb (pa_context *context,
                                     void       *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
        pa_operation    *o;

        o = pa_ext_stream_restore_read (context,
                                        _pa_ext_stream_restore_read_cb,
                                        control);
        if (o == NULL) {
                g_warning ("pa_ext_stream_restore_read() failed");
                return;
        }

        pa_operation_unref (o);
}

static void
req_update_server_info (GvcMixerControl *control,
                        int              index)
{
        pa_operation *o;

        o = pa_context_get_server_info (control->priv->pa_context,
                                        _pa_context_get_server_info_cb,
                                        control);
        if (o == NULL) {
                g_warning ("pa_context_get_server_info() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_client_info (GvcMixerControl *control,
                        int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_client_info_list (control->priv->pa_context,
                                                     _pa_context_get_client_info_cb,
                                                     control);
        } else {
                o = pa_context_get_client_info (control->priv->pa_context,
                                                index,
                                                _pa_context_get_client_info_cb,
                                                control);
        }

        if (o == NULL) {
                g_warning ("pa_context_client_info_list() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_card (GvcMixerControl *control,
                 int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_card_info_list (control->priv->pa_context,
                                                   _pa_context_get_card_info_by_index_cb,
                                                   control);
        } else {
                o = pa_context_get_card_info_by_index (control->priv->pa_context,
                                                       index,
                                                       _pa_context_get_card_info_by_index_cb,
                                                       control);
        }

        if (o == NULL) {
                g_warning ("pa_context_get_card_info_by_index() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_sink_info (GvcMixerControl *control,
                      int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_sink_info_list (control->priv->pa_context,
                                                   _pa_context_get_sink_info_cb,
                                                   control);
        } else {
                o = pa_context_get_sink_info_by_index (control->priv->pa_context,
                                                       index,
                                                       _pa_context_get_sink_info_cb,
                                                       control);
        }

        if (o == NULL) {
                g_warning ("pa_context_get_sink_info_list() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_source_info (GvcMixerControl *control,
                        int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_source_info_list (control->priv->pa_context,
                                                     _pa_context_get_source_info_cb,
                                                     control);
        } else {
                o = pa_context_get_source_info_by_index(control->priv->pa_context,
                                                        index,
                                                        _pa_context_get_source_info_cb,
                                                        control);
        }

        if (o == NULL) {
                g_warning ("pa_context_get_source_info_list() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_sink_input_info (GvcMixerControl *control,
                            int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_sink_input_info_list (control->priv->pa_context,
                                                         _pa_context_get_sink_input_info_cb,
                                                         control);
        } else {
                o = pa_context_get_sink_input_info (control->priv->pa_context,
                                                    index,
                                                    _pa_context_get_sink_input_info_cb,
                                                    control);
        }

        if (o == NULL) {
                g_warning ("pa_context_get_sink_input_info_list() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
req_update_source_output_info (GvcMixerControl *control,
                               int              index)
{
        pa_operation *o;

        if (index < 0) {
                o = pa_context_get_source_output_info_list (control->priv->pa_context,
                                                            _pa_context_get_source_output_info_cb,
                                                            control);
        } else {
                o = pa_context_get_source_output_info (control->priv->pa_context,
                                                       index,
                                                       _pa_context_get_source_output_info_cb,
                                                       control);
        }

        if (o == NULL) {
                g_warning ("pa_context_get_source_output_info_list() failed");
                return;
        }
        pa_operation_unref (o);
}

static void
remove_client (GvcMixerControl *control,
               guint            index)
{
        g_hash_table_remove (control->priv->clients,
                             GUINT_TO_POINTER (index));
}

static void
remove_card (GvcMixerControl *control,
             guint            index)
{

        GList *devices, *d;

        devices = g_list_concat (g_hash_table_get_values (control->priv->ui_inputs),
                                 g_hash_table_get_values (control->priv->ui_outputs));

        for (d = devices; d != NULL; d = d->next) {
                GvcMixerCard *card;
                GvcMixerUIDevice *device = d->data;

                g_object_get (G_OBJECT (device), "card", &card, NULL);

                if (card == NULL)
                        continue;

                if (gvc_mixer_card_get_index (card) == index) {
                        g_signal_emit (G_OBJECT (control),
                                       signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));
                        g_debug ("Card removal remove device %s",
                                 gvc_mixer_ui_device_get_description (device));
                        g_hash_table_remove (gvc_mixer_ui_device_is_output (device) ? control->priv->ui_outputs : control->priv->ui_inputs,
                                             GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)));
                }
        }

        g_list_free (devices);

        g_hash_table_remove (control->priv->cards,
                             GUINT_TO_POINTER (index));

        g_signal_emit (G_OBJECT (control),
                       signals[CARD_REMOVED],
                       0,
                       index);
}

static void
remove_sink (GvcMixerControl *control,
             guint            index)
{
        GvcMixerStream   *stream;
        GvcMixerUIDevice *device;

        g_debug ("Removing sink: index=%u", index);

        stream = g_hash_table_lookup (control->priv->sinks,
                                      GUINT_TO_POINTER (index));
        if (stream == NULL)
                return;

        device = gvc_mixer_control_lookup_device_from_stream (control, stream);

        if (device != NULL) {
                gvc_mixer_ui_device_invalidate_stream (device);
                if (!gvc_mixer_ui_device_has_ports (device)) {
                        g_signal_emit (G_OBJECT (control),
                                       signals[OUTPUT_REMOVED],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));
                } else {
                        GList *devices, *d;

                        devices = g_hash_table_get_values (control->priv->ui_outputs);

                        for (d = devices; d != NULL; d = d->next) {
                                guint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
                                device = d->data;
                                g_object_get (G_OBJECT (device),
                                             "stream-id", &stream_id,
                                              NULL);
                                if (stream_id == gvc_mixer_stream_get_id (stream))
                                        gvc_mixer_ui_device_invalidate_stream (device);
                        }

                        g_list_free (devices);
                }
        }

        g_hash_table_remove (control->priv->sinks,
                             GUINT_TO_POINTER (index));

        remove_stream (control, stream);
}

static void
remove_source (GvcMixerControl *control,
               guint            index)
{
        GvcMixerStream   *stream;
        GvcMixerUIDevice *device;

        g_debug ("Removing source: index=%u", index);

        stream = g_hash_table_lookup (control->priv->sources,
                                      GUINT_TO_POINTER (index));
        if (stream == NULL)
                return;

        device = gvc_mixer_control_lookup_device_from_stream (control, stream);

        if (device != NULL) {
                gvc_mixer_ui_device_invalidate_stream (device);
                if (!gvc_mixer_ui_device_has_ports (device)) {
                        g_signal_emit (G_OBJECT (control),
                                       signals[INPUT_REMOVED],
                                       0,
                                       gvc_mixer_ui_device_get_id (device));
                } else {
                        GList *devices, *d;

                        devices = g_hash_table_get_values (control->priv->ui_inputs);

                        for (d = devices; d != NULL; d = d->next) {
                                guint stream_id = GVC_MIXER_UI_DEVICE_INVALID;
                                device = d->data;
                                g_object_get (G_OBJECT (device),
                                             "stream-id", &stream_id,
                                              NULL);
                                if (stream_id == gvc_mixer_stream_get_id (stream))
                                        gvc_mixer_ui_device_invalidate_stream (device);
                        }

                        g_list_free (devices);
                }
        }

        g_hash_table_remove (control->priv->sources,
                             GUINT_TO_POINTER (index));

        remove_stream (control, stream);
}

static void
remove_sink_input (GvcMixerControl *control,
                   guint            index)
{
        GvcMixerStream *stream;

        g_debug ("Removing sink input: index=%u", index);

        stream = g_hash_table_lookup (control->priv->sink_inputs,
                                      GUINT_TO_POINTER (index));
        if (stream == NULL) {
                return;
        }
        g_hash_table_remove (control->priv->sink_inputs,
                             GUINT_TO_POINTER (index));

        remove_stream (control, stream);
}

static void
remove_source_output (GvcMixerControl *control,
                      guint            index)
{
        GvcMixerStream *stream;

        g_debug ("Removing source output: index=%u", index);

        stream = g_hash_table_lookup (control->priv->source_outputs,
                                      GUINT_TO_POINTER (index));
        if (stream == NULL) {
                return;
        }
        g_hash_table_remove (control->priv->source_outputs,
                             GUINT_TO_POINTER (index));

        remove_stream (control, stream);
}

static void
_pa_context_subscribe_cb (pa_context                  *context,
                          pa_subscription_event_type_t t,
                          uint32_t                     index,
                          void                        *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
        case PA_SUBSCRIPTION_EVENT_SINK:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_sink (control, index);
                } else {
                        req_update_sink_info (control, index);
                }
                break;

        case PA_SUBSCRIPTION_EVENT_SOURCE:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_source (control, index);
                } else {
                        req_update_source_info (control, index);
                }
                break;

        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_sink_input (control, index);
                } else {
                        req_update_sink_input_info (control, index);
                }
                break;

        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_source_output (control, index);
                } else {
                        req_update_source_output_info (control, index);
                }
                break;

        case PA_SUBSCRIPTION_EVENT_CLIENT:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_client (control, index);
                } else {
                        req_update_client_info (control, index);
                }
                break;

        case PA_SUBSCRIPTION_EVENT_SERVER:
                req_update_server_info (control, index);
                break;

        case PA_SUBSCRIPTION_EVENT_CARD:
                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                        remove_card (control, index);
                } else {
                        req_update_card (control, index);
                }
                break;
        default:
                break;
        }
}

static void
gvc_mixer_control_ready (GvcMixerControl *control)
{
        pa_operation *o;

        pa_context_set_subscribe_callback (control->priv->pa_context,
                                           _pa_context_subscribe_cb,
                                           control);
        o = pa_context_subscribe (control->priv->pa_context,
                                  (pa_subscription_mask_t)
                                  (PA_SUBSCRIPTION_MASK_SINK|
                                   PA_SUBSCRIPTION_MASK_SOURCE|
                                   PA_SUBSCRIPTION_MASK_SINK_INPUT|
                                   PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
                                   PA_SUBSCRIPTION_MASK_CLIENT|
                                   PA_SUBSCRIPTION_MASK_SERVER|
                                   PA_SUBSCRIPTION_MASK_CARD),
                                  NULL,
                                  NULL);

        if (o == NULL) {
                g_warning ("pa_context_subscribe() failed");
                return;
        }
        pa_operation_unref (o);

        req_update_server_info (control, -1);
        req_update_card (control, -1);
        req_update_client_info (control, -1);
        req_update_sink_info (control, -1);
        req_update_source_info (control, -1);
        req_update_sink_input_info (control, -1);
        req_update_source_output_info (control, -1);

        control->priv->server_protocol_version = pa_context_get_server_protocol_version (control->priv->pa_context);

        control->priv->n_outstanding = 6;

        /* This call is not always supported */
        o = pa_ext_stream_restore_read (control->priv->pa_context,
                                        _pa_ext_stream_restore_read_cb,
                                        control);
        if (o != NULL) {
                pa_operation_unref (o);
                control->priv->n_outstanding++;

                pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
                                                        _pa_ext_stream_restore_subscribe_cb,
                                                        control);

                o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
                                                     1,
                                                     NULL,
                                                     NULL);
                if (o != NULL) {
                        pa_operation_unref (o);
                }

        } else {
                g_debug ("Failed to initialized stream_restore extension: %s",
                         pa_strerror (pa_context_errno (control->priv->pa_context)));
        }
}

static void
gvc_mixer_new_pa_context (GvcMixerControl *self)
{
        pa_proplist     *proplist;

        g_return_if_fail (self);
        g_return_if_fail (!self->priv->pa_context);

        proplist = pa_proplist_new ();
        pa_proplist_sets (proplist,
                          PA_PROP_APPLICATION_NAME,
                          self->priv->name);
        pa_proplist_sets (proplist,
                          PA_PROP_APPLICATION_ID,
                          "org.gnome.VolumeControl");
        pa_proplist_sets (proplist,
                          PA_PROP_APPLICATION_ICON_NAME,
                          "multimedia-volume-control");
        pa_proplist_sets (proplist,
                          PA_PROP_APPLICATION_VERSION,
                          PACKAGE_VERSION);

        self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);

        pa_proplist_free (proplist);
        g_assert (self->priv->pa_context);
}

static void
remove_all_items (GvcMixerControl *control,
                  GHashTable *hash_table,
                  void (*remove_item)(GvcMixerControl *control, guint index))
{
        GHashTableIter iter;
        gpointer key, value;

        g_hash_table_iter_init (&iter, hash_table);
        while (g_hash_table_iter_next (&iter, &key, &value)) {
                if (remove_item) {
                        remove_item (control, GPOINTER_TO_UINT (key));
                        g_hash_table_remove (hash_table, key);
                        g_hash_table_iter_init (&iter, hash_table);
                } else {
                        g_hash_table_iter_remove (&iter);
                }
        }
}

static gboolean
idle_reconnect (gpointer data)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (data);

        g_return_val_if_fail (control, FALSE);

        g_debug ("Reconnect: clean up all objects");

        remove_all_items (control, control->priv->sinks, remove_sink);
        remove_all_items (control, control->priv->sources, remove_source);
        remove_all_items (control, control->priv->sink_inputs, remove_sink_input);
        remove_all_items (control, control->priv->source_outputs, remove_source_output);
        remove_all_items (control, control->priv->cards, remove_card);
        remove_all_items (control, control->priv->ui_inputs, NULL);
        remove_all_items (control, control->priv->ui_outputs, NULL);
        remove_all_items (control, control->priv->clients, remove_client);

        g_debug ("Reconnect: make new connection");

        if (control->priv->pa_context) {
                pa_context_unref (control->priv->pa_context);
                control->priv->pa_context = NULL;
                control->priv->server_protocol_version = 0;
                gvc_mixer_new_pa_context (control);
        }

        gvc_mixer_control_open (control); /* cannot fail */

        control->priv->reconnect_id = 0;
        return FALSE;
}

static void
_pa_context_state_cb (pa_context *context,
                      void       *userdata)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);

        switch (pa_context_get_state (context)) {
        case PA_CONTEXT_UNCONNECTED:
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
                break;

        case PA_CONTEXT_READY:
                gvc_mixer_control_ready (control);
                break;

        case PA_CONTEXT_FAILED:
                control->priv->state = GVC_STATE_FAILED;
                g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED);
                if (control->priv->reconnect_id == 0)
                        control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
                break;

        case PA_CONTEXT_TERMINATED:
        default:
                /* FIXME: */
                break;
        }
}

gboolean
gvc_mixer_control_open (GvcMixerControl *control)
{
        int res;

        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
        g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
        g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE);

        pa_context_set_state_callback (control->priv->pa_context,
                                       _pa_context_state_cb,
                                       control);

        control->priv->state = GVC_STATE_CONNECTING;
        g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING);
        res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL);
        if (res < 0) {
                g_warning ("Failed to connect context: %s",
                           pa_strerror (pa_context_errno (control->priv->pa_context)));
        }

        return res;
}

gboolean
gvc_mixer_control_close (GvcMixerControl *control)
{
        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
        g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);

        pa_context_disconnect (control->priv->pa_context);

        control->priv->state = GVC_STATE_CLOSED;
        g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED);
        return TRUE;
}

static void
gvc_mixer_control_dispose (GObject *object)
{
        GvcMixerControl *control = GVC_MIXER_CONTROL (object);

        if (control->priv->reconnect_id != 0) {
                g_source_remove (control->priv->reconnect_id);
                control->priv->reconnect_id = 0;
        }

        if (control->priv->pa_context != NULL) {
                pa_context_unref (control->priv->pa_context);
                control->priv->pa_context = NULL;
        }

        if (control->priv->default_source_name != NULL) {
                g_free (control->priv->default_source_name);
                control->priv->default_source_name = NULL;
        }
        if (control->priv->default_sink_name != NULL) {
                g_free (control->priv->default_sink_name);
                control->priv->default_sink_name = NULL;
        }

        if (control->priv->pa_mainloop != NULL) {
                pa_glib_mainloop_free (control->priv->pa_mainloop);
                control->priv->pa_mainloop = NULL;
        }

        if (control->priv->all_streams != NULL) {
                g_hash_table_destroy (control->priv->all_streams);
                control->priv->all_streams = NULL;
        }

        if (control->priv->sinks != NULL) {
                g_hash_table_destroy (control->priv->sinks);
                control->priv->sinks = NULL;
        }
        if (control->priv->sources != NULL) {
                g_hash_table_destroy (control->priv->sources);
                control->priv->sources = NULL;
        }
        if (control->priv->sink_inputs != NULL) {
                g_hash_table_destroy (control->priv->sink_inputs);
                control->priv->sink_inputs = NULL;
        }
        if (control->priv->source_outputs != NULL) {
                g_hash_table_destroy (control->priv->source_outputs);
                control->priv->source_outputs = NULL;
        }
        if (control->priv->clients != NULL) {
                g_hash_table_destroy (control->priv->clients);
                control->priv->clients = NULL;
        }
        if (control->priv->cards != NULL) {
                g_hash_table_destroy (control->priv->cards);
                control->priv->cards = NULL;
        }
        if (control->priv->ui_outputs != NULL) {
                g_hash_table_destroy (control->priv->ui_outputs);
                control->priv->ui_outputs = NULL;
        }
        if (control->priv->ui_inputs != NULL) {
                g_hash_table_destroy (control->priv->ui_inputs);
                control->priv->ui_inputs = NULL;
        }

        free_priv_port_names (control);
        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
}

static void
gvc_mixer_control_set_property (GObject       *object,
                                guint          prop_id,
                                const GValue  *value,
                                GParamSpec    *pspec)
{
        GvcMixerControl *self = GVC_MIXER_CONTROL (object);

        switch (prop_id) {
        case PROP_NAME:
                g_free (self->priv->name);
                self->priv->name = g_value_dup_string (value);
                g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gvc_mixer_control_get_property (GObject     *object,
                                guint        prop_id,
                                GValue      *value,
                                GParamSpec  *pspec)
{
        GvcMixerControl *self = GVC_MIXER_CONTROL (object);

        switch (prop_id) {
        case PROP_NAME:
                g_value_set_string (value, self->priv->name);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}


static GObject *
gvc_mixer_control_constructor (GType                  type,
                               guint                  n_construct_properties,
                               GObjectConstructParam *construct_params)
{
        GObject         *object;
        GvcMixerControl *self;

        object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);

        self = GVC_MIXER_CONTROL (object);

        gvc_mixer_new_pa_context (self);
        self->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID;

        return object;
}

static void
gvc_mixer_control_class_init (GvcMixerControlClass *klass)
{
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);

        object_class->constructor = gvc_mixer_control_constructor;
        object_class->dispose = gvc_mixer_control_dispose;
        object_class->finalize = gvc_mixer_control_finalize;
        object_class->set_property = gvc_mixer_control_set_property;
        object_class->get_property = gvc_mixer_control_get_property;

        obj_props[PROP_NAME] = g_param_spec_string ("name",
                                                    "Name",
                                                    "Name to display for this mixer control",
                                                    NULL,
                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        g_object_class_install_properties (object_class, N_PROPS, obj_props);

        signals [STATE_CHANGED] =
                g_signal_new ("state-changed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, state_changed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [STREAM_ADDED] =
                g_signal_new ("stream-added",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [STREAM_REMOVED] =
                g_signal_new ("stream-removed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [STREAM_CHANGED] =
                g_signal_new ("stream-changed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_changed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [AUDIO_DEVICE_SELECTION_NEEDED] =
                g_signal_new ("audio-device-selection-needed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              0,
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_UINT);
        signals [CARD_ADDED] =
                g_signal_new ("card-added",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_added),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [CARD_REMOVED] =
                g_signal_new ("card-removed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, card_removed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [DEFAULT_SINK_CHANGED] =
                g_signal_new ("default-sink-changed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [DEFAULT_SOURCE_CHANGED] =
                g_signal_new ("default-source-changed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [ACTIVE_OUTPUT_UPDATE] =
                g_signal_new ("active-output-update",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [ACTIVE_INPUT_UPDATE] =
                g_signal_new ("active-input-update",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [OUTPUT_ADDED] =
                g_signal_new ("output-added",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, output_added),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [INPUT_ADDED] =
                g_signal_new ("input-added",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, input_added),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [OUTPUT_REMOVED] =
                g_signal_new ("output-removed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, output_removed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
        signals [INPUT_REMOVED] =
                g_signal_new ("input-removed",
                              G_TYPE_FROM_CLASS (klass),
                              G_SIGNAL_RUN_LAST,
                              G_STRUCT_OFFSET (GvcMixerControlClass, input_removed),
                              NULL, NULL, NULL,
                              G_TYPE_NONE, 1, G_TYPE_UINT);
}


static void
gvc_mixer_control_init (GvcMixerControl *control)
{
        control->priv = gvc_mixer_control_get_instance_private (control);

        control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
        g_assert (control->priv->pa_mainloop);

        control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
        g_assert (control->priv->pa_api);

        control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->ui_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
        control->priv->ui_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);

        control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);

#ifdef HAVE_ALSA
        control->priv->headset_card = -1;
#endif /* HAVE_ALSA */

        control->priv->state = GVC_STATE_CLOSED;
}

static void
gvc_mixer_control_finalize (GObject *object)
{
        GvcMixerControl *mixer_control;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_CONTROL (object));

        mixer_control = GVC_MIXER_CONTROL (object);
        g_free (mixer_control->priv->name);
        mixer_control->priv->name = NULL;

        g_return_if_fail (mixer_control->priv != NULL);
        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
}

GvcMixerControl *
gvc_mixer_control_new (const char *name)
{
        GObject *control;
        control = g_object_new (GVC_TYPE_MIXER_CONTROL,
                                "name", name,
                                NULL);
        return GVC_MIXER_CONTROL (control);
}

gdouble
gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control)
{
	g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), 0);
	return (gdouble) PA_VOLUME_NORM;
}

gdouble
gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control)
{
	g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), 0);
	return (gdouble) PA_VOLUME_UI_MAX;
}
0707010000000F000081A400000000000000000000000166437C94000020FF000000000000000000000000000000000000003800000000libgnome-volume-control-0.gitmodule/gvc-mixer-control.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_CONTROL_H
#define __GVC_MIXER_CONTROL_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"
#include "gvc-mixer-card.h"
#include "gvc-mixer-ui-device.h"

G_BEGIN_DECLS

typedef enum
{
        GVC_STATE_CLOSED,
        GVC_STATE_READY,
        GVC_STATE_CONNECTING,
        GVC_STATE_FAILED
} GvcMixerControlState;

typedef enum
{
        GVC_HEADSET_PORT_CHOICE_NONE        = 0,
        GVC_HEADSET_PORT_CHOICE_HEADPHONES  = 1 << 0,
        GVC_HEADSET_PORT_CHOICE_HEADSET     = 1 << 1,
        GVC_HEADSET_PORT_CHOICE_MIC         = 1 << 2
} GvcHeadsetPortChoice;

#define GVC_TYPE_MIXER_CONTROL         (gvc_mixer_control_get_type ())
#define GVC_MIXER_CONTROL(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl))
#define GVC_MIXER_CONTROL_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
#define GVC_IS_MIXER_CONTROL(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CONTROL))
#define GVC_IS_MIXER_CONTROL_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CONTROL))
#define GVC_MIXER_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))

typedef struct GvcMixerControlPrivate GvcMixerControlPrivate;

typedef struct
{
        GObject                 parent;
        GvcMixerControlPrivate *priv;
} GvcMixerControl;

typedef struct
{
        GObjectClass            parent_class;

        void (*state_changed)          (GvcMixerControl      *control,
                                        GvcMixerControlState  new_state);
        void (*stream_added)           (GvcMixerControl *control,
                                        guint            id);
        void (*stream_changed)         (GvcMixerControl *control,
                                        guint            id);
        void (*stream_removed)         (GvcMixerControl *control,
                                        guint            id);
        void (*card_added)             (GvcMixerControl *control,
                                        guint            id);
        void (*card_removed)           (GvcMixerControl *control,
                                        guint            id);
        void (*default_sink_changed)   (GvcMixerControl *control,
                                        guint            id);
        void (*default_source_changed) (GvcMixerControl *control,
                                        guint            id);
        void (*active_output_update)   (GvcMixerControl *control,
                                        guint            id);
        void (*active_input_update)    (GvcMixerControl *control,
                                        guint            id);
        void (*output_added)           (GvcMixerControl *control,
                                        guint            id);
        void (*input_added)            (GvcMixerControl *control,
                                        guint            id);
        void (*output_removed)         (GvcMixerControl *control,
                                        guint            id);
        void (*input_removed)          (GvcMixerControl *control,
                                        guint            id);
        void (*audio_device_selection_needed)
                                       (GvcMixerControl      *control,
                                        guint                 id,
                                        gboolean              show_dialog,
                                        GvcHeadsetPortChoice  choices);
} GvcMixerControlClass;

GType               gvc_mixer_control_get_type            (void);

GvcMixerControl *   gvc_mixer_control_new                 (const char *name);

gboolean            gvc_mixer_control_open                (GvcMixerControl *control);
gboolean            gvc_mixer_control_close               (GvcMixerControl *control);

GSList *            gvc_mixer_control_get_cards           (GvcMixerControl *control);
GSList *            gvc_mixer_control_get_streams         (GvcMixerControl *control);
GSList *            gvc_mixer_control_get_sinks           (GvcMixerControl *control);
GSList *            gvc_mixer_control_get_sources         (GvcMixerControl *control);
GSList *            gvc_mixer_control_get_sink_inputs     (GvcMixerControl *control);
GSList *            gvc_mixer_control_get_source_outputs  (GvcMixerControl *control);

GvcMixerStream *        gvc_mixer_control_lookup_stream_id          (GvcMixerControl *control,
                                                                     guint            id);
GvcMixerCard   *        gvc_mixer_control_lookup_card_id            (GvcMixerControl *control,
                                                                     guint            id);
GvcMixerUIDevice *      gvc_mixer_control_lookup_output_id          (GvcMixerControl *control,
                                                                     guint            id);
GvcMixerUIDevice *      gvc_mixer_control_lookup_input_id           (GvcMixerControl *control,
                                                                     guint            id);
GvcMixerUIDevice *      gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control,
                                                                     GvcMixerStream *stream);

GvcMixerStream *        gvc_mixer_control_get_default_sink     (GvcMixerControl *control);
GvcMixerStream *        gvc_mixer_control_get_default_source   (GvcMixerControl *control);
GvcMixerStream *        gvc_mixer_control_get_event_sink_input (GvcMixerControl *control);

gboolean                gvc_mixer_control_set_default_sink     (GvcMixerControl *control,
                                                                GvcMixerStream  *stream);
gboolean                gvc_mixer_control_set_default_source   (GvcMixerControl *control,
                                                                GvcMixerStream  *stream);

gdouble                 gvc_mixer_control_get_vol_max_norm                  (GvcMixerControl *control);
gdouble                 gvc_mixer_control_get_vol_max_amplified             (GvcMixerControl *control);
void                    gvc_mixer_control_change_output                     (GvcMixerControl *control,
                                                                             GvcMixerUIDevice* output);
void                    gvc_mixer_control_change_input                      (GvcMixerControl *control,
                                                                             GvcMixerUIDevice* input);
GvcMixerStream*         gvc_mixer_control_get_stream_from_device            (GvcMixerControl *control,
                                                                             GvcMixerUIDevice *device);
gboolean                gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control,
                                                                             GvcMixerUIDevice *device,
                                                                             const gchar* profile);

void                    gvc_mixer_control_set_headset_port                  (GvcMixerControl      *control,
                                                                             guint                 id,
                                                                             GvcHeadsetPortChoice  choices);

GvcMixerControlState gvc_mixer_control_get_state            (GvcMixerControl *control);

G_END_DECLS

#endif /* __GVC_MIXER_CONTROL_H */
07070100000010000081A400000000000000000000000166437C9400001CC8000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/gvc-mixer-event-role.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>
#include <pulse/ext-stream-restore.h>

#include "gvc-mixer-event-role.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"

struct GvcMixerEventRolePrivate
{
        char          *device;
};

enum
{
        PROP_0,
        PROP_DEVICE,
        N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };

static void     gvc_mixer_event_role_finalize   (GObject            *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM)

static gboolean
update_settings (GvcMixerEventRole *role,
                 gboolean           is_muted,
                 gpointer          *op)
{
        pa_operation              *o;
        const GvcChannelMap       *map;
        pa_context                *context;
        pa_ext_stream_restore_info info;

        map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role));

        info.volume = *gvc_channel_map_get_cvolume(map);
        info.name = "sink-input-by-media-role:event";
        info.channel_map = *gvc_channel_map_get_pa_channel_map(map);
        info.device = role->priv->device;
        info.mute = is_muted;

        context = gvc_mixer_stream_get_pa_context (GVC_MIXER_STREAM (role));

        o = pa_ext_stream_restore_write (context,
                                         PA_UPDATE_REPLACE,
                                         &info,
                                         1,
                                         TRUE,
                                         NULL,
                                         NULL);

        if (o == NULL) {
                g_warning ("pa_ext_stream_restore_write() failed");
                return FALSE;
        }

        if (op != NULL)
                *op = o;

        return TRUE;
}

static gboolean
gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op)
{
        return update_settings (GVC_MIXER_EVENT_ROLE (stream),
                                gvc_mixer_stream_get_is_muted (stream), op);
}

static gboolean
gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream,
                                      gboolean        is_muted)
{
        /* Apply change straight away so that we don't get a race with
         * gvc_mixer_event_role_push_volume().
         * See https://bugs.freedesktop.org/show_bug.cgi?id=51413 */
        gvc_mixer_stream_set_is_muted (stream, is_muted);
        return update_settings (GVC_MIXER_EVENT_ROLE (stream),
                                is_muted, NULL);
}

static gboolean
gvc_mixer_event_role_set_device (GvcMixerEventRole *role,
                                 const char        *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_EVENT_ROLE (role), FALSE);

        g_free (role->priv->device);
        role->priv->device = g_strdup (device);
        g_object_notify_by_pspec (G_OBJECT (role), obj_props[PROP_DEVICE]);

        return TRUE;
}

static void
gvc_mixer_event_role_set_property (GObject       *object,
                                   guint          prop_id,
                                   const GValue  *value,
                                   GParamSpec    *pspec)
{
        GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object);

        switch (prop_id) {
        case PROP_DEVICE:
                gvc_mixer_event_role_set_device (self, g_value_get_string (value));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gvc_mixer_event_role_get_property (GObject     *object,
                                   guint        prop_id,
                                   GValue      *value,
                                   GParamSpec  *pspec)
{
        GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object);

        switch (prop_id) {
        case PROP_DEVICE:
                g_value_set_string (value, self->priv->device);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
{
        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);

        object_class->finalize = gvc_mixer_event_role_finalize;
        object_class->set_property = gvc_mixer_event_role_set_property;
        object_class->get_property = gvc_mixer_event_role_get_property;

        stream_class->push_volume = gvc_mixer_event_role_push_volume;
        stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;

        obj_props[PROP_DEVICE] = g_param_spec_string ("device",
                                                      "Device",
                                                      "Device",
                                                      NULL,
                                                      G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        g_object_class_install_properties (object_class, N_PROPS, obj_props);
}

static void
gvc_mixer_event_role_init (GvcMixerEventRole *event_role)
{
        event_role->priv = gvc_mixer_event_role_get_instance_private (event_role);

}

static void
gvc_mixer_event_role_finalize (GObject *object)
{
        GvcMixerEventRole *mixer_event_role;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_EVENT_ROLE (object));

        mixer_event_role = GVC_MIXER_EVENT_ROLE (object);

        g_return_if_fail (mixer_event_role->priv != NULL);

        g_free (mixer_event_role->priv->device);

        G_OBJECT_CLASS (gvc_mixer_event_role_parent_class)->finalize (object);
}

/**
 * gvc_mixer_event_role_new: (skip)
 * @context:
 * @device:
 * @channel_map:
 *
 * Returns:
 */
GvcMixerStream *
gvc_mixer_event_role_new (pa_context *context,
                          const char *device,
                          GvcChannelMap *channel_map)
{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_EVENT_ROLE,
                               "pa-context", context,
                               "index", 0,
                               "device", device,
                               "channel-map", channel_map,
                               NULL);

        return GVC_MIXER_STREAM (object);
}
07070100000011000081A400000000000000000000000166437C94000008DC000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/gvc-mixer-event-role.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_EVENT_ROLE_H
#define __GVC_MIXER_EVENT_ROLE_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_EVENT_ROLE         (gvc_mixer_event_role_get_type ())
#define GVC_MIXER_EVENT_ROLE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRole))
#define GVC_MIXER_EVENT_ROLE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass))
#define GVC_IS_MIXER_EVENT_ROLE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_EVENT_ROLE))
#define GVC_IS_MIXER_EVENT_ROLE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_EVENT_ROLE))
#define GVC_MIXER_EVENT_ROLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass))

typedef struct GvcMixerEventRolePrivate GvcMixerEventRolePrivate;

typedef struct
{
        GvcMixerStream            parent;
        GvcMixerEventRolePrivate *priv;
} GvcMixerEventRole;

typedef struct
{
        GvcMixerStreamClass parent_class;
} GvcMixerEventRoleClass;

GType               gvc_mixer_event_role_get_type      (void);

GvcMixerStream *    gvc_mixer_event_role_new           (pa_context    *context,
                                                        const char    *device,
                                                        GvcChannelMap *channel_map);

G_END_DECLS

#endif /* __GVC_MIXER_EVENT_ROLE_H */
07070100000012000081A400000000000000000000000166437C9400001257000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/gvc-mixer-sink-input.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-sink-input.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"

struct GvcMixerSinkInputPrivate
{
        gpointer dummy;
};

static void     gvc_mixer_sink_input_finalize   (GObject                *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)

static gboolean
gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
{
        pa_operation        *o;
        guint                index;
        const GvcChannelMap *map;
        pa_context          *context;
        const pa_cvolume    *cv;

        index = gvc_mixer_stream_get_index (stream);

        map = gvc_mixer_stream_get_channel_map (stream);

        cv = gvc_channel_map_get_cvolume(map);

        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_sink_input_volume (context,
                                              index,
                                              cv,
                                              NULL,
                                              NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_sink_input_volume() failed");
                return FALSE;
        }

        *op = o;

        return TRUE;
}

static gboolean
gvc_mixer_sink_input_change_is_muted (GvcMixerStream *stream,
                                      gboolean        is_muted)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_sink_input_mute (context,
                                            index,
                                            is_muted,
                                            NULL,
                                            NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_sink_input_mute_by_index() failed");
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static void
gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
{
        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);

        object_class->finalize = gvc_mixer_sink_input_finalize;

        stream_class->push_volume = gvc_mixer_sink_input_push_volume;
        stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
}

static void
gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
{
        sink_input->priv = gvc_mixer_sink_input_get_instance_private (sink_input);
}

static void
gvc_mixer_sink_input_finalize (GObject *object)
{
        GvcMixerSinkInput *mixer_sink_input;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));

        mixer_sink_input = GVC_MIXER_SINK_INPUT (object);

        g_return_if_fail (mixer_sink_input->priv != NULL);
        G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->finalize (object);
}

/**
 * gvc_mixer_sink_input_new: (skip)
 * @context:
 * @index:
 * @channel_map:
 *
 * Returns:
 */
GvcMixerStream *
gvc_mixer_sink_input_new (pa_context    *context,
                          guint          index,
                          GvcChannelMap *channel_map)
{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_SINK_INPUT,
                               "pa-context", context,
                               "index", index,
                               "channel-map", channel_map,
                               NULL);

        return GVC_MIXER_STREAM (object);
}
07070100000013000081A400000000000000000000000166437C94000008DB000000000000000000000000000000000000003B00000000libgnome-volume-control-0.gitmodule/gvc-mixer-sink-input.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_SINK_INPUT_H
#define __GVC_MIXER_SINK_INPUT_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_SINK_INPUT         (gvc_mixer_sink_input_get_type ())
#define GVC_MIXER_SINK_INPUT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInput))
#define GVC_MIXER_SINK_INPUT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))
#define GVC_IS_MIXER_SINK_INPUT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK_INPUT))
#define GVC_IS_MIXER_SINK_INPUT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK_INPUT))
#define GVC_MIXER_SINK_INPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))

typedef struct GvcMixerSinkInputPrivate GvcMixerSinkInputPrivate;

typedef struct
{
        GvcMixerStream            parent;
        GvcMixerSinkInputPrivate *priv;
} GvcMixerSinkInput;

typedef struct
{
        GvcMixerStreamClass parent_class;
} GvcMixerSinkInputClass;

GType               gvc_mixer_sink_input_get_type      (void);

GvcMixerStream *    gvc_mixer_sink_input_new           (pa_context    *context,
                                                        guint          index,
                                                        GvcChannelMap *channel_map);

G_END_DECLS

#endif /* __GVC_MIXER_SINK_INPUT_H */
07070100000014000081A400000000000000000000000166437C94000015A7000000000000000000000000000000000000003500000000libgnome-volume-control-0.gitmodule/gvc-mixer-sink.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-sink.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"

struct GvcMixerSinkPrivate
{
        gpointer dummy;
};

static void     gvc_mixer_sink_finalize   (GObject           *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)

static gboolean
gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op)
{
        pa_operation        *o;
        guint                index;
        const GvcChannelMap *map;
        pa_context          *context;
        const pa_cvolume    *cv;

        index = gvc_mixer_stream_get_index (stream);

        map = gvc_mixer_stream_get_channel_map (stream);

        /* set the volume */
        cv = gvc_channel_map_get_cvolume(map);

        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_sink_volume_by_index (context,
                                                 index,
                                                 cv,
                                                 NULL,
                                                 NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        *op = o;

        return TRUE;
}

static gboolean
gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
                                gboolean        is_muted)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_sink_mute_by_index (context,
                                               index,
                                               is_muted,
                                               NULL,
                                               NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static gboolean
gvc_mixer_sink_change_port (GvcMixerStream *stream,
                            const char     *port)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_sink_port_by_index (context,
                                               index,
                                               port,
                                               NULL,
                                               NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_sink_port_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static void
gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
{
        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);

        object_class->finalize = gvc_mixer_sink_finalize;

        stream_class->push_volume = gvc_mixer_sink_push_volume;
        stream_class->change_port = gvc_mixer_sink_change_port;
        stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
}

static void
gvc_mixer_sink_init (GvcMixerSink *sink)
{
        sink->priv = gvc_mixer_sink_get_instance_private (sink);
}

static void
gvc_mixer_sink_finalize (GObject *object)
{
        GvcMixerSink *mixer_sink;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_SINK (object));

        mixer_sink = GVC_MIXER_SINK (object);

        g_return_if_fail (mixer_sink->priv != NULL);
        G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->finalize (object);
}

/**
 * gvc_mixer_sink_new: (skip)
 * @context:
 * @index:
 * @channel_map:
 *
 * Returns:
 */
GvcMixerStream *
gvc_mixer_sink_new (pa_context    *context,
                    guint          index,
                    GvcChannelMap *channel_map)

{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_SINK,
                               "pa-context", context,
                               "index", index,
                               "channel-map", channel_map,
                               NULL);

        return GVC_MIXER_STREAM (object);
}
07070100000015000081A400000000000000000000000166437C9400000854000000000000000000000000000000000000003500000000libgnome-volume-control-0.gitmodule/gvc-mixer-sink.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_SINK_H
#define __GVC_MIXER_SINK_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_SINK         (gvc_mixer_sink_get_type ())
#define GVC_MIXER_SINK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK, GvcMixerSink))
#define GVC_MIXER_SINK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))
#define GVC_IS_MIXER_SINK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK))
#define GVC_IS_MIXER_SINK_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK))
#define GVC_MIXER_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))

typedef struct GvcMixerSinkPrivate GvcMixerSinkPrivate;

typedef struct
{
        GvcMixerStream       parent;
        GvcMixerSinkPrivate *priv;
} GvcMixerSink;

typedef struct
{
        GvcMixerStreamClass parent_class;
} GvcMixerSinkClass;

GType               gvc_mixer_sink_get_type            (void);

GvcMixerStream *    gvc_mixer_sink_new                 (pa_context    *context,
                                                        guint          index,
                                                        GvcChannelMap *channel_map);

G_END_DECLS

#endif /* __GVC_MIXER_SINK_H */
07070100000016000081A400000000000000000000000166437C94000012D5000000000000000000000000000000000000003E00000000libgnome-volume-control-0.gitmodule/gvc-mixer-source-output.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-source-output.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"

struct GvcMixerSourceOutputPrivate
{
        gpointer dummy;
};

static void     gvc_mixer_source_output_finalize   (GObject            *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM)

static gboolean
gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op)
{
        pa_operation        *o;
        guint                index;
        const GvcChannelMap *map;
        pa_context          *context;
        const pa_cvolume    *cv;

        index = gvc_mixer_stream_get_index (stream);

        map = gvc_mixer_stream_get_channel_map (stream);

        cv = gvc_channel_map_get_cvolume(map);

        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_source_output_volume (context,
                                                 index,
                                                 cv,
                                                 NULL,
                                                 NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_source_output_volume() failed");
                return FALSE;
        }

        *op = o;

        return TRUE;
}

static gboolean
gvc_mixer_source_output_change_is_muted (GvcMixerStream *stream,
                                      gboolean        is_muted)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_source_output_mute (context,
                                               index,
                                               is_muted,
                                               NULL,
                                               NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_source_output_mute_by_index() failed");
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static void
gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass)
{
        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);

        object_class->finalize = gvc_mixer_source_output_finalize;

        stream_class->push_volume = gvc_mixer_source_output_push_volume;
        stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted;
}

static void
gvc_mixer_source_output_init (GvcMixerSourceOutput *source_output)
{
        source_output->priv = gvc_mixer_source_output_get_instance_private (source_output);

}

static void
gvc_mixer_source_output_finalize (GObject *object)
{
        GvcMixerSourceOutput *mixer_source_output;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_SOURCE_OUTPUT (object));

        mixer_source_output = GVC_MIXER_SOURCE_OUTPUT (object);

        g_return_if_fail (mixer_source_output->priv != NULL);
        G_OBJECT_CLASS (gvc_mixer_source_output_parent_class)->finalize (object);
}

/**
 * gvc_mixer_source_output_new: (skip)
 * @context:
 * @index:
 * @channel_map:
 *
 * Returns:
 */
GvcMixerStream *
gvc_mixer_source_output_new (pa_context    *context,
                             guint          index,
                             GvcChannelMap *channel_map)
{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_SOURCE_OUTPUT,
                               "pa-context", context,
                               "index", index,
                               "channel-map", channel_map,
                               NULL);

        return GVC_MIXER_STREAM (object);
}
07070100000017000081A400000000000000000000000166437C940000092F000000000000000000000000000000000000003E00000000libgnome-volume-control-0.gitmodule/gvc-mixer-source-output.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_SOURCE_OUTPUT_H
#define __GVC_MIXER_SOURCE_OUTPUT_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_SOURCE_OUTPUT         (gvc_mixer_source_output_get_type ())
#define GVC_MIXER_SOURCE_OUTPUT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutput))
#define GVC_MIXER_SOURCE_OUTPUT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass))
#define GVC_IS_MIXER_SOURCE_OUTPUT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT))
#define GVC_IS_MIXER_SOURCE_OUTPUT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE_OUTPUT))
#define GVC_MIXER_SOURCE_OUTPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass))

typedef struct GvcMixerSourceOutputPrivate GvcMixerSourceOutputPrivate;

typedef struct
{
        GvcMixerStream               parent;
        GvcMixerSourceOutputPrivate *priv;
} GvcMixerSourceOutput;

typedef struct
{
        GvcMixerStreamClass parent_class;
} GvcMixerSourceOutputClass;

GType               gvc_mixer_source_output_get_type      (void);

GvcMixerStream *    gvc_mixer_source_output_new           (pa_context    *context,
                                                           guint          index,
                                                           GvcChannelMap *channel_map);

G_END_DECLS

#endif /* __GVC_MIXER_SOURCE_OUTPUT_H */
07070100000018000081A400000000000000000000000166437C9400001611000000000000000000000000000000000000003700000000libgnome-volume-control-0.gitmodule/gvc-mixer-source.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-source.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"

struct GvcMixerSourcePrivate
{
        gpointer dummy;
};

static void     gvc_mixer_source_finalize   (GObject            *object);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM)

static gboolean
gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op)
{
        pa_operation        *o;
        guint                index;
        const GvcChannelMap *map;
        pa_context          *context;
        const pa_cvolume    *cv;

        index = gvc_mixer_stream_get_index (stream);

        map = gvc_mixer_stream_get_channel_map (stream);

        /* set the volume */
        cv = gvc_channel_map_get_cvolume (map);

        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_source_volume_by_index (context,
                                                   index,
                                                   cv,
                                                   NULL,
                                                   NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        *op = o;

        return TRUE;
}

static gboolean
gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
                                gboolean        is_muted)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_source_mute_by_index (context,
                                                 index,
                                                 is_muted,
                                                 NULL,
                                                 NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static gboolean
gvc_mixer_source_change_port (GvcMixerStream *stream,
                              const char     *port)
{
        pa_operation *o;
        guint         index;
        pa_context   *context;

        index = gvc_mixer_stream_get_index (stream);
        context = gvc_mixer_stream_get_pa_context (stream);

        o = pa_context_set_source_port_by_index (context,
                                                 index,
                                                 port,
                                                 NULL,
                                                 NULL);

        if (o == NULL) {
                g_warning ("pa_context_set_source_port_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                return FALSE;
        }

        pa_operation_unref(o);

        return TRUE;
}

static void
gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
{
        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);

        object_class->finalize = gvc_mixer_source_finalize;

        stream_class->push_volume = gvc_mixer_source_push_volume;
        stream_class->change_is_muted = gvc_mixer_source_change_is_muted;
        stream_class->change_port = gvc_mixer_source_change_port;
}

static void
gvc_mixer_source_init (GvcMixerSource *source)
{
        source->priv = gvc_mixer_source_get_instance_private (source);
}

static void
gvc_mixer_source_finalize (GObject *object)
{
        GvcMixerSource *mixer_source;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_SOURCE (object));

        mixer_source = GVC_MIXER_SOURCE (object);

        g_return_if_fail (mixer_source->priv != NULL);
        G_OBJECT_CLASS (gvc_mixer_source_parent_class)->finalize (object);
}

/**
 * gvc_mixer_source_new: (skip)
 * @context:
 * @index:
 * @channel_map:
 *
 * Returns:
 */
GvcMixerStream *
gvc_mixer_source_new (pa_context    *context,
                      guint          index,
                      GvcChannelMap *channel_map)

{
        GObject *object;

        object = g_object_new (GVC_TYPE_MIXER_SOURCE,
                               "pa-context", context,
                               "index", index,
                               "channel-map", channel_map,
                               NULL);

        return GVC_MIXER_STREAM (object);
}
07070100000019000081A400000000000000000000000166437C9400000884000000000000000000000000000000000000003700000000libgnome-volume-control-0.gitmodule/gvc-mixer-source.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_SOURCE_H
#define __GVC_MIXER_SOURCE_H

#include <glib-object.h>
#include "gvc-mixer-stream.h"

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_SOURCE         (gvc_mixer_source_get_type ())
#define GVC_MIXER_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSource))
#define GVC_MIXER_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass))
#define GVC_IS_MIXER_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE))
#define GVC_IS_MIXER_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE))
#define GVC_MIXER_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass))

typedef struct GvcMixerSourcePrivate GvcMixerSourcePrivate;

typedef struct
{
        GvcMixerStream       parent;
        GvcMixerSourcePrivate *priv;
} GvcMixerSource;

typedef struct
{
        GvcMixerStreamClass parent_class;
} GvcMixerSourceClass;

GType               gvc_mixer_source_get_type            (void);

GvcMixerStream *    gvc_mixer_source_new               (pa_context    *context,
                                                        guint          index,
                                                        GvcChannelMap *channel_map);

G_END_DECLS

#endif /* __GVC_MIXER_SOURCE_H */
0707010000001A000081A400000000000000000000000166437C9400000460000000000000000000000000000000000000003F00000000libgnome-volume-control-0.gitmodule/gvc-mixer-stream-private.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_STREAM_PRIVATE_H
#define __GVC_MIXER_STREAM_PRIVATE_H

#include <glib-object.h>

#include "gvc-channel-map.h"

G_BEGIN_DECLS

pa_context *        gvc_mixer_stream_get_pa_context  (GvcMixerStream *stream);

G_END_DECLS

#endif /* __GVC_MIXER_STREAM_PRIVATE_H */
0707010000001B000081A400000000000000000000000166437C9400008FAB000000000000000000000000000000000000003700000000libgnome-volume-control-0.gitmodule/gvc-mixer-stream.c/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 William Jon McCann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include <glib.h>
#include <glib/gi18n-lib.h>

#include <pulse/pulseaudio.h>

#include "gvc-mixer-stream.h"
#include "gvc-mixer-stream-private.h"
#include "gvc-channel-map-private.h"
#include "gvc-enum-types.h"

static guint32 stream_serial = 1;

struct GvcMixerStreamPrivate
{
        pa_context    *pa_context;
        guint          id;
        guint          index;
        guint          card_index;
        GvcChannelMap *channel_map;
        char          *name;
        char          *description;
        char          *application_id;
        char          *icon_name;
        char          *form_factor;
        char          *sysfs_path;
        gboolean       is_muted;
        gboolean       can_decibel;
        gboolean       is_event_stream;
        gboolean       is_virtual;
        pa_volume_t    base_volume;
        pa_operation  *change_volume_op;
        char          *port;
        char          *human_port;
        GList         *ports;
        GvcMixerStreamState state;
};

enum
{
        PROP_0,
        PROP_ID,
        PROP_PA_CONTEXT,
        PROP_CHANNEL_MAP,
        PROP_INDEX,
        PROP_NAME,
        PROP_DESCRIPTION,
        PROP_APPLICATION_ID,
        PROP_ICON_NAME,
        PROP_FORM_FACTOR,
        PROP_SYSFS_PATH,
        PROP_VOLUME,
        PROP_DECIBEL,
        PROP_IS_MUTED,
        PROP_CAN_DECIBEL,
        PROP_IS_EVENT_STREAM,
        PROP_IS_VIRTUAL,
        PROP_CARD_INDEX,
        PROP_PORT,
        PROP_STATE,
        N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };

static void     gvc_mixer_stream_finalize   (GObject            *object);

G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GvcMixerStream, gvc_mixer_stream, G_TYPE_OBJECT)

static void
free_port (GvcMixerStreamPort *p)
{
        g_free (p->port);
        g_free (p->human_port);
        g_slice_free (GvcMixerStreamPort, p);
}

static GvcMixerStreamPort *
dup_port (GvcMixerStreamPort *p)
{
        GvcMixerStreamPort *m;

        m = g_slice_new (GvcMixerStreamPort);

        *m = *p;
        m->port = g_strdup (p->port);
        m->human_port = g_strdup (p->human_port);

        return m;
}

G_DEFINE_BOXED_TYPE (GvcMixerStreamPort, gvc_mixer_stream_port, dup_port, free_port)

static guint32
get_next_stream_serial (void)
{
        guint32 serial;

        serial = stream_serial++;

        if ((gint32)stream_serial < 0) {
                stream_serial = 1;
        }

        return serial;
}

pa_context *
gvc_mixer_stream_get_pa_context (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
        return stream->priv->pa_context;
}

guint
gvc_mixer_stream_get_index (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
        return stream->priv->index;
}

guint
gvc_mixer_stream_get_id (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
        return stream->priv->id;
}

const GvcChannelMap *
gvc_mixer_stream_get_channel_map (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->channel_map;
}

/**
 * gvc_mixer_stream_get_volume:
 * @stream:
 *
 * Returns: (type guint32):
 */
pa_volume_t
gvc_mixer_stream_get_volume (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);

        return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME];
}

gdouble
gvc_mixer_stream_get_decibel (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);

        return pa_sw_volume_to_dB(
                        (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]);
}

/**
 * gvc_mixer_stream_set_volume:
 * @stream:
 * @volume: (type guint32):
 *
 * Returns:
 */
gboolean
gvc_mixer_stream_set_volume (GvcMixerStream *stream,
                              pa_volume_t     volume)
{
        pa_cvolume cv;

        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
        pa_cvolume_scale(&cv, volume);

        if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
                return TRUE;
        }

        return FALSE;
}

gboolean
gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
                              gdouble         db)
{
        pa_cvolume cv;

        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map);
        pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db));

        if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) {
                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
        }

        return TRUE;
}

gboolean
gvc_mixer_stream_get_is_muted  (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        return stream->priv->is_muted;
}

gboolean
gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        return stream->priv->can_decibel;
}

gboolean
gvc_mixer_stream_set_is_muted  (GvcMixerStream *stream,
                                gboolean        is_muted)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (is_muted != stream->priv->is_muted) {
                stream->priv->is_muted = is_muted;
                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_MUTED]);
        }

        return TRUE;
}

gboolean
gvc_mixer_stream_set_can_decibel  (GvcMixerStream *stream,
                                   gboolean        can_decibel)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (can_decibel != stream->priv->can_decibel) {
                stream->priv->can_decibel = can_decibel;
                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CAN_DECIBEL]);
        }

        return TRUE;
}

const char *
gvc_mixer_stream_get_name (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->name;
}

const char *
gvc_mixer_stream_get_description (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->description;
}

gboolean
gvc_mixer_stream_set_name (GvcMixerStream *stream,
                           const char     *name)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->name);
        stream->priv->name = g_strdup (name);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_NAME]);

        return TRUE;
}

gboolean
gvc_mixer_stream_set_description (GvcMixerStream *stream,
                                  const char     *description)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->description);
        stream->priv->description = g_strdup (description);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_DESCRIPTION]);

        return TRUE;
}

gboolean
gvc_mixer_stream_is_event_stream (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        return stream->priv->is_event_stream;
}

gboolean
gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
                                      gboolean is_event_stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        stream->priv->is_event_stream = is_event_stream;
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_EVENT_STREAM]);

        return TRUE;
}

gboolean
gvc_mixer_stream_is_virtual (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        return stream->priv->is_virtual;
}

gboolean
gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream,
                                 gboolean is_virtual)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        stream->priv->is_virtual = is_virtual;
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_IS_VIRTUAL]);

        return TRUE;
}

const char *
gvc_mixer_stream_get_application_id (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->application_id;
}

gboolean
gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
                                     const char *application_id)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->application_id);
        stream->priv->application_id = g_strdup (application_id);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_APPLICATION_ID]);

        return TRUE;
}

static void
on_channel_map_volume_changed (GvcChannelMap  *channel_map,
                               gboolean        set,
                               GvcMixerStream *stream)
{
        if (set == TRUE)
                gvc_mixer_stream_push_volume (stream);

        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_VOLUME]);
}

static gboolean
gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
                                  GvcChannelMap  *channel_map)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (channel_map != NULL) {
                g_object_ref (channel_map);
        }

        if (stream->priv->channel_map != NULL) {
                g_signal_handlers_disconnect_by_func (stream->priv->channel_map,
                                                      on_channel_map_volume_changed,
                                                      stream);
                g_object_unref (stream->priv->channel_map);
        }

        stream->priv->channel_map = channel_map;

        if (stream->priv->channel_map != NULL) {
                g_signal_connect (stream->priv->channel_map,
                                  "volume-changed",
                                  G_CALLBACK (on_channel_map_volume_changed),
                                  stream);

                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CHANNEL_MAP]);
        }

        return TRUE;
}

const char *
gvc_mixer_stream_get_icon_name (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->icon_name;
}

const char *
gvc_mixer_stream_get_form_factor (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->form_factor;
}

const char *
gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->sysfs_path;
}

/**
 * gvc_mixer_stream_get_gicon:
 * @stream: a #GvcMixerStream
 *
 * Returns: (transfer full): a new #GIcon
 */
GIcon *
gvc_mixer_stream_get_gicon (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        if (stream->priv->icon_name == NULL)
                return NULL;
        return g_themed_icon_new_with_default_fallbacks (stream->priv->icon_name);
}

gboolean
gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
                                const char     *icon_name)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->icon_name);
        stream->priv->icon_name = g_strdup (icon_name);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_ICON_NAME]);

        return TRUE;
}

gboolean
gvc_mixer_stream_set_form_factor (GvcMixerStream *stream,
                                  const char     *form_factor)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->form_factor);
        stream->priv->form_factor = g_strdup (form_factor);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_FORM_FACTOR]);

        return TRUE;
}

gboolean
gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream,
                                 const char     *sysfs_path)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        g_free (stream->priv->sysfs_path);
        stream->priv->sysfs_path = g_strdup (sysfs_path);
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_SYSFS_PATH]);

        return TRUE;
}

/**
 * gvc_mixer_stream_get_base_volume:
 * @stream:
 *
 * Returns: (type guint32):
 */
pa_volume_t
gvc_mixer_stream_get_base_volume (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);

        return stream->priv->base_volume;
}

/**
 * gvc_mixer_stream_set_base_volume:
 * @stream:
 * @base_volume: (type guint32):
 *
 * Returns:
 */
gboolean
gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
                                  pa_volume_t base_volume)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        stream->priv->base_volume = base_volume;

        return TRUE;
}

const GvcMixerStreamPort *
gvc_mixer_stream_get_port (GvcMixerStream *stream)
{
        GList *l;

        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        g_return_val_if_fail (stream->priv->ports != NULL, NULL);

        for (l = stream->priv->ports; l != NULL; l = l->next) {
                GvcMixerStreamPort *p = l->data;
                if (g_strcmp0 (stream->priv->port, p->port) == 0) {
                        return p;
                }
        }

        g_assert_not_reached ();

        return NULL;
}

gboolean
gvc_mixer_stream_set_port (GvcMixerStream *stream,
                           const char     *port)
{
        GList *l;

        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        g_return_val_if_fail (stream->priv->ports != NULL, FALSE);

        g_free (stream->priv->port);
        stream->priv->port = g_strdup (port);

        g_free (stream->priv->human_port);
        stream->priv->human_port = NULL;

        for (l = stream->priv->ports; l != NULL; l = l->next) {
                GvcMixerStreamPort *p = l->data;
                if (g_str_equal (stream->priv->port, p->port)) {
                        stream->priv->human_port = g_strdup (p->human_port);
                        break;
                }
        }

        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_PORT]);

        return TRUE;
}

gboolean
gvc_mixer_stream_change_port (GvcMixerStream *stream,
                              const char     *port)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port);
}

/**
 * gvc_mixer_stream_get_ports:
 *
 * Return value: (transfer none) (element-type GvcMixerStreamPort):
 */
const GList *
gvc_mixer_stream_get_ports (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
        return stream->priv->ports;
}

gboolean
gvc_mixer_stream_set_state (GvcMixerStream      *stream,
                            GvcMixerStreamState  state)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (stream->priv->state != state) {
                stream->priv->state = state;
                g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_STATE]);
        }

        return TRUE;
}

GvcMixerStreamState
gvc_mixer_stream_get_state (GvcMixerStream      *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), GVC_STREAM_STATE_INVALID);
        return stream->priv->state;
}

static int
sort_ports (GvcMixerStreamPort *a,
            GvcMixerStreamPort *b)
{
        if (a->priority == b->priority)
                return 0;
        if (a->priority > b->priority)
                return 1;
        return -1;
}

/**
 * gvc_mixer_stream_set_ports:
 * @ports: (transfer full) (element-type GvcMixerStreamPort):
 */
gboolean
gvc_mixer_stream_set_ports (GvcMixerStream *stream,
                            GList          *ports)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        g_return_val_if_fail (stream->priv->ports == NULL, FALSE);

        stream->priv->ports = g_list_sort (ports, (GCompareFunc) sort_ports);

        return TRUE;
}

guint
gvc_mixer_stream_get_card_index (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), PA_INVALID_INDEX);
        return stream->priv->card_index;
}

gboolean
gvc_mixer_stream_set_card_index (GvcMixerStream *stream,
                                 guint           card_index)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        stream->priv->card_index = card_index;
        g_object_notify_by_pspec (G_OBJECT (stream), obj_props[PROP_CARD_INDEX]);

        return TRUE;
}

static void
gvc_mixer_stream_set_property (GObject       *object,
                               guint          prop_id,
                               const GValue  *value,
                               GParamSpec    *pspec)
{
        GvcMixerStream *self = GVC_MIXER_STREAM (object);

        switch (prop_id) {
        case PROP_PA_CONTEXT:
                self->priv->pa_context = g_value_get_pointer (value);
                break;
        case PROP_INDEX:
                self->priv->index = g_value_get_ulong (value);
                break;
        case PROP_ID:
                self->priv->id = g_value_get_ulong (value);
                break;
        case PROP_CHANNEL_MAP:
                gvc_mixer_stream_set_channel_map (self, g_value_get_object (value));
                break;
        case PROP_NAME:
                gvc_mixer_stream_set_name (self, g_value_get_string (value));
                break;
        case PROP_DESCRIPTION:
                gvc_mixer_stream_set_description (self, g_value_get_string (value));
                break;
        case PROP_APPLICATION_ID:
                gvc_mixer_stream_set_application_id (self, g_value_get_string (value));
                break;
        case PROP_ICON_NAME:
                gvc_mixer_stream_set_icon_name (self, g_value_get_string (value));
                break;
        case PROP_FORM_FACTOR:
                gvc_mixer_stream_set_form_factor (self, g_value_get_string (value));
                break;
	case PROP_SYSFS_PATH:
		gvc_mixer_stream_set_sysfs_path (self, g_value_get_string (value));
		break;
        case PROP_VOLUME:
                gvc_mixer_stream_set_volume (self, g_value_get_ulong (value));
                break;
        case PROP_DECIBEL:
                gvc_mixer_stream_set_decibel (self, g_value_get_double (value));
                break;
        case PROP_IS_MUTED:
                gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value));
                break;
        case PROP_IS_EVENT_STREAM:
                gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value));
                break;
        case PROP_IS_VIRTUAL:
                gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value));
                break;
        case PROP_CAN_DECIBEL:
                gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value));
                break;
        case PROP_PORT:
                gvc_mixer_stream_set_port (self, g_value_get_string (value));
                break;
        case PROP_STATE:
                gvc_mixer_stream_set_state (self, g_value_get_enum (value));
                break;
        case PROP_CARD_INDEX:
                self->priv->card_index = g_value_get_long (value);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void
gvc_mixer_stream_get_property (GObject     *object,
                               guint        prop_id,
                               GValue      *value,
                               GParamSpec  *pspec)
{
        GvcMixerStream *self = GVC_MIXER_STREAM (object);

        switch (prop_id) {
        case PROP_PA_CONTEXT:
                g_value_set_pointer (value, self->priv->pa_context);
                break;
        case PROP_INDEX:
                g_value_set_ulong (value, self->priv->index);
                break;
        case PROP_ID:
                g_value_set_ulong (value, self->priv->id);
                break;
        case PROP_CHANNEL_MAP:
                g_value_set_object (value, self->priv->channel_map);
                break;
        case PROP_NAME:
                g_value_set_string (value, self->priv->name);
                break;
        case PROP_DESCRIPTION:
                g_value_set_string (value, self->priv->description);
                break;
        case PROP_APPLICATION_ID:
                g_value_set_string (value, self->priv->application_id);
                break;
        case PROP_ICON_NAME:
                g_value_set_string (value, self->priv->icon_name);
                break;
        case PROP_FORM_FACTOR:
                g_value_set_string (value, self->priv->form_factor);
                break;
	case PROP_SYSFS_PATH:
		g_value_set_string (value, self->priv->sysfs_path);
		break;
        case PROP_VOLUME:
                g_value_set_ulong (value,
                                   pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)));
                break;
        case PROP_DECIBEL:
                g_value_set_double (value,
                                    pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))));
                break;
        case PROP_IS_MUTED:
                g_value_set_boolean (value, self->priv->is_muted);
                break;
        case PROP_IS_EVENT_STREAM:
                g_value_set_boolean (value, self->priv->is_event_stream);
                break;
        case PROP_IS_VIRTUAL:
                g_value_set_boolean (value, self->priv->is_virtual);
                break;
        case PROP_CAN_DECIBEL:
                g_value_set_boolean (value, self->priv->can_decibel);
                break;
        case PROP_PORT:
                g_value_set_string (value, self->priv->port);
                break;
        case PROP_STATE:
                g_value_set_enum (value, self->priv->state);
                break;
        case PROP_CARD_INDEX:
                g_value_set_long (value, self->priv->card_index);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static GObject *
gvc_mixer_stream_constructor (GType                  type,
                              guint                  n_construct_properties,
                              GObjectConstructParam *construct_params)
{
        GObject       *object;
        GvcMixerStream *self;

        object = G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->constructor (type, n_construct_properties, construct_params);

        self = GVC_MIXER_STREAM (object);

        self->priv->id = get_next_stream_serial ();

        return object;
}

static gboolean
gvc_mixer_stream_real_change_port (GvcMixerStream *stream,
                                   const char     *port)
{
        return FALSE;
}

static gboolean
gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op)
{
        return FALSE;
}

static gboolean
gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
                                       gboolean        is_muted)
{
        return FALSE;
}

gboolean
gvc_mixer_stream_push_volume (GvcMixerStream *stream)
{
        pa_operation *op;
        gboolean ret;

        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (stream->priv->is_event_stream != FALSE)
                return TRUE;

        g_debug ("Pushing new volume to stream '%s' (%s)",
                 stream->priv->description, stream->priv->name);

        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op);
        if (ret) {
                if (stream->priv->change_volume_op != NULL)
                        pa_operation_unref (stream->priv->change_volume_op);
                stream->priv->change_volume_op = op;
        }
        return ret;
}

gboolean
gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
                                  gboolean        is_muted)
{
        gboolean ret;
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_is_muted (stream, is_muted);
        return ret;
}

gboolean
gvc_mixer_stream_is_running (GvcMixerStream *stream)
{
        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);

        if (stream->priv->change_volume_op == NULL)
                return FALSE;

        if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING))
                return TRUE;

        pa_operation_unref(stream->priv->change_volume_op);
        stream->priv->change_volume_op = NULL;

        return FALSE;
}

static void
gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
{
        GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);

        gobject_class->constructor = gvc_mixer_stream_constructor;
        gobject_class->finalize = gvc_mixer_stream_finalize;
        gobject_class->set_property = gvc_mixer_stream_set_property;
        gobject_class->get_property = gvc_mixer_stream_get_property;

        klass->push_volume = gvc_mixer_stream_real_push_volume;
        klass->change_port = gvc_mixer_stream_real_change_port;
        klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;

        obj_props[PROP_INDEX] = g_param_spec_ulong ("index",
                                                    "Index",
                                                    "The index for this stream",
                                                    0, G_MAXULONG, 0,
                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_ID] = g_param_spec_ulong ("id",
                                                 "id",
                                                 "The id for this stream",
                                                 0, G_MAXULONG, 0,
                                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_CHANNEL_MAP] = g_param_spec_object ("channel-map",
                                                           "channel map",
                                                           "The channel map for this stream",
                                                           GVC_TYPE_CHANNEL_MAP,
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_PA_CONTEXT] = g_param_spec_pointer ("pa-context",
                                                           "PulseAudio context",
                                                           "The PulseAudio context for this stream",
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_VOLUME] = g_param_spec_ulong ("volume",
                                                     "Volume",
                                                     "The volume for this stream",
                                                     0, G_MAXULONG, 0,
                                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_DECIBEL] = g_param_spec_double ("decibel",
                                                       "Decibel",
                                                       "The decibel level for this stream",
                                                       -G_MAXDOUBLE, G_MAXDOUBLE, 0,
                                                       G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_NAME] = g_param_spec_string ("name",
                                                    "Name",
                                                    "Name to display for this stream",
                                                    NULL,
                                                    G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_DESCRIPTION] = g_param_spec_string ("description",
                                                           "Description",
                                                           "Description to display for this stream",
                                                           NULL,
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_APPLICATION_ID] = g_param_spec_string ("application-id",
                                                              "Application identifier",
                                                              "Application identifier for this stream",
                                                              NULL,
                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
                                                         "Icon Name",
                                                         "Name of icon to display for this stream",
                                                         NULL,
                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_FORM_FACTOR] = g_param_spec_string ("form-factor",
                                                           "Form Factor",
                                                           "Device form factor for this stream, as reported by PulseAudio",
                                                           NULL,
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_SYSFS_PATH] = g_param_spec_string ("sysfs-path",
                                                          "Sysfs path",
                                                          "Sysfs path for the device associated with this stream",
                                                          NULL,
                                                          G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_IS_MUTED] = g_param_spec_boolean ("is-muted",
                                                         "is muted",
                                                         "Whether stream is muted",
                                                         FALSE,
                                                         G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_CAN_DECIBEL] = g_param_spec_boolean ("can-decibel",
                                                            "can decibel",
                                                            "Whether stream volume can be converted to decibel units",
                                                            FALSE,
                                                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_IS_EVENT_STREAM] = g_param_spec_boolean ("is-event-stream",
                                                                "is event stream",
                                                                "Whether stream's role is to play an event",
                                                                FALSE,
                                                                G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_IS_VIRTUAL] = g_param_spec_boolean ("is-virtual",
                                                           "is virtual stream",
                                                           "Whether the stream is virtual",
                                                           FALSE,
                                                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_PORT] = g_param_spec_string ("port",
                                                    "Port",
                                                    "The name of the current port for this stream",
                                                    NULL,
                                                    G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_STATE] = g_param_spec_enum ("state",
                                                   "State",
                                                   "The current state of this stream",
                                                   GVC_TYPE_MIXER_STREAM_STATE,
                                                   GVC_STREAM_STATE_INVALID,
                                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
        obj_props[PROP_CARD_INDEX] = g_param_spec_long ("card-index",
                                                        "Card index",
                                                        "The index of the card for this stream",
                                                        PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX,
                                                        G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
}

static void
gvc_mixer_stream_init (GvcMixerStream *stream)
{
        stream->priv = gvc_mixer_stream_get_instance_private (stream);
}

static void
gvc_mixer_stream_finalize (GObject *object)
{
        GvcMixerStream *mixer_stream;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_IS_MIXER_STREAM (object));

        mixer_stream = GVC_MIXER_STREAM (object);

        g_return_if_fail (mixer_stream->priv != NULL);

        g_object_unref (mixer_stream->priv->channel_map);
        mixer_stream->priv->channel_map = NULL;

        g_free (mixer_stream->priv->name);
        mixer_stream->priv->name = NULL;

        g_free (mixer_stream->priv->description);
        mixer_stream->priv->description = NULL;

        g_free (mixer_stream->priv->application_id);
        mixer_stream->priv->application_id = NULL;

        g_free (mixer_stream->priv->icon_name);
        mixer_stream->priv->icon_name = NULL;

        g_free (mixer_stream->priv->form_factor);
        mixer_stream->priv->form_factor = NULL;

        g_free (mixer_stream->priv->sysfs_path);
        mixer_stream->priv->sysfs_path = NULL;

        g_free (mixer_stream->priv->port);
        mixer_stream->priv->port = NULL;

        g_free (mixer_stream->priv->human_port);
        mixer_stream->priv->human_port = NULL;

        g_list_free_full (mixer_stream->priv->ports, (GDestroyNotify) free_port);
        mixer_stream->priv->ports = NULL;

       if (mixer_stream->priv->change_volume_op) {
               pa_operation_unref(mixer_stream->priv->change_volume_op);
               mixer_stream->priv->change_volume_op = NULL;
       }

        G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object);
}
0707010000001C000081A400000000000000000000000166437C9400001DDF000000000000000000000000000000000000003700000000libgnome-volume-control-0.gitmodule/gvc-mixer-stream.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_MIXER_STREAM_H
#define __GVC_MIXER_STREAM_H

#include <glib-object.h>
#include "gvc-pulseaudio-fake.h"
#include "gvc-channel-map.h"
#include <gio/gio.h>

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_STREAM         (gvc_mixer_stream_get_type ())
#define GVC_MIXER_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStream))
#define GVC_MIXER_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))
#define GVC_IS_MIXER_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_STREAM))
#define GVC_IS_MIXER_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_STREAM))
#define GVC_MIXER_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))

typedef struct GvcMixerStreamPrivate GvcMixerStreamPrivate;

typedef struct
{
        GObject                parent;
        GvcMixerStreamPrivate *priv;
} GvcMixerStream;

typedef struct
{
        GObjectClass           parent_class;

        /* vtable */
        gboolean (*push_volume)     (GvcMixerStream *stream,
                                     gpointer *operation);
        gboolean (*change_is_muted) (GvcMixerStream *stream,
                                     gboolean        is_muted);
        gboolean (*change_port)     (GvcMixerStream *stream,
                                     const char     *port);
} GvcMixerStreamClass;

typedef struct
{
        char *port;
        char *human_port;
        guint priority;
        gboolean available;
} GvcMixerStreamPort;

typedef enum
{
        GVC_STREAM_STATE_INVALID,
        GVC_STREAM_STATE_RUNNING,
        GVC_STREAM_STATE_IDLE,
        GVC_STREAM_STATE_SUSPENDED
} GvcMixerStreamState;

GType               gvc_mixer_stream_port_get_type   (void) G_GNUC_CONST;
GType               gvc_mixer_stream_get_type        (void) G_GNUC_CONST;

guint               gvc_mixer_stream_get_index       (GvcMixerStream *stream);
guint               gvc_mixer_stream_get_id          (GvcMixerStream *stream);
const GvcChannelMap *gvc_mixer_stream_get_channel_map(GvcMixerStream *stream);
const GvcMixerStreamPort *gvc_mixer_stream_get_port  (GvcMixerStream *stream);
const GList *       gvc_mixer_stream_get_ports       (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_change_port     (GvcMixerStream *stream,
                                                      const char     *port);

pa_volume_t         gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
gdouble             gvc_mixer_stream_get_decibel     (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_push_volume     (GvcMixerStream *stream);
pa_volume_t         gvc_mixer_stream_get_base_volume (GvcMixerStream *stream);

gboolean            gvc_mixer_stream_get_is_muted    (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
                                                      gboolean        is_muted);
gboolean            gvc_mixer_stream_is_running      (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_name        (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_icon_name   (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_form_factor (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_sysfs_path  (GvcMixerStream *stream);
GIcon *             gvc_mixer_stream_get_gicon       (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_description (GvcMixerStream *stream);
const char *        gvc_mixer_stream_get_application_id (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_is_event_stream (GvcMixerStream *stream);
gboolean            gvc_mixer_stream_is_virtual      (GvcMixerStream *stream);
guint               gvc_mixer_stream_get_card_index  (GvcMixerStream *stream);
GvcMixerStreamState gvc_mixer_stream_get_state       (GvcMixerStream *stream);

/* private */
gboolean            gvc_mixer_stream_set_volume      (GvcMixerStream *stream,
                                                      pa_volume_t     volume);
gboolean            gvc_mixer_stream_set_decibel     (GvcMixerStream *stream,
                                                      gdouble         db);
gboolean            gvc_mixer_stream_set_is_muted    (GvcMixerStream *stream,
                                                      gboolean        is_muted);
gboolean            gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream,
                                                      gboolean        can_decibel);
gboolean            gvc_mixer_stream_set_name        (GvcMixerStream *stream,
                                                      const char     *name);
gboolean            gvc_mixer_stream_set_description (GvcMixerStream *stream,
                                                      const char     *description);
gboolean            gvc_mixer_stream_set_icon_name   (GvcMixerStream *stream,
                                                      const char     *name);
gboolean            gvc_mixer_stream_set_form_factor (GvcMixerStream *stream,
                                                      const char     *form_factor);
gboolean            gvc_mixer_stream_set_sysfs_path  (GvcMixerStream *stream,
                                                      const char     *sysfs_path);
gboolean            gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream,
                                                          gboolean is_event_stream);
gboolean            gvc_mixer_stream_set_is_virtual  (GvcMixerStream *stream,
                                                      gboolean is_event_stream);
gboolean            gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
                                                         const char *application_id);
gboolean            gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
                                                      pa_volume_t     base_volume);
gboolean            gvc_mixer_stream_set_port        (GvcMixerStream *stream,
                                                      const char     *port);
gboolean            gvc_mixer_stream_set_ports       (GvcMixerStream *stream,
                                                      GList          *ports);
gboolean            gvc_mixer_stream_set_card_index  (GvcMixerStream *stream,
                                                      guint           card_index);
gboolean            gvc_mixer_stream_set_state       (GvcMixerStream      *stream,
                                                      GvcMixerStreamState  state);

G_END_DECLS

#endif /* __GVC_MIXER_STREAM_H */
0707010000001D000081A400000000000000000000000166437C9400006C11000000000000000000000000000000000000003A00000000libgnome-volume-control-0.gitmodule/gvc-mixer-ui-device.c/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * gvc-mixer-ui-device.c
 * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
 * Copyright (C) 2012 David Henningsson, Canonical Ltd. <david.henningsson@canonical.com>
 *
 * gvc-mixer-ui-device.c is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * gvc-mixer-ui-device.c is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

#include "gvc-mixer-ui-device.h"
#include "gvc-mixer-card.h"

struct GvcMixerUIDevicePrivate
{
        gchar                      *first_line_desc;
        gchar                      *second_line_desc;

        GvcMixerCard               *card;
        gchar                      *port_name;
        char                       *icon_name; 
        guint                       stream_id;
        guint                       id;
        gboolean                    port_available;

        /* These two lists contain pointers to GvcMixerCardProfile objects. Those objects are owned by GvcMixerCard. *
         * TODO: Do we want to add a weak reference to the GvcMixerCard for this reason? */
        GList                      *supported_profiles; /* all profiles supported by this port.*/
        GList                      *profiles; /* profiles to be added to combobox, subset of supported_profiles. */
        GvcMixerUIDeviceDirection   type;
        gboolean                    disable_profile_swapping;
        gchar                      *user_preferred_profile;
};

enum
{
        PROP_0,
        PROP_DESC_LINE_1,
        PROP_DESC_LINE_2,
        PROP_CARD,
        PROP_PORT_NAME,
        PROP_STREAM_ID,
        PROP_UI_DEVICE_TYPE,
        PROP_PORT_AVAILABLE,
        PROP_ICON_NAME,
        N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };

static void     gvc_mixer_ui_device_finalize   (GObject               *object);

static void     gvc_mixer_ui_device_set_icon_name (GvcMixerUIDevice *device,
                                                   const char       *icon_name);

G_DEFINE_TYPE_WITH_PRIVATE (GvcMixerUIDevice, gvc_mixer_ui_device, G_TYPE_OBJECT)

static guint32
get_next_output_serial (void)
{
        static guint32 output_serial = 1;
        guint32 serial;

        serial = output_serial++;

        if ((gint32)output_serial < 0)
                output_serial = 1;

        return serial;
}

static void
gvc_mixer_ui_device_get_property  (GObject       *object,
                                   guint         property_id,
                                   GValue        *value,
                                   GParamSpec    *pspec)
{
        GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object);

        switch (property_id) {
        case PROP_DESC_LINE_1:
                g_value_set_string (value, self->priv->first_line_desc);
                break;
        case PROP_DESC_LINE_2:
                g_value_set_string (value, self->priv->second_line_desc);
                break;
        case PROP_CARD:
                g_value_set_pointer (value, self->priv->card);
                break;
        case PROP_PORT_NAME:
                g_value_set_string (value, self->priv->port_name);
                break;
        case PROP_STREAM_ID:
                g_value_set_uint (value, self->priv->stream_id);
                break;
        case PROP_UI_DEVICE_TYPE:
                g_value_set_uint (value, (guint)self->priv->type);
                break;
        case PROP_PORT_AVAILABLE:
                g_value_set_boolean (value, self->priv->port_available);
                break;
        case PROP_ICON_NAME:
                g_value_set_string (value, gvc_mixer_ui_device_get_icon_name (self));
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                break;
        }
}

static void
gvc_mixer_ui_device_set_property  (GObject      *object,
                                   guint         property_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
        GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object);

        switch (property_id) {
        case PROP_DESC_LINE_1:
                g_free (self->priv->first_line_desc);
                self->priv->first_line_desc = g_value_dup_string (value);
                g_debug ("gvc-mixer-output-set-property - 1st line: %s",
                         self->priv->first_line_desc);
                break;
        case PROP_DESC_LINE_2:
                g_free (self->priv->second_line_desc);
                self->priv->second_line_desc = g_value_dup_string (value);
                g_debug ("gvc-mixer-output-set-property - 2nd line: %s",
                         self->priv->second_line_desc);
                break;
        case PROP_CARD:
                self->priv->card = g_value_get_pointer (value);
                g_debug ("gvc-mixer-output-set-property - card: %p",
                         self->priv->card);
                break;
        case PROP_PORT_NAME:
                g_free (self->priv->port_name);
                self->priv->port_name = g_value_dup_string (value);
                g_debug ("gvc-mixer-output-set-property - card port name: %s",
                         self->priv->port_name);
                break;
        case PROP_STREAM_ID:
                self->priv->stream_id = g_value_get_uint (value);
                g_debug ("gvc-mixer-output-set-property - sink/source id: %i",
                         self->priv->stream_id);
                break;
        case PROP_UI_DEVICE_TYPE:
                self->priv->type = (GvcMixerUIDeviceDirection) g_value_get_uint (value);
                g_debug ("gvc-mixer-output-set-property - device type: %s",
                         self->priv->type == UIDeviceInput ? "input" : "output");
                break;
        case PROP_PORT_AVAILABLE:
                g_debug ("gvc-mixer-output-set-property - old port available %i, value passed in %i",
                         self->priv->port_available, g_value_get_boolean (value));
                self->priv->port_available = g_value_get_boolean (value);
                break;
        case PROP_ICON_NAME:
                gvc_mixer_ui_device_set_icon_name (self, g_value_get_string (value));
                g_debug ("gvc-mixer-output-set-property - icon name: %s",
                         self->priv->icon_name);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                break;
        }
}

static GObject *
gvc_mixer_ui_device_constructor (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_params)
{
        GObject           *object;
        GvcMixerUIDevice  *self;

        object = G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->constructor (type, n_construct_properties, construct_params);

        self = GVC_MIXER_UI_DEVICE (object);
        self->priv->id = get_next_output_serial ();
        self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID;
        return object;
}

static void
gvc_mixer_ui_device_init (GvcMixerUIDevice *device)
{
        device->priv = gvc_mixer_ui_device_get_instance_private (device);
}

static void
gvc_mixer_ui_device_dispose (GObject *object)
{
        GvcMixerUIDevice *device;

        g_return_if_fail (object != NULL);
        g_return_if_fail (GVC_MIXER_UI_DEVICE (object));

        device = GVC_MIXER_UI_DEVICE (object);

        g_clear_pointer (&device->priv->port_name, g_free);
        g_clear_pointer (&device->priv->icon_name, g_free);
        g_clear_pointer (&device->priv->first_line_desc, g_free);
        g_clear_pointer (&device->priv->second_line_desc, g_free);
        g_clear_pointer (&device->priv->profiles, g_list_free);
        g_clear_pointer (&device->priv->supported_profiles, g_list_free);
        g_clear_pointer (&device->priv->user_preferred_profile, g_free);

        G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->dispose (object);
}

static void
gvc_mixer_ui_device_finalize (GObject *object)
{
        G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->finalize (object);
}

static void
gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass)
{
        GObjectClass* object_class = G_OBJECT_CLASS (klass);

        object_class->constructor = gvc_mixer_ui_device_constructor;
        object_class->dispose = gvc_mixer_ui_device_dispose;
        object_class->finalize = gvc_mixer_ui_device_finalize;
        object_class->set_property = gvc_mixer_ui_device_set_property;
        object_class->get_property = gvc_mixer_ui_device_get_property;

        obj_props[PROP_DESC_LINE_1] =
                g_param_spec_string ("description",
                                     "Description construct prop",
                                     "Set first line description",
                                     "no-name-set",
                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_DESC_LINE_2] =
                g_param_spec_string ("origin",
                                     "origin construct prop",
                                     "Set second line description name",
                                     "no-name-set",
                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_CARD] =
                g_param_spec_pointer ("card",
                                      "Card from pulse",
                                      "Set/Get card",
                                      G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_PORT_NAME] =
                g_param_spec_string ("port-name",
                                     "port-name construct prop",
                                     "Set port-name",
                                     NULL,
                                     G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_STREAM_ID] =
                g_param_spec_uint ("stream-id",
                                   "stream id assigned by gvc-stream",
                                   "Set/Get stream id",
                                   0,
                                   G_MAXUINT,
                                   GVC_MIXER_UI_DEVICE_INVALID,
                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_UI_DEVICE_TYPE] =
                g_param_spec_uint ("type",
                                   "ui-device type",
                                   "determine whether its an input and output",
                                   0, 1, 0,
                                   G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_PORT_AVAILABLE] =
                g_param_spec_boolean ("port-available",
                                      "available",
                                      "determine whether this port is available",
                                      FALSE,
                                      G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);

        obj_props[PROP_ICON_NAME] =
                g_param_spec_string ("icon-name",
                                     "Icon Name",
                                     "Name of icon to display for this card",
                                     NULL,
                                     G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS);

        g_object_class_install_properties (object_class, N_PROPS, obj_props);
}

/* Removes the part of the string that starts with skip_prefix
 * ie. corresponding to the other direction.
 * Normally either "input:" or "output:"
 *
 * Example: if given the input string "output:hdmi-stereo+input:analog-stereo" and
 * skip_prefix "input:", the resulting string is "output:hdmi-stereo".
 *
 * The returned string must be freed with g_free().
 */
static gchar *
get_profile_canonical_name (const gchar *profile_name, const gchar *skip_prefix)
{
        gchar *result = NULL;
        gchar **s;
        guint i;

        /* optimisation for the simple case. */
        if (strstr (profile_name, skip_prefix) == NULL)
                return g_strdup (profile_name);

        s = g_strsplit (profile_name, "+", 0);
        for (i = 0; i < g_strv_length (s); i++) {
                if (g_str_has_prefix (s[i], skip_prefix))
                        continue;
                if (result == NULL)
                        result = g_strdup (s[i]);
                else {
                        gchar *c = g_strdup_printf("%s+%s", result, s[i]);
                        g_free(result);
                        result = c;
                }
        }

        g_strfreev(s);

        if (!result)
                return g_strdup("off");

        return result;
}

const gchar *
gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device, const gchar *profile)
{
        const gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:";
        gchar *target_cname = get_profile_canonical_name (profile, skip_prefix);
        GList *l;
        gchar *result = NULL;

        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
        g_return_val_if_fail (profile != NULL, NULL);

        for (l = device->priv->profiles; l != NULL; l = l->next) {
                gchar *canonical_name;
                GvcMixerCardProfile* p = l->data;
                canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
                if (strcmp (canonical_name, target_cname) == 0)
                        result = p->profile;
                g_free (canonical_name);
        }

        g_free (target_cname);
        g_debug ("Matching profile for '%s' is '%s'", profile, result ? result : "(null)");
        return result;
}


static void
add_canonical_names_of_profiles (GvcMixerUIDevice *device,
                                 const GList      *in_profiles,
                                 GHashTable       *added_profiles,
                                 const gchar      *skip_prefix,
                                 gboolean          only_canonical)
{
        const GList *l;

        for (l = in_profiles; l != NULL; l = l->next) {
                gchar *canonical_name;
                GvcMixerCardProfile* p = l->data;

                canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
                g_debug ("The canonical name for '%s' is '%s'", p->profile, canonical_name);

                /* Have we already added the canonical version of this profile? */
                if (g_hash_table_contains (added_profiles, canonical_name)) {
                        g_free (canonical_name);
                        continue;
                }

                if (only_canonical && strcmp (p->profile, canonical_name) != 0) {
                        g_free (canonical_name);
                        continue;
                }

                g_free (canonical_name);

                /* https://bugzilla.gnome.org/show_bug.cgi?id=693654
                 * Don't add a profile that will make the UI device completely disappear */
                if (p->n_sinks == 0 && p->n_sources == 0)
                        continue;

                g_debug ("Adding profile to combobox: '%s' - '%s'", p->profile, p->human_profile);
                g_hash_table_insert (added_profiles, g_strdup (p->profile), p);
                device->priv->profiles = g_list_append (device->priv->profiles, p);
        }
}

/**
 * gvc_mixer_ui_device_set_profiles:
 * @in_profiles: (element-type Gvc.MixerCardProfile): a list of GvcMixerCardProfile
 *
 * Assigns value to
 *  - device->priv->profiles (profiles to be added to combobox)
 *  - device->priv->supported_profiles (all profiles of this port)
 *  - device->priv->disable_profile_swapping (whether to show the combobox)
 *
 * This method attempts to reduce the list of profiles visible to the user by figuring out
 * from the context of that device (whether it's an input or an output) what profiles
 * actually provide an alternative.
 *
 * It does this by the following.
 *  - It ignores off profiles.
 *  - It takes the canonical name of the profile. That name is what you get when you
 *    ignore the other direction.
 *  - In the first iteration, it only adds the names of canonical profiles - i e
 *    when the other side is turned off.
 *  - Normally the first iteration covers all cases, but sometimes (e g bluetooth)
 *    it doesn't, so add other profiles whose canonical name isn't already added
 *    in a second iteration.
 */
void
gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device,
                                  const GList      *in_profiles)
{
        GHashTable *added_profiles;
        const gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:";

        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));

        g_debug ("Set profiles for '%s'", gvc_mixer_ui_device_get_description(device));

        g_clear_pointer (&device->priv->supported_profiles, g_list_free);
        g_clear_pointer (&device->priv->profiles, g_list_free);

        if (in_profiles == NULL)
                return;

        device->priv->supported_profiles = g_list_copy ((GList*) in_profiles);

        added_profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

        /* Run two iterations: First, add profiles which are canonical themselves,
         * Second, add profiles for which the canonical name is not added already. */

        add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, TRUE);
        add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, FALSE);

        /* TODO: Consider adding the "Off" profile here */

        device->priv->disable_profile_swapping = g_hash_table_size (added_profiles) <= 1;
        g_hash_table_destroy (added_profiles);
}

/**
 * gvc_mixer_ui_device_get_best_profile:
 * @selected: (allow-none): The selected profile or its canonical name or %NULL for any profile
 * @current: The currently selected profile
 *
 * Returns: (transfer none): a profile name, valid as long as the UI device profiles are.
 */
const gchar *
gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device,
                                      const gchar      *selected,
                                      const gchar      *current)
{
        GList *candidates, *l;
        const gchar *result;
        const gchar *skip_prefix;
        gchar *canonical_name_selected;

        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);
        g_return_val_if_fail (current != NULL, NULL);

        if (device->priv->type == UIDeviceInput)
                skip_prefix = "output:";
        else
                skip_prefix = "input:";

        /* First make a list of profiles acceptable to switch to */
        canonical_name_selected = NULL;
        if (selected)
                canonical_name_selected = get_profile_canonical_name (selected, skip_prefix);

	candidates = NULL;
        for (l = device->priv->supported_profiles; l != NULL; l = l->next) {
                gchar *canonical_name;
                GvcMixerCardProfile* p = l->data;
                canonical_name = get_profile_canonical_name (p->profile, skip_prefix);
                if (!canonical_name_selected || strcmp (canonical_name, canonical_name_selected) == 0) {
                        candidates = g_list_append (candidates, p);
                        g_debug ("Candidate for profile switching: '%s'", p->profile);
                }
                g_free (canonical_name);
        }

        if (!candidates) {
                g_warning ("No suitable profile candidates for '%s'", selected ? selected : "(null)");
                g_free (canonical_name_selected);
                return current;
        }

        /* 1) Maybe we can skip profile switching altogether? */
        result = NULL;
        for (l = candidates; (result == NULL) && (l != NULL); l = l->next) {
                GvcMixerCardProfile* p = l->data;
                if (strcmp (current, p->profile) == 0)
                        result = p->profile;
        }

        /* 2) Try to keep the other side unchanged if possible */
        if (result == NULL) {
                guint prio = 0;
                const gchar *skip_prefix_reverse = device->priv->type == UIDeviceInput ? "input:" : "output:";
                gchar *current_reverse = get_profile_canonical_name (current, skip_prefix_reverse);
                for (l = candidates; l != NULL; l = l->next) {
                        gchar *p_reverse;
                        GvcMixerCardProfile* p = l->data;
                        p_reverse = get_profile_canonical_name (p->profile, skip_prefix_reverse);
                        g_debug ("Comparing '%s' (from '%s') with '%s', prio %d", p_reverse, p->profile, current_reverse, p->priority); 
                        if (strcmp (p_reverse, current_reverse) == 0 && (!result || p->priority > prio)) {
                                result = p->profile;
                                prio = p->priority;
                        }
                        g_free (p_reverse);
                }
                g_free (current_reverse);
        }

        /* 3) All right, let's just pick the profile with highest priority.
         * TODO: We could consider asking a GUI question if this stops streams
         * in the other direction */
        if (result == NULL) {
                guint prio = 0;
                for (l = candidates; l != NULL; l = l->next) {
                        GvcMixerCardProfile* p = l->data;
                        if ((p->priority > prio) || !result) {
                                result = p->profile;
                                prio = p->priority;
                        }
                }
        }

        g_list_free (candidates);
        g_free (canonical_name_selected);
        return result;
}

const gchar *
gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device)
{
        GvcMixerCardProfile *profile;

        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        if (device->priv->card == NULL) {
                g_warning ("Device did not have an appropriate card");
                return NULL;
        }

        profile = gvc_mixer_card_get_profile (device->priv->card);
        return gvc_mixer_ui_device_get_matching_profile (device, profile->profile);
}

gboolean
gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);

        return device->priv->disable_profile_swapping;
}

/**
 * gvc_mixer_ui_device_get_profiles:
 * @device:
 *
 * Returns: (transfer none) (element-type Gvc.MixerCardProfile):
 */
GList*
gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->profiles;
}

/**
 * gvc_mixer_ui_device_get_supported_profiles:
 * @device:
 *
 * Returns: (transfer none) (element-type Gvc.MixerCardProfile):
 */
GList*
gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->supported_profiles;
}

guint
gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0);

        return device->priv->id;
}

guint
gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0);

        return device->priv->stream_id;
}

void
gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *self)
{
        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (self));

        self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID;
}

const gchar *
gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->first_line_desc;
}

const char *
gvc_mixer_ui_device_get_icon_name (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        if (device->priv->icon_name)
                return device->priv->icon_name;

        if (device->priv->card)
                return gvc_mixer_card_get_icon_name (device->priv->card);

        return NULL;
}

static void
gvc_mixer_ui_device_set_icon_name (GvcMixerUIDevice *device,
                                   const char       *icon_name)
{
        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));

        g_free (device->priv->icon_name);
        device->priv->icon_name = g_strdup (icon_name);
        g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_ICON_NAME]);
}


/**
 * gvc_mixer_ui_device_get_gicon:
 * @device:
 *
 * Returns: (transfer full):
 */
GIcon *
gvc_mixer_ui_device_get_gicon (GvcMixerUIDevice *device)
{
        const char *icon_name;

        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        icon_name = gvc_mixer_ui_device_get_icon_name (device);

        if (icon_name != NULL)
                return g_themed_icon_new_with_default_fallbacks (icon_name);
        else
                return NULL;
}

const gchar *
gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->second_line_desc;
}

const gchar*
gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->user_preferred_profile;
}

const gchar *
gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device)
{
        GList *last;
        GvcMixerCardProfile *profile;

        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        last = g_list_last (device->priv->supported_profiles);
        profile = last->data;

        return profile->profile;
}

void
gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device,
                                                const gchar      *profile)
{
        g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device));
        g_return_if_fail (profile != NULL);

        g_free (device->priv->user_preferred_profile);
        device->priv->user_preferred_profile = g_strdup (profile);
}

const gchar *
gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL);

        return device->priv->port_name;
}

gboolean
gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);

        return (device->priv->port_name != NULL);
}

gboolean
gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device)
{
        g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE);

        return (device->priv->type == UIDeviceOutput);
}
0707010000001E000081A400000000000000000000000166437C9400001121000000000000000000000000000000000000003A00000000libgnome-volume-control-0.gitmodule/gvc-mixer-ui-device.h/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Copyright (C) Conor Curran 2011 <conor.curran@canonical.com>
 *
 * This is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * gvc-mixer-ui-device.h is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _GVC_MIXER_UI_DEVICE_H_
#define _GVC_MIXER_UI_DEVICE_H_

#include <glib-object.h>
#include <gio/gio.h>

G_BEGIN_DECLS

#define GVC_TYPE_MIXER_UI_DEVICE             (gvc_mixer_ui_device_get_type ())
#define GVC_MIXER_UI_DEVICE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevice))
#define GVC_MIXER_UI_DEVICE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass))
#define GVC_IS_MIXER_UI_DEVICE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GVC_TYPE_MIXER_UI_DEVICE))
#define GVC_IS_MIXER_UI_DEVICE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GVC_TYPE_MIXER_UI_DEVICE))
#define GVC_MIXER_UI_DEVICE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass))

#define GVC_MIXER_UI_DEVICE_INVALID          0

typedef struct GvcMixerUIDevicePrivate GvcMixerUIDevicePrivate;

typedef struct
{
        GObjectClass parent_class;
} GvcMixerUIDeviceClass;

typedef struct
{
        GObject parent_instance;
        GvcMixerUIDevicePrivate *priv;
} GvcMixerUIDevice;

typedef enum
{
        UIDeviceInput,
        UIDeviceOutput,
} GvcMixerUIDeviceDirection;

GType gvc_mixer_ui_device_get_type (void) G_GNUC_CONST;

guint          gvc_mixer_ui_device_get_id                      (GvcMixerUIDevice *device);
guint          gvc_mixer_ui_device_get_stream_id               (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_description             (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_icon_name               (GvcMixerUIDevice *device);
GIcon *        gvc_mixer_ui_device_get_gicon                   (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_origin                  (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_port                    (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_best_profile            (GvcMixerUIDevice *device,
                                                                const gchar      *selected,
                                                                const gchar      *current);
const gchar *  gvc_mixer_ui_device_get_active_profile          (GvcMixerUIDevice* device);
const gchar *  gvc_mixer_ui_device_get_matching_profile        (GvcMixerUIDevice *device,
                                                                const gchar      *profile);
const gchar *  gvc_mixer_ui_device_get_user_preferred_profile  (GvcMixerUIDevice *device);
const gchar *  gvc_mixer_ui_device_get_top_priority_profile    (GvcMixerUIDevice *device);
GList *        gvc_mixer_ui_device_get_profiles                (GvcMixerUIDevice *device);
GList *        gvc_mixer_ui_device_get_supported_profiles      (GvcMixerUIDevice *device);
gboolean       gvc_mixer_ui_device_should_profiles_be_hidden   (GvcMixerUIDevice *device);
void           gvc_mixer_ui_device_set_profiles                (GvcMixerUIDevice *device,
                                                                const GList      *in_profiles);
void           gvc_mixer_ui_device_set_user_preferred_profile  (GvcMixerUIDevice *device,
                                                                const gchar      *profile);
void           gvc_mixer_ui_device_invalidate_stream           (GvcMixerUIDevice *device);
gboolean       gvc_mixer_ui_device_has_ports                   (GvcMixerUIDevice *device);
gboolean       gvc_mixer_ui_device_is_output                   (GvcMixerUIDevice *device);

G_END_DECLS

#endif /* _GVC_MIXER_UI_DEVICE_H_ */
0707010000001F000081A400000000000000000000000166437C940000043B000000000000000000000000000000000000003A00000000libgnome-volume-control-0.gitmodule/gvc-pulseaudio-fake.h/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2008 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __GVC_PULSEAUDIO_FAKE_H
#define __GVC_PULSEAUDIO_FAKE_H

#ifndef PA_API_VERSION
#define pa_channel_position_t int
#define pa_volume_t guint32
#define pa_context gpointer
#endif /* PA_API_VERSION */

#endif /* __GVC_PULSEAUDIO_FAKE_H */
07070100000020000081A400000000000000000000000166437C94000004A6000000000000000000000000000000000000004100000000libgnome-volume-control-0.gitmodule/libgnome-volume-control.doap<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
         xmlns:foaf="http://xmlns.com/foaf/0.1/"
         xmlns:gnome="http://api.gnome.org/doap-extensions#"
         xmlns="http://usefulinc.com/ns/doap#">

  <name xml:lang="en">libgnome-volume-control</name>
  <shortdesc xml:lang="en">GObject layer for PulseAudio</shortdesc>
  <description>
    This library contains code to access PulseAudio using a GObject
    based library, shared between gnome-control-center, gnome-settings-daemon
    and gnome-shell. It is not API stable, and it is meant to be used
    as a submodule.
  </description>

  <!-- <category rdf:resource="http://api.gnome.org/doap-extensions#desktop" /> -->

  <maintainer>
    <foaf:Person>
      <foaf:name>Emmanuele Bassi</foaf:name>
      <foaf:mbox rdf:resource="mailto:ebassi@gnome.org"/>
      <gnome:userid>ebassi</gnome:userid>
    </foaf:Person>
  </maintainer>
  <author>
    <foaf:Person>
      <foaf:name>Bastien Nocera</foaf:name>
      <foaf:mbox rdf:resource="mailto:hadess@hadess.net" />
      <gnome:userid>hadess</gnome:userid>
    </foaf:Person>
  </author>
</Project>
07070100000021000081A400000000000000000000000166437C9400000D19000000000000000000000000000000000000003000000000libgnome-volume-control-0.gitmodule/meson.buildproject('gvc', 'c',
  meson_version: '>= 0.42.0',
  default_options: ['static=true']
)

assert(meson.is_subproject(), 'This project is only intended to be used as a subproject!')

gnome = import('gnome')

pkglibdir = get_option('pkglibdir')
pkgdatadir = get_option('pkgdatadir')

cdata = configuration_data()
cdata.set_quoted('GETTEXT_PACKAGE', get_option('package_name'))
cdata.set_quoted('PACKAGE_VERSION', get_option('package_version'))

libgvc_gir_headers = [
  'gvc-channel-map.h',
  'gvc-mixer-card.h',
  'gvc-mixer-control.h',
  'gvc-mixer-event-role.h',
  'gvc-mixer-sink.h',
  'gvc-mixer-sink-input.h',
  'gvc-mixer-source.h',
  'gvc-mixer-source-output.h',
  'gvc-mixer-stream.h',
  'gvc-mixer-ui-device.h'
]

libgvc_enums = gnome.mkenums_simple('gvc-enum-types',
  sources: libgvc_gir_headers
)

libgvc_gir_sources = [
  'gvc-channel-map.c',
  'gvc-mixer-card.c',
  'gvc-mixer-control.c',
  'gvc-mixer-event-role.c',
  'gvc-mixer-sink.c',
  'gvc-mixer-sink-input.c',
  'gvc-mixer-source.c',
  'gvc-mixer-source-output.c',
  'gvc-mixer-stream.c',
  'gvc-mixer-ui-device.c'
]

libgvc_no_gir_sources = [
  'gvc-mixer-card-private.h',
  'gvc-mixer-stream-private.h',
  'gvc-channel-map-private.h',
  'gvc-mixer-control-private.h',
  'gvc-pulseaudio-fake.h'
]

libgvc_deps = [
  dependency('gio-2.0'),
  dependency('gobject-2.0'),
  dependency('libpulse', version: '>= 12.99.3'),
  dependency('libpulse-mainloop-glib')
]

enable_alsa = get_option('alsa')
if enable_alsa
  libgvc_deps += dependency('alsa')
endif
cdata.set('HAVE_ALSA', enable_alsa)

enable_static = get_option('static')
enable_introspection = get_option('introspection')

assert(not enable_static or not enable_introspection, 'Currently meson requires a shared library for building girs.')
assert(enable_static or pkglibdir != '', 'Installing shared library, but pkglibdir is unset!')

c_args = ['-DG_LOG_DOMAIN="Gvc"']

if enable_introspection
  c_args += '-DWITH_INTROSPECTION'
endif

if enable_static
  libgvc_static = static_library('gvc',
    sources: libgvc_gir_sources + libgvc_no_gir_sources + libgvc_enums,
    dependencies: libgvc_deps,
    c_args: c_args
  )

  libgvc = libgvc_static
else
  if pkglibdir == ''
    error('Installing shared library, but pkglibdir is unset!')
  endif

  libgvc_shared = shared_library('gvc',
    sources: libgvc_gir_sources + libgvc_no_gir_sources + libgvc_enums,
    dependencies: libgvc_deps,
    c_args: c_args,
    install_dir: pkglibdir,
    install: true
  )

  libgvc = libgvc_shared
endif

if enable_introspection
  assert(pkgdatadir != '', 'Installing introspection, but pkgdatadir is unset!')

  libgvc_gir = gnome.generate_gir(libgvc,
    sources: libgvc_gir_sources + libgvc_gir_headers + libgvc_enums,
    nsversion: '1.0',
    namespace: 'Gvc',
    includes: ['Gio-2.0', 'GObject-2.0'],
    extra_args: ['-DWITH_INTROSPECTION', '--quiet'],
    install_dir_gir: pkgdatadir,
    install_dir_typelib: pkglibdir,
    install: true
  )
endif

if enable_alsa
  executable('test-audio-device-selection',
    sources: 'test-audio-device-selection.c',
    link_with: libgvc,
    dependencies: libgvc_deps,
    c_args: c_args
  )
endif

libgvc_dep = declare_dependency(
  link_with: libgvc,
  include_directories: include_directories('.'),
  dependencies: libgvc_deps
)

configure_file(
  output: 'config.h',
  configuration: cdata
)
07070100000022000081A400000000000000000000000166437C940000032C000000000000000000000000000000000000003600000000libgnome-volume-control-0.gitmodule/meson_options.txtoption('package_name',
  type: 'string',
  value: '',
  description: 'The value for the GETTEXT_PACKAGE define.'
)

option('package_version',
  type: 'string',
  value: '',
  description: 'The value for the PACKAGE_VERSION define.'
)

option('pkglibdir',
  type: 'string',
  value: '',
  description: 'The private directory the shared library/typelib will be installed into.'
)

option('pkgdatadir',
  type: 'string',
  value: '',
  description: 'The private directory the gir file will be installed into.'
)

option('alsa',
  type: 'boolean',
  value: true,
  description: 'Build ALSA support.'
)

option('static',
  type: 'boolean',
  value: false,
  description: 'Build as a static library.'
)

option('introspection',
  type: 'boolean',
  value: false,
  description: 'Build gobject-introspection support'
)
07070100000023000081A400000000000000000000000166437C94000007BB000000000000000000000000000000000000004200000000libgnome-volume-control-0.gitmodule/test-audio-device-selection.c
#include <stdio.h>
#include <locale.h>
#include <pulse/pulseaudio.h>
#include "gvc-mixer-control.h"

#define MAX_ATTEMPTS 3

typedef struct {
	GvcHeadsetPortChoice choice;
	const char *name;
} AudioSelectionChoice;

static AudioSelectionChoice audio_selection_choices[] = {
	{ GVC_HEADSET_PORT_CHOICE_HEADPHONES,   "headphones" },
	{ GVC_HEADSET_PORT_CHOICE_HEADSET,      "headset" },
	{ GVC_HEADSET_PORT_CHOICE_MIC,          "microphone" },
};

static void
audio_selection_needed (GvcMixerControl      *volume,
			guint                 id,
			gboolean              show_dialog,
			GvcHeadsetPortChoice  choices,
			gpointer              user_data)
{
	const char *args[G_N_ELEMENTS (audio_selection_choices) + 1];
	guint i, n;
	int response = -1;

	if (!show_dialog) {
		g_print ("--- Audio selection not needed anymore for id %d\n", id);
		return;
	}

	n = 0;
	for (i = 0; i < G_N_ELEMENTS (audio_selection_choices); ++i) {
		if (choices & audio_selection_choices[i].choice)
			args[n++] = audio_selection_choices[i].name;
	}
	args[n] = NULL;

	g_print ("+++ Audio selection needed for id %d\n", id);
	g_print ("    Choices are:\n");
	for (i = 0; args[i] != NULL; i++)
		g_print ("    %d. %s\n", i + 1, args[i]);

	for (i = 0; response < 0 && i < MAX_ATTEMPTS; i++) {
		int res;

		g_print ("What is your choice?\n");
		if (scanf ("%d", &res) == 1 &&
		    res > 0 &&
		    res < (int) g_strv_length ((char **)  args)) {
			response = res;
			break;
		}
	}

	gvc_mixer_control_set_headset_port (volume,
					    id,
					    audio_selection_choices[response - 1].choice);
}

int main (int argc, char **argv)
{
	GMainLoop *loop;
	GvcMixerControl *volume;

	setlocale (LC_ALL, "");

	loop = g_main_loop_new (NULL, FALSE);

	volume = gvc_mixer_control_new ("GNOME Volume Control test");
	g_signal_connect (volume,
			  "audio-device-selection-needed",
			  G_CALLBACK (audio_selection_needed),
			  NULL);
	gvc_mixer_control_open (volume);

	g_main_loop_run (loop);

	return 0;
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!636 blocks
openSUSE Build Service is sponsored by