[gnome-settings-daemon] Bug 589825 – daemon is using 100% of CPU
- From: Bastien Nocera <hadess src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-settings-daemon] Bug 589825 – daemon is using 100% of CPU
- Date: Tue, 28 Jul 2009 10:19:04 +0000 (UTC)
commit c2179bd1a3c18fa56675c81f58df9e9c8896c8ab
Author: Bastien Nocera <hadess hadess net>
Date: Tue Jul 28 11:17:43 2009 +0100
Bug 589825 â?? daemon is using 100% of CPU
Update gnome-volume-control code from gnome-media, update
requirements to PulseAudio 0.9.15.
configure.ac | 2 +-
plugins/media-keys/cut-n-paste/Makefile.am | 2 +
plugins/media-keys/cut-n-paste/gvc-mixer-card.c | 493 ++++++++++++++++++++
plugins/media-keys/cut-n-paste/gvc-mixer-card.h | 86 ++++
plugins/media-keys/cut-n-paste/gvc-mixer-control.c | 332 +++++++++++++-
plugins/media-keys/cut-n-paste/gvc-mixer-control.h | 8 +
plugins/media-keys/cut-n-paste/gvc-mixer-sink.c | 32 ++
plugins/media-keys/cut-n-paste/gvc-mixer-source.c | 32 ++
plugins/media-keys/cut-n-paste/gvc-mixer-stream.c | 129 +++++
plugins/media-keys/cut-n-paste/gvc-mixer-stream.h | 20 +-
10 files changed, 1118 insertions(+), 18 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 353250a..69cc93d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -246,7 +246,7 @@ AC_ARG_ENABLE(pulse,
[WANT_PULSE=yes]) dnl Default value
if test x$WANT_PULSE = xyes ; then
- PA_REQUIRED_VERSION=0.9.12
+ PA_REQUIRED_VERSION=0.9.15
PKG_CHECK_MODULES(PULSE, glib-2.0 libpulse >= $PA_REQUIRED_VERSION libpulse-mainloop-glib >= $PA_REQUIRED_VERSION,
[have_pulse=true
AC_DEFINE(HAVE_PULSE, 1, [Define if PULSE sound server should be used])],
diff --git a/plugins/media-keys/cut-n-paste/Makefile.am b/plugins/media-keys/cut-n-paste/Makefile.am
index f7f56f0..bc59a10 100644
--- a/plugins/media-keys/cut-n-paste/Makefile.am
+++ b/plugins/media-keys/cut-n-paste/Makefile.am
@@ -18,6 +18,8 @@ libgvc_la_SOURCES = \
gvc-mixer-stream.c \
gvc-channel-map.h \
gvc-channel-map.c \
+ gvc-mixer-card.c \
+ gvc-mixer-card.h \
gvc-mixer-sink.h \
gvc-mixer-sink.c \
gvc-mixer-source.h \
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-card.c b/plugins/media-keys/cut-n-paste/gvc-mixer-card.c
new file mode 100644
index 0000000..9037ff2
--- /dev/null
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-card.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ * Copyright (C) 2009 Bastien Nocera
+ *
+ * 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.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-card.h"
+
+#define GVC_MIXER_CARD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardPrivate))
+
+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;
+};
+
+enum
+{
+ PROP_0,
+ PROP_ID,
+ PROP_PA_CONTEXT,
+ PROP_INDEX,
+ PROP_NAME,
+ PROP_ICON_NAME,
+ PROP_PROFILE,
+ PROP_HUMAN_PROFILE,
+};
+
+static void gvc_mixer_card_class_init (GvcMixerCardClass *klass);
+static void gvc_mixer_card_init (GvcMixerCard *mixer_card);
+static void gvc_mixer_card_finalize (GObject *object);
+
+G_DEFINE_TYPE (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 (G_OBJECT (card), "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 (G_OBJECT (card), "icon-name");
+
+ return TRUE;
+}
+
+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, FALSE);
+
+ 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);
+
+ 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 (G_OBJECT (card), "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;
+}
+
+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 != NULL) {
+ pa_operation *o;
+
+ g_free (card->priv->target_profile);
+ card->priv->target_profile = g_strdup (profile);
+
+ o = 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 (o == NULL) {
+ g_warning ("pa_context_set_card_profile_by_index() failed");
+ return FALSE;
+ }
+
+ pa_operation_unref (o);
+ } else {
+ g_assert (card->priv->human_profile == NULL);
+ card->priv->profile = g_strdup (profile);
+ }
+
+ return TRUE;
+}
+
+const GList *
+gvc_mixer_card_get_profiles (GvcMixerCard *card)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ return card->priv->profiles;
+}
+
+static int
+sort_profiles (GvcMixerCardProfile *a,
+ GvcMixerCardProfile *b)
+{
+ if (a->priority == b->priority)
+ return 0;
+ if (a->priority > b->priority)
+ return 1;
+ return -1;
+}
+
+gboolean
+gvc_mixer_card_set_profiles (GvcMixerCard *card,
+ GList *profiles)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE);
+ g_return_val_if_fail (card->priv->profiles == NULL, FALSE);
+
+ card->priv->profiles = g_list_sort (profiles, (GCompareFunc) sort_profiles);
+
+ return TRUE;
+}
+
+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;
+
+ g_object_class_install_property (gobject_class,
+ 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_object_class_install_property (gobject_class,
+ 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_object_class_install_property (gobject_class,
+ 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_object_class_install_property (gobject_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ "Name",
+ "Name to display for this card",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class,
+ 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_object_class_install_property (gobject_class,
+ PROP_PROFILE,
+ g_param_spec_string ("profile",
+ "Profile",
+ "Name of current profile for this card",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ 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_type_class_add_private (klass, sizeof (GvcMixerCardPrivate));
+}
+
+static void
+gvc_mixer_card_init (GvcMixerCard *card)
+{
+ card->priv = GVC_MIXER_CARD_GET_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
+free_profile (GvcMixerCardProfile *p)
+{
+ g_free (p->profile);
+ g_free (p->human_profile);
+ g_free (p->status);
+ g_free (p);
+}
+
+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_foreach (mixer_card->priv->profiles, (GFunc) free_profile, NULL);
+ g_list_free (mixer_card->priv->profiles);
+ mixer_card->priv->profiles = NULL;
+
+ G_OBJECT_CLASS (gvc_mixer_card_parent_class)->finalize (object);
+}
+
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-card.h b/plugins/media-keys/cut-n-paste/gvc-mixer-card.h
new file mode 100644
index 0000000..3da3edb
--- /dev/null
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-card.h
@@ -0,0 +1,86 @@
+/* -*- 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_H
+#define __GVC_MIXER_CARD_H
+
+#include <glib-object.h>
+#include <pulse/pulseaudio.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;
+} GvcMixerCardProfile;
+
+GType gvc_mixer_card_get_type (void);
+GvcMixerCard * gvc_mixer_card_new (pa_context *context,
+ guint index);
+
+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);
+
+pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card);
+gboolean gvc_mixer_card_change_profile (GvcMixerCard *card,
+ const char *profile);
+
+/* 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);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CARD_H */
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
index 92b0286..0b5feee 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
@@ -39,9 +39,12 @@
#include "gvc-mixer-sink-input.h"
#include "gvc-mixer-source-output.h"
#include "gvc-mixer-event-role.h"
+#include "gvc-mixer-card.h"
#define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate))
+#define RECONNECT_DELAY 5
+
struct GvcMixerControlPrivate
{
pa_glib_mainloop *pa_mainloop;
@@ -66,6 +69,7 @@ struct GvcMixerControlPrivate
GHashTable *sink_inputs; /* routable output streams */
GHashTable *source_outputs; /* routable input streams */
GHashTable *clients;
+ GHashTable *cards;
GvcMixerStream *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
};
@@ -75,6 +79,8 @@ enum {
READY,
STREAM_ADDED,
STREAM_REMOVED,
+ CARD_ADDED,
+ CARD_REMOVED,
DEFAULT_SINK_CHANGED,
DEFAULT_SOURCE_CHANGED,
LAST_SIGNAL
@@ -240,17 +246,30 @@ gvc_mixer_control_get_default_source (GvcMixerControl *control)
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));
+}
+
GvcMixerStream *
gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
guint id)
{
- GvcMixerStream *stream;
+ g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+ return gvc_mixer_control_lookup_id (control->priv->all_streams, id);
+}
+GvcMixerCard *
+gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
+ guint id)
+{
g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
- stream = g_hash_table_lookup (control->priv->all_streams,
- GUINT_TO_POINTER (id));
- return stream;
+ return gvc_mixer_control_lookup_id (control->priv->cards, id);
}
static void
@@ -264,6 +283,50 @@ listify_hash_values_hfunc (gpointer key,
}
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);
+}
+
+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)
{
@@ -276,14 +339,7 @@ gvc_stream_collate (GvcMixerStream *a,
namea = gvc_mixer_stream_get_name (a);
nameb = gvc_mixer_stream_get_name (b);
- if (nameb == NULL && namea == NULL)
- return 0;
- if (nameb == NULL)
- return 1;
- if (namea == NULL)
- return -1;
-
- return g_utf8_collate (namea, nameb);
+ return gvc_name_collate (namea, nameb);
}
GSList *
@@ -579,10 +635,27 @@ update_sink (GvcMixerControl *control,
stream = g_hash_table_lookup (control->priv->sinks,
GUINT_TO_POINTER (info->index));
if (stream == NULL) {
+#if PA_MICRO > 15
+ GList *list = NULL;
+ guint i;
+#endif /* PA_MICRO > 15 */
+
map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map);
stream = gvc_mixer_sink_new (control->priv->pa_context,
info->index,
map);
+#if PA_MICRO > 15
+ for (i = 0; i < info->n_ports; i++) {
+ GvcMixerStreamPort *port;
+
+ port = g_new0 (GvcMixerStreamPort, 1);
+ 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);
+#endif /* PA_MICRO > 15 */
g_object_unref (map);
is_new = TRUE;
} else if (gvc_mixer_stream_is_running (stream)) {
@@ -598,6 +671,10 @@ update_sink (GvcMixerControl *control,
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));
+#if PA_MICRO > 15
+ if (info->active_port != NULL)
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+#endif /* PA_MICRO > 15 */
if (is_new) {
g_hash_table_insert (control->priv->sinks,
@@ -642,11 +719,29 @@ update_source (GvcMixerControl *control,
stream = g_hash_table_lookup (control->priv->sources,
GUINT_TO_POINTER (info->index));
if (stream == NULL) {
+#if PA_MICRO > 15
+ GList *list = NULL;
+ guint i;
+#endif /* PA_MICRO > 15 */
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);
+#if PA_MICRO > 15
+ for (i = 0; i < info->n_ports; i++) {
+ GvcMixerStreamPort *port;
+
+ port = g_new0 (GvcMixerStreamPort, 1);
+ 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);
+#endif /* PA_MICRO > 15 */
+
g_object_unref (map);
is_new = TRUE;
} else if (gvc_mixer_stream_is_running (stream)) {
@@ -664,6 +759,10 @@ update_source (GvcMixerControl *control,
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);
+#if PA_MICRO > 15
+ if (info->active_port != NULL)
+ gvc_mixer_stream_set_port (stream, info->active_port->name);
+#endif /* PA_MICRO > 15 */
if (is_new) {
g_hash_table_insert (control->priv->sources,
@@ -872,6 +971,117 @@ update_client (GvcMixerControl *control,
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;
+}
+
+static void
+update_card (GvcMixerControl *control,
+ const pa_card_info *info)
+{
+ GvcMixerCard *card;
+ gboolean is_new;
+#if 1
+ guint i;
+ const char *key;
+ void *state;
+
+ g_debug ("Udpating 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) {
+ GList *list = NULL;
+
+ for (i = 0; i < info->n_profiles; i++) {
+ struct pa_card_profile_info pi = info->profiles[i];
+ GvcMixerCardProfile *profile;
+
+ 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->priority = pi.priority;
+ list = g_list_prepend (list, profile);
+ }
+ card = gvc_mixer_card_new (control->priv->pa_context,
+ info->index);
+ gvc_mixer_card_set_profiles (card, list);
+ is_new = TRUE;
+ }
+
+ 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),
+ g_object_ref (card));
+ }
+ 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,
@@ -897,7 +1107,6 @@ _pa_context_get_sink_info_cb (pa_context *context,
update_sink (control, i);
}
-
static void
_pa_context_get_source_info_cb (pa_context *context,
const pa_source_info *i,
@@ -999,6 +1208,30 @@ _pa_context_get_client_info_cb (pa_context *context,
}
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)
@@ -1154,6 +1387,30 @@ req_update_client_info (GvcMixerControl *control,
}
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)
{
@@ -1258,6 +1515,19 @@ remove_client (GvcMixerControl *control,
}
static void
+remove_card (GvcMixerControl *control,
+ guint index)
+{
+ 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)
{
@@ -1392,6 +1662,14 @@ _pa_context_subscribe_cb (pa_context *context,
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;
}
}
@@ -1410,7 +1688,8 @@ gvc_mixer_control_ready (GvcMixerControl *control)
PA_SUBSCRIPTION_MASK_SINK_INPUT|
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
PA_SUBSCRIPTION_MASK_CLIENT|
- PA_SUBSCRIPTION_MASK_SERVER),
+ PA_SUBSCRIPTION_MASK_SERVER|
+ PA_SUBSCRIPTION_MASK_CARD),
NULL,
NULL);
@@ -1426,6 +1705,7 @@ gvc_mixer_control_ready (GvcMixerControl *control)
req_update_source_info (control, -1);
req_update_sink_input_info (control, -1);
req_update_source_output_info (control, -1);
+ req_update_card (control, -1);
control->priv->n_outstanding = 6;
@@ -1441,7 +1721,6 @@ gvc_mixer_control_ready (GvcMixerControl *control)
_pa_ext_stream_restore_subscribe_cb,
control);
-
o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
1,
NULL,
@@ -1548,7 +1827,7 @@ _pa_context_state_cb (pa_context *context,
case PA_CONTEXT_FAILED:
g_warning ("Connection failed, reconnecting...");
if (control->priv->reconnect_id == 0)
- control->priv->reconnect_id = g_idle_add (idle_reconnect, control);
+ control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control);
break;
case PA_CONTEXT_TERMINATED:
@@ -1640,6 +1919,10 @@ gvc_mixer_control_dispose (GObject *object)
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;
+ }
G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
}
@@ -1702,6 +1985,22 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1, 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,
+ g_cclosure_marshal_VOID__UINT,
+ 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,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
signals [DEFAULT_SINK_CHANGED] =
g_signal_new ("default-sink-changed",
G_TYPE_FROM_CLASS (klass),
@@ -1738,6 +2037,7 @@ gvc_mixer_control_init (GvcMixerControl *control)
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->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
}
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
index 3de9e62..d2f7968 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
@@ -24,6 +24,7 @@
#include <glib-object.h>
#include <pulse/pulseaudio.h>
#include "gvc-mixer-stream.h"
+#include "gvc-mixer-card.h"
G_BEGIN_DECLS
@@ -52,6 +53,10 @@ typedef struct
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,
@@ -67,6 +72,7 @@ gboolean gvc_mixer_control_close (GvcMixerControl *cont
gboolean gvc_mixer_control_is_ready (GvcMixerControl *control);
pa_context * gvc_mixer_control_get_pa_context (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);
@@ -75,6 +81,8 @@ GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *cont
GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control,
guint id);
+GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control,
+ guint id);
GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control);
GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control);
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
index 06e5af6..5e95f63 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
@@ -106,6 +106,37 @@ gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
return TRUE;
}
+static gboolean
+gvc_mixer_sink_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+#if PA_MICRO > 15
+ 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;
+#else
+ return FALSE;
+#endif /* PA_MICRO > 15 */
+}
+
static GObject *
gvc_mixer_sink_constructor (GType type,
guint n_construct_properties,
@@ -132,6 +163,7 @@ gvc_mixer_sink_class_init (GvcMixerSinkClass *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;
g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
index ae02d85..d13be9d 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
@@ -106,6 +106,37 @@ gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
return TRUE;
}
+static gboolean
+gvc_mixer_source_change_port (GvcMixerStream *stream,
+ const char *port)
+{
+#if PA_MICRO > 15
+ 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;
+#else
+ return FALSE;
+#endif /* PA_MICRO > 15 */
+}
+
static GObject *
gvc_mixer_source_constructor (GType type,
guint n_construct_properties,
@@ -133,6 +164,7 @@ gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
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;
g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate));
}
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
index e5cfb19..caea0f1 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
@@ -51,6 +51,9 @@ struct GvcMixerStreamPrivate
gboolean is_virtual;
pa_volume_t base_volume;
pa_operation *change_volume_op;
+ char *port;
+ char *human_port;
+ GList *ports;
};
enum
@@ -70,6 +73,7 @@ enum
PROP_CAN_DECIBEL,
PROP_IS_EVENT_STREAM,
PROP_IS_VIRTUAL,
+ PROP_PORT,
};
static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass);
@@ -396,6 +400,92 @@ gvc_mixer_stream_set_base_volume (GvcMixerStream *stream,
return TRUE;
}
+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 (G_OBJECT (stream), "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);
+}
+
+const GList *
+gvc_mixer_stream_get_ports (GvcMixerStream *stream)
+{
+ g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+ return stream->priv->ports;
+}
+
+static int
+sort_ports (GvcMixerStreamPort *a,
+ GvcMixerStreamPort *b)
+{
+ if (a->priority == b->priority)
+ return 0;
+ if (a->priority > b->priority)
+ return 1;
+ return -1;
+}
+
+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;
+}
+
static void
gvc_mixer_stream_set_property (GObject *object,
guint prop_id,
@@ -447,6 +537,9 @@ gvc_mixer_stream_set_property (GObject *object,
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;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -506,6 +599,9 @@ gvc_mixer_stream_get_property (GObject *object,
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;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -530,6 +626,13 @@ gvc_mixer_stream_constructor (GType type,
}
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;
@@ -593,6 +696,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
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;
g_object_class_install_property (gobject_class,
@@ -693,6 +797,13 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
"Whether the stream is virtual",
FALSE,
G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+ g_object_class_install_property (gobject_class,
+ PROP_PORT,
+ g_param_spec_string ("port",
+ "Port",
+ "The name of the current port for this stream",
+ NULL,
+ G_PARAM_READWRITE));
g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate));
}
@@ -703,6 +814,14 @@ gvc_mixer_stream_init (GvcMixerStream *stream)
}
static void
+free_port (GvcMixerStreamPort *p)
+{
+ g_free (p->port);
+ g_free (p->human_port);
+ g_free (p);
+}
+
+static void
gvc_mixer_stream_finalize (GObject *object)
{
GvcMixerStream *mixer_stream;
@@ -726,6 +845,16 @@ gvc_mixer_stream_finalize (GObject *object)
g_free (mixer_stream->priv->icon_name);
mixer_stream->priv->icon_name = 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_foreach (mixer_stream->priv->ports, (GFunc) free_port, NULL);
+ g_list_free (mixer_stream->priv->ports);
+ 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;
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
index 4171ca3..4ae2d34 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
@@ -48,17 +48,31 @@ typedef struct
GObjectClass parent_class;
/* vtable */
- gboolean (*push_volume) (GvcMixerStream *stream, gpointer *operation);
+ 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;
+} GvcMixerStreamPort;
+
GType gvc_mixer_stream_get_type (void);
pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream);
guint gvc_mixer_stream_get_index (GvcMixerStream *stream);
guint gvc_mixer_stream_get_id (GvcMixerStream *stream);
GvcChannelMap * gvc_mixer_stream_get_channel_map (GvcMixerStream *stream);
+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);
@@ -100,6 +114,10 @@ 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);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]