rpms/gnome-settings-daemon/F-11 0001-Update-gnome-volume-control-code-from-master.patch, NONE, 1.1 0002-Bug-590073-gnome-settings-daemon-crashed-with-SI.patch, NONE, 1.1 0003-Update-gnome-volume-control-code.patch, NONE, 1.1 0004-Update-volume-control-code-for-new-API.patch, NONE, 1.1 gnome-settings-daemon.spec, 1.109, 1.110

Bastien Nocera hadess at fedoraproject.org
Mon Sep 7 15:02:30 UTC 2009


Author: hadess

Update of /cvs/pkgs/rpms/gnome-settings-daemon/F-11
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv27258

Modified Files:
	gnome-settings-daemon.spec 
Added Files:
	0001-Update-gnome-volume-control-code-from-master.patch 
	0002-Bug-590073-gnome-settings-daemon-crashed-with-SI.patch 
	0003-Update-gnome-volume-control-code.patch 
	0004-Update-volume-control-code-for-new-API.patch 
Log Message:
* Mon Sep 07 2009 Bastien Nocera <bnocera at redhat.com> 2.26.1-10
- Update volume code from 2.27


0001-Update-gnome-volume-control-code-from-master.patch:
 cut-n-paste/gvc-channel-map.c         |  175 ++++++++++++++++++---
 cut-n-paste/gvc-channel-map.h         |   24 ++
 cut-n-paste/gvc-mixer-control.c       |  276 +++++++++++++++++++++++++++-------
 cut-n-paste/gvc-mixer-control.h       |    1 
 cut-n-paste/gvc-mixer-event-role.c    |   29 +--
 cut-n-paste/gvc-mixer-event-role.h    |    5 
 cut-n-paste/gvc-mixer-sink-input.c    |   40 ++--
 cut-n-paste/gvc-mixer-sink.c          |   53 +++---
 cut-n-paste/gvc-mixer-source-output.c |    6 
 cut-n-paste/gvc-mixer-source.c        |   48 ++---
 cut-n-paste/gvc-mixer-stream.c        |  225 ++++++++++++++++++++++++---
 cut-n-paste/gvc-mixer-stream.h        |   23 ++
 gsd-media-keys-manager.c              |   11 -
 13 files changed, 708 insertions(+), 208 deletions(-)

--- NEW FILE 0001-Update-gnome-volume-control-code-from-master.patch ---
>From dffcd6f60cb6984b28d673d013b436de4ee54f4e Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess at hadess.net>
Date: Mon, 22 Jun 2009 15:40:19 +0100
Subject: [PATCH 1/4] Update gnome-volume-control code from master

And make slight changes to gsd-media-keys-manager.c to match
the new API.
---
 plugins/media-keys/cut-n-paste/gvc-channel-map.c   |  175 +++++++++++--
 plugins/media-keys/cut-n-paste/gvc-channel-map.h   |   24 ++-
 plugins/media-keys/cut-n-paste/gvc-mixer-control.c |  276 ++++++++++++++++----
 plugins/media-keys/cut-n-paste/gvc-mixer-control.h |    1 +
 .../media-keys/cut-n-paste/gvc-mixer-event-role.c  |   29 +-
 .../media-keys/cut-n-paste/gvc-mixer-event-role.h  |    5 +-
 .../media-keys/cut-n-paste/gvc-mixer-sink-input.c  |   40 ++--
 plugins/media-keys/cut-n-paste/gvc-mixer-sink.c    |   53 ++--
 .../cut-n-paste/gvc-mixer-source-output.c          |    6 +-
 plugins/media-keys/cut-n-paste/gvc-mixer-source.c  |   48 ++--
 plugins/media-keys/cut-n-paste/gvc-mixer-stream.c  |  225 ++++++++++++++---
 plugins/media-keys/cut-n-paste/gvc-mixer-stream.h  |   23 ++-
 plugins/media-keys/gsd-media-keys-manager.c        |   10 +-
 13 files changed, 708 insertions(+), 207 deletions(-)

diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.c b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
index e7c9b22..32750ef 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.c
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
@@ -33,15 +33,26 @@
 
 #define GVC_CHANNEL_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapPrivate))
 
+#ifndef PA_CHECK_VERSION
+#define PA_CHECK_VERSION(major,minor,micro)                             \
+        ((PA_MAJOR > (major)) ||                                        \
+         (PA_MAJOR == (major) && PA_MINOR > (minor)) ||                 \
+         (PA_MAJOR == (major) && PA_MINOR == (minor) && PA_MICRO >= (micro)))
+#endif
+
+
 struct GvcChannelMapPrivate
 {
-        guint                 num_channels;
-        pa_channel_position_t positions[PA_CHANNELS_MAX];
-        gdouble               gains[PA_CHANNELS_MAX];
+        pa_channel_map        pa_map;
+        pa_cvolume            pa_volume;
+        gdouble               extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */
+        gboolean              can_balance;
+        gboolean              can_fade;
+        gboolean              has_lfe;
 };
 
 enum {
-        GAINS_CHANGED,
+        VOLUME_CHANGED,
         LAST_SIGNAL
 };
 
@@ -53,25 +64,135 @@ static void     gvc_channel_map_finalize   (GObject            *object);
 
 G_DEFINE_TYPE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT)
 
+/* FIXME remove when we depend on a newer PA */
+static int
+gvc_pa_channel_map_has_position (const pa_channel_map *map, pa_channel_position_t p) {
+        unsigned c;
+
+        g_return_val_if_fail(pa_channel_map_valid(map), 0);
+        g_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
+
+        for (c = 0; c < map->channels; c++)
+                if (map->map[c] == p)
+                        return 1;
+
+        return 0;
+}
+
+#if !PA_CHECK_VERSION(0,9,16)
+/* The PulseAudio master increase version only when tagged, so let's avoid clashing with pa_ namespace */
+#define pa_cvolume_get_position gvc_cvolume_get_position
+static pa_volume_t
+gvc_cvolume_get_position (pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) {
+        unsigned c;
+        pa_volume_t v = PA_VOLUME_MUTED;
+
+        g_assert(cv);
+        g_assert(map);
+
+        g_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), PA_VOLUME_MUTED);
+        g_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, PA_VOLUME_MUTED);
+
+        for (c = 0; c < map->channels; c++)
+                if (map->map[c] == t)
+                        if (cv->values[c] > v)
+                                v = cv->values[c];
+
+        return v;
+}
+#endif
+
 guint
 gvc_channel_map_get_num_channels (GvcChannelMap *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0);
-        return map->priv->num_channels;
+
+        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 (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        return map->priv->can_balance;
+}
+
+gboolean
+gvc_channel_map_can_fade (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 (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);
 }
 
-gdouble *
-gvc_channel_map_get_gains (GvcChannelMap *map)
+gboolean
+gvc_channel_map_has_lfe (GvcChannelMap  *map)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE);
+
+        return map->priv->has_lfe;
+}
+
+const pa_channel_map *
+gvc_channel_map_get_pa_channel_map (GvcChannelMap  *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
-        return map->priv->gains;
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return &map->priv->pa_map;
 }
 
-pa_channel_position_t *
-gvc_channel_map_get_positions (GvcChannelMap *map)
+const pa_cvolume *
+gvc_channel_map_get_cvolume (GvcChannelMap  *map)
 {
         g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL);
-        return map->priv->positions;
+
+        if (!pa_channel_map_valid(&map->priv->pa_map))
+                return NULL;
+
+        return &map->priv->pa_volume;
 }
 
 static void
@@ -81,11 +202,11 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
 
         gobject_class->finalize = gvc_channel_map_finalize;
 
-        signals [GAINS_CHANGED] =
-                g_signal_new ("gains-changed",
+        signals [VOLUME_CHANGED] =
+                g_signal_new ("volume-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GvcChannelMapClass, gains_changed),
+                              G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
                               NULL, NULL,
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE, 0);
@@ -94,10 +215,19 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
 }
 
 void
-gvc_channel_map_gains_changed (GvcChannelMap *map)
+gvc_channel_map_volume_changed (GvcChannelMap     *map,
+                                const pa_cvolume  *cv)
 {
         g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
-        g_signal_emit (map, signals[GAINS_CHANGED], 0);
+        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;
+
+        g_signal_emit (map, signals[VOLUME_CHANGED], 0);
 }
 
 static void
@@ -133,13 +263,14 @@ static void
 set_from_pa_map (GvcChannelMap        *map,
                  const pa_channel_map *pa_map)
 {
-        guint i;
+        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->has_lfe = gvc_pa_channel_map_has_position (pa_map, PA_CHANNEL_POSITION_LFE);
 
-        map->priv->num_channels = pa_map->channels;
-        for (i = 0; i < pa_map->channels; i++) {
-                map->priv->positions[i] = pa_map->map[i];
-                map->priv->gains[i] = 1.0;
-        }
+        map->priv->pa_map = *pa_map;
+        pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM);
 }
 
 GvcChannelMap *
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.h b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
index 963904f..b35c9cb 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.h
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
@@ -44,19 +44,35 @@ typedef struct
 typedef struct
 {
         GObjectClass           parent_class;
-        void (*gains_changed) (GvcChannelMap *channel_map);
+        void (*volume_changed) (GvcChannelMap *channel_map);
 } GvcChannelMapClass;
 
+enum {
+        VOLUME,
+        BALANCE,
+        FADE,
+        LFE,
+};
+
+#define NUM_TYPES LFE + 1
+
 GType                   gvc_channel_map_get_type                (void);
 
 GvcChannelMap *         gvc_channel_map_new                     (void);
 GvcChannelMap *         gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map);
 guint                   gvc_channel_map_get_num_channels        (GvcChannelMap  *map);
-pa_channel_position_t * gvc_channel_map_get_positions           (GvcChannelMap  *map);
-gdouble *               gvc_channel_map_get_gains               (GvcChannelMap  *map);
+const gdouble *         gvc_channel_map_get_volume              (GvcChannelMap  *map);
+gboolean                gvc_channel_map_can_balance             (GvcChannelMap  *map);
+gboolean                gvc_channel_map_can_fade                (GvcChannelMap  *map);
+gboolean                gvc_channel_map_has_lfe                 (GvcChannelMap  *map);
 
-void                    gvc_channel_map_gains_changed           (GvcChannelMap  *map);
+void                    gvc_channel_map_volume_changed          (GvcChannelMap    *map,
+                                                                 const pa_cvolume *cv);
+const char *            gvc_channel_map_get_mapping             (GvcChannelMap  *map);
 
+/* private */
+const pa_cvolume *      gvc_channel_map_get_cvolume             (GvcChannelMap  *map);
+const pa_channel_map *  gvc_channel_map_get_pa_channel_map      (GvcChannelMap  *map);
 G_END_DECLS
 
 #endif /* __GVC_CHANNEL_MAP_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 e7dd18b..92b0286 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
@@ -48,6 +48,7 @@ struct GvcMixerControlPrivate
         pa_mainloop_api  *pa_api;
         pa_context       *pa_context;
         int               n_outstanding;
+        guint             reconnect_id;
 
         gboolean          default_sink_is_set;
         guint             default_sink_id;
@@ -65,9 +66,12 @@ struct GvcMixerControlPrivate
         GHashTable       *sink_inputs; /* routable output streams */
         GHashTable       *source_outputs; /* routable input streams */
         GHashTable       *clients;
+
+        GvcMixerStream   *new_default_stream; /* new default stream, used in gvc_mixer_control_set_default_sink () */
 };
 
 enum {
+        CONNECTING,
         READY,
         STREAM_ADDED,
         STREAM_REMOVED,
@@ -104,6 +108,42 @@ gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
         return stream;
 }
 
+static void
+gvc_mixer_control_stream_restore_cb (pa_context *c,
+                                     const pa_ext_stream_restore_info *info,
+                                     int eol,
+                                     void *userdata)
+{
+        pa_operation *o;
+        GvcMixerControl *control = (GvcMixerControl *) userdata;
+        pa_ext_stream_restore_info new_info;
+
+        if (eol || control->priv->new_default_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 (control->priv->new_default_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, info->device);
+
+        pa_operation_unref (o);
+}
+
 gboolean
 gvc_mixer_control_set_default_sink (GvcMixerControl *control,
                                     GvcMixerStream  *stream)
@@ -118,7 +158,23 @@ gvc_mixer_control_set_default_sink (GvcMixerControl *control,
                                          NULL,
                                          NULL);
         if (o == NULL) {
-                g_warning ("pa_context_set_default_sink() failed");
+                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_stream = stream;
+        g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_stream);
+
+        o = pa_ext_stream_restore_read (control->priv->pa_context,
+                                        gvc_mixer_control_stream_restore_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;
         }
 
@@ -220,6 +276,13 @@ 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);
 }
 
@@ -320,12 +383,14 @@ _set_default_source (GvcMixerControl *control,
 {
         guint new_id;
 
-        new_id = 0;
-
-        if (stream != NULL) {
-                new_id = gvc_mixer_stream_get_id (stream);
+        if (stream == NULL) {
+                control->priv->default_source_id = 0;
+                control->priv->default_source_is_set = FALSE;
+                return;
         }
 
+        new_id = gvc_mixer_stream_get_id (stream);
+
         if (control->priv->default_source_id != new_id) {
                 control->priv->default_source_id = new_id;
                 control->priv->default_source_is_set = TRUE;
@@ -342,12 +407,14 @@ _set_default_sink (GvcMixerControl *control,
 {
         guint new_id;
 
-        new_id = 0;
-
-        if (stream != NULL) {
-                new_id = gvc_mixer_stream_get_id (stream);
+        if (stream == NULL) {
+                control->priv->default_sink_id = 0;
+                control->priv->default_sink_is_set = FALSE;
+                return;
         }
 
+        new_id = gvc_mixer_stream_get_id (stream);
+
         if (control->priv->default_sink_id != new_id) {
                 control->priv->default_sink_id = new_id;
                 control->priv->default_sink_is_set = TRUE;
@@ -495,6 +562,7 @@ update_sink (GvcMixerControl    *control,
         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);
@@ -506,22 +574,21 @@ update_sink (GvcMixerControl    *control,
                  map_buff);
 #endif
 
-        /* for now completely ignore virtual streams */
-        if (!(info->flags & PA_SINK_HARDWARE)) {
-                return;
-        }
-
+        map = NULL;
         is_new = FALSE;
         stream = g_hash_table_lookup (control->priv->sinks,
                                       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_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);
@@ -531,11 +598,6 @@ 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 (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
-                gdouble db;
-                db = pa_sw_volume_to_dB (max_volume);
-                gvc_mixer_stream_set_decibel (stream, db);
-        }
 
         if (is_new) {
                 g_hash_table_insert (control->priv->sinks,
@@ -549,6 +611,10 @@ update_sink (GvcMixerControl    *control,
             && strcmp (control->priv->default_sink_name, info->name) == 0) {
                 _set_default_sink (control, stream);
         }
+
+        if (map == NULL)
+                map = gvc_mixer_stream_get_channel_map (stream);
+        gvc_channel_map_volume_changed (map, &info->volume);
 }
 
 static void
@@ -566,8 +632,8 @@ update_source (GvcMixerControl      *control,
                  info->description);
 #endif
 
-        /* for now completely ignore virtual streams */
-        if (!(info->flags & PA_SOURCE_HARDWARE)) {
+        /* completely ignore monitors, they're not real sources */
+        if (info->monitor_of_sink != PA_INVALID_INDEX) {
                 return;
         }
 
@@ -583,6 +649,10 @@ update_source (GvcMixerControl      *control,
                                                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);
@@ -593,11 +663,7 @@ update_source (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_SOURCE_DECIBEL_VOLUME));
-        if (!!(info->flags & PA_SINK_DECIBEL_VOLUME)) {
-                gdouble db;
-                db = pa_sw_volume_to_dB (max_volume);
-                gvc_mixer_stream_set_decibel (stream, db);
-        }
+        gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume);
 
         if (is_new) {
                 g_hash_table_insert (control->priv->sources,
@@ -662,6 +728,34 @@ set_icon_name_from_proplist (GvcMixerStream *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)
 {
@@ -690,6 +784,10 @@ update_sink_input (GvcMixerControl          *control,
                                                    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);
@@ -699,9 +797,12 @@ update_sink_input (GvcMixerControl          *control,
         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, "applications-multimedia");
         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,
@@ -745,7 +846,9 @@ update_source_output (GvcMixerControl             *control,
 
         gvc_mixer_stream_set_name (stream, name);
         gvc_mixer_stream_set_description (stream, info->name);
-        set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
+        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, "audio-input-microphone");
 
         if (is_new) {
                 g_hash_table_insert (control->priv->source_outputs,
@@ -938,8 +1041,16 @@ update_event_role_stream (GvcMixerControl                  *control,
         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);
+                                                   info->device,
+                                                   map);
                 control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream);
                 control->priv->event_sink_input_is_set = TRUE;
 
@@ -1346,6 +1457,78 @@ gvc_mixer_control_ready (GvcMixerControl *control)
 }
 
 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);
+
+        /* FIXME: read these from an object property */
+        proplist = pa_proplist_new ();
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_NAME,
+                          _("GNOME Volume Control"));
+        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_streams (GvcMixerControl *control, GHashTable *hash_table)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+
+        g_hash_table_iter_init (&iter, hash_table);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                remove_stream (control, value);
+                g_hash_table_iter_remove (&iter);
+        }
+}
+
+static gboolean
+idle_reconnect (gpointer data)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (data);
+        GHashTableIter iter;
+        gpointer key, value;
+
+        g_return_val_if_fail (control, FALSE);
+
+        if (control->priv->pa_context) {
+                pa_context_unref (control->priv->pa_context);
+                control->priv->pa_context = NULL;
+                gvc_mixer_new_pa_context (control);
+        }
+
+        remove_all_streams (control, control->priv->sinks);
+        remove_all_streams (control, control->priv->sources);
+        remove_all_streams (control, control->priv->sink_inputs);
+        remove_all_streams (control, control->priv->source_outputs);
+
+        g_hash_table_iter_init (&iter, control->priv->clients);
+        while (g_hash_table_iter_next (&iter, &key, &value))
+                g_hash_table_iter_remove (&iter);
+
+        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)
 {
@@ -1363,7 +1546,9 @@ _pa_context_state_cb (pa_context *context,
                 break;
 
         case PA_CONTEXT_FAILED:
-                g_warning ("Connection failed");
+                g_warning ("Connection failed, reconnecting...");
+                if (control->priv->reconnect_id == 0)
+                        control->priv->reconnect_id = g_idle_add (idle_reconnect, control);
                 break;
 
         case PA_CONTEXT_TERMINATED:
@@ -1386,7 +1571,8 @@ gvc_mixer_control_open (GvcMixerControl *control)
                                        _pa_context_state_cb,
                                        control);
 
-        res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) 0, NULL);
+        g_signal_emit (G_OBJECT (control), signals[CONNECTING], 0);
+        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)));
@@ -1465,30 +1651,12 @@ gvc_mixer_control_constructor (GType                  type,
 {
         GObject         *object;
         GvcMixerControl *self;
-        pa_proplist     *proplist;
 
         object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
 
         self = GVC_MIXER_CONTROL (object);
 
-        /* FIXME: read these from an object property */
-        proplist = pa_proplist_new ();
-        pa_proplist_sets (proplist,
-                          PA_PROP_APPLICATION_NAME,
-                          _("GNOME Volume Control"));
-        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);
-        g_assert (self->priv->pa_context);
-        pa_proplist_free (proplist);
+        gvc_mixer_new_pa_context (self);
 
         return object;
 }
@@ -1502,6 +1670,14 @@ gvc_mixer_control_class_init (GvcMixerControlClass *klass)
         object_class->dispose = gvc_mixer_control_dispose;
         object_class->finalize = gvc_mixer_control_finalize;
 
+        signals [CONNECTING] =
+                g_signal_new ("connecting",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GvcMixerControlClass, connecting),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
         signals [READY] =
                 g_signal_new ("ready",
                               G_TYPE_FROM_CLASS (klass),
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 33e745a..3de9e62 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.h
@@ -46,6 +46,7 @@ typedef struct
 {
         GObjectClass            parent_class;
 
+        void (*connecting)             (GvcMixerControl *control);
         void (*ready)                  (GvcMixerControl *control);
         void (*stream_added)           (GvcMixerControl *control,
                                         guint            id);
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
index b5469ca..69e38ce 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.c
@@ -53,21 +53,22 @@ G_DEFINE_TYPE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
 update_settings (GvcMixerEventRole *role,
-                 guint              volume,
-                 gboolean           is_muted)
+                 gboolean           is_muted,
+                 gpointer          *op)
 {
         pa_operation              *o;
         guint                      index;
+        GvcChannelMap     *map;
         pa_context                *context;
         pa_ext_stream_restore_info info;
 
         index = gvc_mixer_stream_get_index (GVC_MIXER_STREAM (role));
 
-        pa_cvolume_set (&info.volume, 1, (pa_volume_t)volume);
+        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.channels = 1;
-        info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
+        info.channel_map = *gvc_channel_map_get_pa_channel_map(map);
         info.device = role->priv->device;
         info.mute = is_muted;
 
@@ -86,18 +87,17 @@ update_settings (GvcMixerEventRole *role,
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+	if (op != NULL)
+		*op = o;
 
         return TRUE;
 }
 
 static gboolean
-gvc_mixer_event_role_change_volume (GvcMixerStream *stream,
-                                    guint           volume)
+gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         return update_settings (GVC_MIXER_EVENT_ROLE (stream),
-                                volume,
-                                gvc_mixer_stream_get_is_muted (stream));
+                                gvc_mixer_stream_get_is_muted (stream), op);
 }
 
 static gboolean
@@ -105,8 +105,7 @@ gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream,
                                       gboolean        is_muted)
 {
         return update_settings (GVC_MIXER_EVENT_ROLE (stream),
-                                gvc_mixer_stream_get_volume (stream),
-                                is_muted);
+                                is_muted, NULL);
 }
 
 static gboolean
@@ -184,7 +183,7 @@ gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass)
         object_class->set_property = gvc_mixer_event_role_set_property;
         object_class->get_property = gvc_mixer_event_role_get_property;
 
-        stream_class->change_volume = gvc_mixer_event_role_change_volume;
+        stream_class->push_volume = gvc_mixer_event_role_push_volume;
         stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted;
 
         g_object_class_install_property (object_class,
@@ -224,7 +223,8 @@ gvc_mixer_event_role_finalize (GObject *object)
 
 GvcMixerStream *
 gvc_mixer_event_role_new (pa_context *context,
-                          const char *device)
+                          const char *device,
+                          GvcChannelMap *channel_map)
 {
         GObject *object;
 
@@ -232,6 +232,7 @@ gvc_mixer_event_role_new (pa_context *context,
                                "pa-context", context,
                                "index", 0,
                                "device", device,
+                               "channel-map", channel_map,
                                NULL);
 
         return GVC_MIXER_STREAM (object);
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
index 280c597..ab4c509 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-event-role.h
@@ -48,8 +48,9 @@ typedef struct
 
 GType               gvc_mixer_event_role_get_type      (void);
 
-GvcMixerStream *    gvc_mixer_event_role_new           (pa_context *context,
-                                                        const char *device);
+GvcMixerStream *    gvc_mixer_event_role_new           (pa_context    *context,
+                                                        const char    *device,
+                                                        GvcChannelMap *channel_map);
 
 G_END_DECLS
 
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
index b2c7172..35551bb 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink-input.c
@@ -40,44 +40,33 @@ struct GvcMixerSinkInputPrivate
 
 static void     gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass);
 static void     gvc_mixer_sink_input_init       (GvcMixerSinkInput      *mixer_sink_input);
-static void     gvc_mixer_sink_input_finalize   (GObject            *object);
+static void     gvc_mixer_sink_input_finalize   (GObject                *object);
+static void     gvc_mixer_sink_input_dispose    (GObject                *object);
 
 G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
-                                    guint           volume)
+gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              i;
+        const pa_cvolume  *cv;
         guint              num_channels;
-        gdouble           *gains;
 
         index = gvc_mixer_stream_get_index (stream);
 
         map = gvc_mixer_stream_get_channel_map (stream);
         num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                cv.values[i] = v;
-        }
+        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,
+                                              cv,
                                               NULL,
                                               NULL);
 
@@ -86,7 +75,7 @@ gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -140,9 +129,10 @@ gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_sink_input_constructor;
+        object_class->dispose = gvc_mixer_sink_input_dispose;
         object_class->finalize = gvc_mixer_sink_input_finalize;
 
-        stream_class->change_volume = gvc_mixer_sink_input_change_volume;
+        stream_class->push_volume = gvc_mixer_sink_input_push_volume;
         stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate));
@@ -152,7 +142,19 @@ static void
 gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
 {
         sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input);
+}
+
+static void
+gvc_mixer_sink_input_dispose (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_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->dispose (object);
 }
 
 static void
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 76eb3d7..06e5af6 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-sink.c
@@ -40,57 +40,41 @@ struct GvcMixerSinkPrivate
 
 static void     gvc_mixer_sink_class_init (GvcMixerSinkClass *klass);
 static void     gvc_mixer_sink_init       (GvcMixerSink      *mixer_sink);
-static void     gvc_mixer_sink_finalize   (GObject            *object);
+static void     gvc_mixer_sink_finalize   (GObject           *object);
+static void     gvc_mixer_sink_dispose    (GObject           *object);
 
 G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_sink_change_volume (GvcMixerStream *stream,
-                              guint           volume)
+gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              i;
-        guint              num_channels;
-        gdouble           *gains;
+        const pa_cvolume  *cv;
 
         index = gvc_mixer_stream_get_index (stream);
 
-
         map = gvc_mixer_stream_get_channel_map (stream);
-        num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
-
-        g_debug ("Changing volume for sink: n=%d vol=%u", num_channels, (guint)volume);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                g_debug ("Channel %d v=%u", i, v);
-                cv.values[i] = v;
-        }
+        /* 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,
+                                                 cv,
                                                  NULL,
                                                  NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_sink_volume_by_index() failed");
+                g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -113,7 +97,7 @@ gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
                                                NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_sink_mute_by_index() failed");
+                g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
@@ -127,7 +111,7 @@ gvc_mixer_sink_constructor (GType                  type,
                             guint                  n_construct_properties,
                             GObjectConstructParam *construct_params)
 {
-        GObject       *object;
+        GObject      *object;
         GvcMixerSink *self;
 
         object = G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->constructor (type, n_construct_properties, construct_params);
@@ -144,9 +128,10 @@ gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_sink_constructor;
+        object_class->dispose = gvc_mixer_sink_dispose;
         object_class->finalize = gvc_mixer_sink_finalize;
 
-        stream_class->change_volume = gvc_mixer_sink_change_volume;
+        stream_class->push_volume = gvc_mixer_sink_push_volume;
         stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
@@ -156,7 +141,19 @@ static void
 gvc_mixer_sink_init (GvcMixerSink *sink)
 {
         sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink);
+}
+
+static void
+gvc_mixer_sink_dispose (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_OBJECT_CLASS (gvc_mixer_sink_parent_class)->dispose (object);
 }
 
 static void
diff --git a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
index b71ad23..b4cc34d 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source-output.c
@@ -45,10 +45,10 @@ static void     gvc_mixer_source_output_finalize   (GObject            *object);
 G_DEFINE_TYPE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_source_output_change_volume (GvcMixerStream *stream,
-                                       guint           volume)
+gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         /* FIXME: */
+        *op = NULL;
         return TRUE;
 }
 
@@ -84,7 +84,7 @@ gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass)
         object_class->constructor = gvc_mixer_source_output_constructor;
         object_class->finalize = gvc_mixer_source_output_finalize;
 
-        stream_class->change_volume = gvc_mixer_source_output_change_volume;
+        stream_class->push_volume = gvc_mixer_source_output_push_volume;
         stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSourceOutputPrivate));
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 de1b09e..ae02d85 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-source.c
@@ -41,53 +41,40 @@ struct GvcMixerSourcePrivate
 static void     gvc_mixer_source_class_init (GvcMixerSourceClass *klass);
 static void     gvc_mixer_source_init       (GvcMixerSource      *mixer_source);
 static void     gvc_mixer_source_finalize   (GObject            *object);
+static void     gvc_mixer_source_dispose    (GObject           *object);
 
 G_DEFINE_TYPE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM)
 
 static gboolean
-gvc_mixer_source_change_volume (GvcMixerStream *stream,
-                              guint           volume)
+gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         pa_operation      *o;
         guint              index;
         GvcChannelMap     *map;
         pa_context        *context;
-        pa_cvolume         cv;
-        guint              num_channels;
-        guint              i;
-        gdouble           *gains;
+        const pa_cvolume  *cv;
 
         index = gvc_mixer_stream_get_index (stream);
 
         map = gvc_mixer_stream_get_channel_map (stream);
-        num_channels = gvc_channel_map_get_num_channels (map);
-        gains = gvc_channel_map_get_gains (map);
 
-        /* set all values to nominal level */
-        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
-
-
-        /* apply channel gain mapping */
-        for (i = 0; i < num_channels; i++) {
-                pa_volume_t v;
-                v = (double) volume * gains[i];
-                cv.values[i] = v;
-        }
+        /* 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,
+                                                   cv,
                                                    NULL,
                                                    NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_source_volume_by_index() failed");
+                g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
-        pa_operation_unref(o);
+        *op = o;
 
         return TRUE;
 }
@@ -110,7 +97,7 @@ gvc_mixer_source_change_is_muted (GvcMixerStream *stream,
                                                  NULL);
 
         if (o == NULL) {
-                g_warning ("pa_context_set_source_mute_by_index() failed");
+                g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context)));
                 return FALSE;
         }
 
@@ -124,7 +111,7 @@ gvc_mixer_source_constructor (GType                  type,
                             guint                  n_construct_properties,
                             GObjectConstructParam *construct_params)
 {
-        GObject       *object;
+        GObject        *object;
         GvcMixerSource *self;
 
         object = G_OBJECT_CLASS (gvc_mixer_source_parent_class)->constructor (type, n_construct_properties, construct_params);
@@ -141,9 +128,10 @@ gvc_mixer_source_class_init (GvcMixerSourceClass *klass)
         GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
 
         object_class->constructor = gvc_mixer_source_constructor;
+        object_class->dispose = gvc_mixer_source_dispose;
         object_class->finalize = gvc_mixer_source_finalize;
 
-        stream_class->change_volume = gvc_mixer_source_change_volume;
+        stream_class->push_volume = gvc_mixer_source_push_volume;
         stream_class->change_is_muted = gvc_mixer_source_change_is_muted;
 
         g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate));
@@ -153,7 +141,19 @@ static void
 gvc_mixer_source_init (GvcMixerSource *source)
 {
         source->priv = GVC_MIXER_SOURCE_GET_PRIVATE (source);
+}
+
+static void
+gvc_mixer_source_dispose (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_OBJECT_CLASS (gvc_mixer_source_parent_class)->dispose (object);
 }
 
 static void
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 69d8598..e5cfb19 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
@@ -41,13 +41,16 @@ struct GvcMixerStreamPrivate
         guint          id;
         guint          index;
         GvcChannelMap *channel_map;
-        guint          volume;
-        gdouble        decibel;
         char          *name;
         char          *description;
+        char          *application_id;
         char          *icon_name;
         gboolean       is_muted;
         gboolean       can_decibel;
+        gboolean       is_event_stream;
+        gboolean       is_virtual;
+        pa_volume_t    base_volume;
+        pa_operation  *change_volume_op;
 };
 
 enum
@@ -59,11 +62,14 @@ enum
         PROP_INDEX,
         PROP_NAME,
         PROP_DESCRIPTION,
+        PROP_APPLICATION_ID,
         PROP_ICON_NAME,
         PROP_VOLUME,
         PROP_DECIBEL,
         PROP_IS_MUTED,
-        PROP_CAN_DECIBEL
+        PROP_CAN_DECIBEL,
+        PROP_IS_EVENT_STREAM,
+        PROP_IS_VIRTUAL,
 };
 
 static void     gvc_mixer_stream_class_init (GvcMixerStreamClass *klass);
@@ -114,28 +120,36 @@ gvc_mixer_stream_get_channel_map (GvcMixerStream *stream)
         return stream->priv->channel_map;
 }
 
-guint
+pa_volume_t
 gvc_mixer_stream_get_volume (GvcMixerStream *stream)
 {
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
-        return stream->priv->volume;
+
+        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 stream->priv->decibel;
+
+        return pa_sw_volume_to_dB(
+                        (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]);
 }
 
 gboolean
 gvc_mixer_stream_set_volume (GvcMixerStream *stream,
-                             pa_volume_t     volume)
+                              pa_volume_t     volume)
 {
+        pa_cvolume cv;
+
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
-        if (volume != stream->priv->volume) {
-                stream->priv->volume = volume;
+        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);
                 g_object_notify (G_OBJECT (stream), "volume");
         }
 
@@ -146,11 +160,16 @@ gboolean
 gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
                               gdouble         db)
 {
+        pa_cvolume cv;
+
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
 
-        if (db != stream->priv->decibel) {
-                stream->priv->decibel = db;
-                g_object_notify (G_OBJECT (stream), "decibel");
+        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);
+                g_object_notify (G_OBJECT (stream), "volume");
         }
 
         return TRUE;
@@ -238,12 +257,73 @@ gvc_mixer_stream_set_description (GvcMixerStream *stream,
         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 (G_OBJECT (stream), "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 (G_OBJECT (stream), "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 (G_OBJECT (stream), "application-id");
+
+        return TRUE;
+}
+
 static void
-on_channel_map_gains_changed (GvcChannelMap  *channel_map,
-                              GvcMixerStream *stream)
+on_channel_map_volume_changed (GvcChannelMap  *channel_map,
+                               GvcMixerStream *stream)
 {
-        g_debug ("Gains changed");
-        gvc_mixer_stream_change_volume (stream, stream->priv->volume);
+        gvc_mixer_stream_push_volume (stream);
+
+        g_object_notify (G_OBJECT (stream), "volume");
 }
 
 static gboolean
@@ -258,7 +338,7 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
 
         if (stream->priv->channel_map != NULL) {
                 g_signal_handlers_disconnect_by_func (stream->priv->channel_map,
-                                                      on_channel_map_gains_changed,
+                                                      on_channel_map_volume_changed,
                                                       stream);
                 g_object_unref (stream->priv->channel_map);
         }
@@ -267,8 +347,8 @@ gvc_mixer_stream_set_channel_map (GvcMixerStream *stream,
 
         if (stream->priv->channel_map != NULL) {
                 g_signal_connect (stream->priv->channel_map,
-                                  "gains-changed",
-                                  G_CALLBACK (on_channel_map_gains_changed),
+                                  "volume-changed",
+                                  G_CALLBACK (on_channel_map_volume_changed),
                                   stream);
 
                 g_object_notify (G_OBJECT (stream), "channel-map");
@@ -297,6 +377,25 @@ gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
         return TRUE;
 }
 
+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;
+}
+
+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;
+}
+
 static void
 gvc_mixer_stream_set_property (GObject       *object,
                                guint          prop_id,
@@ -324,6 +423,9 @@ gvc_mixer_stream_set_property (GObject       *object,
         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;
@@ -336,6 +438,12 @@ gvc_mixer_stream_set_property (GObject       *object,
         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;
@@ -372,18 +480,29 @@ gvc_mixer_stream_get_property (GObject     *object,
         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_VOLUME:
-                g_value_set_ulong (value, self->priv->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, self->priv->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;
@@ -411,8 +530,7 @@ gvc_mixer_stream_constructor (GType                  type,
 }
 
 static gboolean
-gvc_mixer_stream_real_change_volume (GvcMixerStream *stream,
-                                     guint           volume)
+gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op)
 {
         return FALSE;
 }
@@ -425,12 +543,17 @@ gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
 }
 
 gboolean
-gvc_mixer_stream_change_volume (GvcMixerStream *stream,
-                                guint           volume)
+gvc_mixer_stream_push_volume (GvcMixerStream *stream)
 {
+        pa_operation *op;
         gboolean ret;
         g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
-        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_volume (stream, volume);
+        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;
 }
 
@@ -444,6 +567,21 @@ gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
         return ret;
 }
 
+gboolean
+gvc_mixer_stream_is_running (GvcMixerStream *stream)
+{
+        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)
 {
@@ -454,7 +592,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
         gobject_class->set_property = gvc_mixer_stream_set_property;
         gobject_class->get_property = gvc_mixer_stream_get_property;
 
-        klass->change_volume = gvc_mixer_stream_real_change_volume;
+        klass->push_volume = gvc_mixer_stream_real_push_volume;
         klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
 
         g_object_class_install_property (gobject_class,
@@ -490,7 +628,7 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                              "Volume",
                                                              "The volume for this stream",
                                                              0, G_MAXULONG, 0,
-                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+                                                             G_PARAM_READWRITE));
         g_object_class_install_property (gobject_class,
                                          PROP_DECIBEL,
                                          g_param_spec_double ("decibel",
@@ -514,6 +652,13 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                               NULL,
                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
         g_object_class_install_property (gobject_class,
+                                         PROP_APPLICATION_ID,
+                                         g_param_spec_string ("application-id",
+                                                              "Application identifier",
+                                                              "Application identifier for this stream",
+                                                              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",
@@ -534,7 +679,20 @@ gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
                                                                "Whether stream volume can be converted to decibel units",
                                                                FALSE,
                                                                G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
-
+        g_object_class_install_property (gobject_class,
+                                         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_object_class_install_property (gobject_class,
+                                         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_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate));
 }
 
@@ -542,7 +700,6 @@ static void
 gvc_mixer_stream_init (GvcMixerStream *stream)
 {
         stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream);
-
 }
 
 static void
@@ -563,8 +720,16 @@ gvc_mixer_stream_finalize (GObject *object)
         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;
 
+       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);
 }
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 3dee03b..4171ca3 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.h
@@ -48,8 +48,7 @@ typedef struct
         GObjectClass           parent_class;
 
         /* vtable */
-        gboolean (*change_volume)   (GvcMixerStream *stream,
-                                     guint           volume);
+        gboolean (*push_volume)   (GvcMixerStream *stream, gpointer *operation);
         gboolean (*change_is_muted) (GvcMixerStream *stream,
                                      gboolean        is_muted);
 } GvcMixerStreamClass;
@@ -61,22 +60,26 @@ 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);
 
-guint               gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
+pa_volume_t         gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
 gdouble             gvc_mixer_stream_get_decibel     (GvcMixerStream *stream);
-gboolean            gvc_mixer_stream_change_volume   (GvcMixerStream *stream,
-                                                      guint           volume);
+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_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);
 
 /* private */
 gboolean            gvc_mixer_stream_set_volume      (GvcMixerStream *stream,
-                                                      guint           volume);
+                                                      pa_volume_t     volume);
 gboolean            gvc_mixer_stream_set_decibel     (GvcMixerStream *stream,
                                                       gdouble         db);
 gboolean            gvc_mixer_stream_set_is_muted    (GvcMixerStream *stream,
@@ -89,6 +92,14 @@ 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_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);
 
 G_END_DECLS
 
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index d35048c..3eeea71 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -687,17 +687,17 @@ do_sound_action (GsdMediaKeysManager *manager,
                 if (!muted && (vol <= norm_vol_step)) {
                         manager->priv->num_expected_update_signals = 2;
                         gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
-                        gvc_mixer_stream_change_volume (manager->priv->stream, 0);
+                        gvc_mixer_stream_set_volume (manager->priv->stream, 0);
                 } else if (!muted) {
                         manager->priv->num_expected_update_signals = 1;
-                        gvc_mixer_stream_change_volume (manager->priv->stream, vol - norm_vol_step);
+                        gvc_mixer_stream_set_volume (manager->priv->stream, vol - norm_vol_step);
                 }
                 break;
         case VOLUME_UP_KEY:
                 if (muted) {
                         if (vol == 0) {
                                 manager->priv->num_expected_update_signals = 2;
-                                gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
+                                gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
                                 gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
                         } else {
                                 manager->priv->num_expected_update_signals = 1;
@@ -707,9 +707,9 @@ do_sound_action (GsdMediaKeysManager *manager,
                         if (vol < MAX_VOLUME) {
                                 manager->priv->num_expected_update_signals = 1;
                                 if (vol + norm_vol_step >= MAX_VOLUME) {
-                                        gvc_mixer_stream_change_volume (manager->priv->stream, MAX_VOLUME);
+                                        gvc_mixer_stream_set_volume (manager->priv->stream, MAX_VOLUME);
                                 } else {
-                                        gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
+                                        gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
                                 }
                         }
                 }
-- 
1.6.2.5


0002-Bug-590073-gnome-settings-daemon-crashed-with-SI.patch:
 gsd-media-keys-manager.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

--- NEW FILE 0002-Bug-590073-gnome-settings-daemon-crashed-with-SI.patch ---
>From 604a1f534e703b9d492f886ffabdca879df824ef Mon Sep 17 00:00:00 2001
From: Chris Coulson <chrisccoulson at googlemail.com>
Date: Fri, 31 Jul 2009 16:59:36 +0100
Subject: [PATCH 2/4] =?utf-8?q?Bug=20590073=20=E2=80=93=20gnome-settings-daemon=20crashed=20with=20SIGSEGV=20in=20gvc=5Fmixer=5Fstream=5Fis=5Frunning()?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

Fix crasher when the default sink changes, we were unref'ing
streams, when never ref'ing them.
---
 plugins/media-keys/gsd-media-keys-manager.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 3eeea71..8438875 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -740,7 +740,7 @@ update_default_sink (GsdMediaKeysManager *manager)
         }
 
         if (stream != NULL) {
-                manager->priv->stream = stream;
+                manager->priv->stream = g_object_ref (stream);
                 g_signal_connect (G_OBJECT (manager->priv->stream), "notify::volume",
                                   G_CALLBACK (on_stream_event_notify), manager);
                 g_signal_connect (G_OBJECT (manager->priv->stream), "notify::is-muted",
-- 
1.6.2.5


0003-Update-gnome-volume-control-code.patch:
 cut-n-paste/gvc-channel-map.c   |   15 +++++++++++----
 cut-n-paste/gvc-channel-map.h   |    5 +++--
 cut-n-paste/gvc-mixer-control.c |    2 +-
 cut-n-paste/gvc-mixer-stream.c  |   16 +++++++++++++---
 gsd-media-keys-manager.c        |    4 +++-
 5 files changed, 31 insertions(+), 11 deletions(-)

--- NEW FILE 0003-Update-gnome-volume-control-code.patch ---
>From aec15023ea43ab636df75e7237a6452c90bc5eb3 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess at hadess.net>
Date: Fri, 14 Aug 2009 18:16:10 +0100
Subject: [PATCH 3/4] Update gnome-volume-control code

Should cut down on the feedback loops.
---
 plugins/media-keys/cut-n-paste/gvc-channel-map.c   |   15 +++++++++++----
 plugins/media-keys/cut-n-paste/gvc-channel-map.h   |    5 +++--
 plugins/media-keys/cut-n-paste/gvc-mixer-control.c |    2 +-
 plugins/media-keys/cut-n-paste/gvc-mixer-stream.c  |   16 +++++++++++++---
 plugins/media-keys/gsd-media-keys-manager.c        |    3 +++
 5 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.c b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
index 32750ef..ea3e5af 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.c
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.c
@@ -44,6 +44,7 @@
 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;
@@ -208,15 +209,16 @@ gvc_channel_map_class_init (GvcChannelMapClass *klass)
                               G_SIGNAL_RUN_LAST,
                               G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed),
                               NULL, NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE, 0);
+                              g_cclosure_marshal_VOID__BOOLEAN,
+                              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
         g_type_class_add_private (klass, sizeof (GvcChannelMapPrivate));
 }
 
 void
 gvc_channel_map_volume_changed (GvcChannelMap     *map,
-                                const pa_cvolume  *cv)
+                                const pa_cvolume  *cv,
+                                gboolean           set)
 {
         g_return_if_fail (GVC_IS_CHANNEL_MAP (map));
         g_return_if_fail (cv != NULL);
@@ -227,13 +229,18 @@ gvc_channel_map_volume_changed (GvcChannelMap     *map,
 
         map->priv->pa_volume = *cv;
 
-        g_signal_emit (map, signals[VOLUME_CHANGED], 0);
+        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_PRIVATE (map);
+        map->priv->pa_volume_is_set = FALSE;
 }
 
 static void
diff --git a/plugins/media-keys/cut-n-paste/gvc-channel-map.h b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
index b35c9cb..497ce69 100644
--- a/plugins/media-keys/cut-n-paste/gvc-channel-map.h
+++ b/plugins/media-keys/cut-n-paste/gvc-channel-map.h
@@ -44,7 +44,7 @@ typedef struct
 typedef struct
 {
         GObjectClass           parent_class;
-        void (*volume_changed) (GvcChannelMap *channel_map);
+        void (*volume_changed) (GvcChannelMap *channel_map, gboolean set);
 } GvcChannelMapClass;
 
 enum {
@@ -67,7 +67,8 @@ gboolean                gvc_channel_map_can_fade                (GvcChannelMap
 gboolean                gvc_channel_map_has_lfe                 (GvcChannelMap  *map);
 
 void                    gvc_channel_map_volume_changed          (GvcChannelMap    *map,
-                                                                 const pa_cvolume *cv);
+                                                                 const pa_cvolume *cv,
+                                                                 gboolean          set);
 const char *            gvc_channel_map_get_mapping             (GvcChannelMap  *map);
 
 /* private */
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..6986202 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-control.c
@@ -614,7 +614,7 @@ update_sink (GvcMixerControl    *control,
 
         if (map == NULL)
                 map = gvc_mixer_stream_get_channel_map (stream);
-        gvc_channel_map_volume_changed (map, &info->volume);
+        gvc_channel_map_volume_changed (map, &info->volume, TRUE);
 }
 
 static void
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..0f8bea7 100644
--- a/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
+++ b/plugins/media-keys/cut-n-paste/gvc-mixer-stream.c
@@ -149,7 +149,7 @@ gvc_mixer_stream_set_volume (GvcMixerStream *stream,
         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);
+                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
                 g_object_notify (G_OBJECT (stream), "volume");
         }
 
@@ -168,7 +168,7 @@ gvc_mixer_stream_set_decibel (GvcMixerStream *stream,
         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);
+                gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE);
                 g_object_notify (G_OBJECT (stream), "volume");
         }
 
@@ -319,9 +319,11 @@ gvc_mixer_stream_set_application_id (GvcMixerStream *stream,
 
 static void
 on_channel_map_volume_changed (GvcChannelMap  *channel_map,
+                               gboolean        set,
                                GvcMixerStream *stream)
 {
-        gvc_mixer_stream_push_volume (stream);
+        if (set == TRUE)
+                gvc_mixer_stream_push_volume (stream);
 
         g_object_notify (G_OBJECT (stream), "volume");
 }
@@ -547,7 +549,15 @@ 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)
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 8438875..2b14bcc 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -688,9 +688,11 @@ do_sound_action (GsdMediaKeysManager *manager,
                         manager->priv->num_expected_update_signals = 2;
                         gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
                         gvc_mixer_stream_set_volume (manager->priv->stream, 0);
+                        gvc_mixer_stream_push_volume (manager->priv->stream);
                 } else if (!muted) {
                         manager->priv->num_expected_update_signals = 1;
                         gvc_mixer_stream_set_volume (manager->priv->stream, vol - norm_vol_step);
+                        gvc_mixer_stream_push_volume (manager->priv->stream);
                 }
                 break;
         case VOLUME_UP_KEY:
@@ -698,6 +700,7 @@ do_sound_action (GsdMediaKeysManager *manager,
                         if (vol == 0) {
                                 manager->priv->num_expected_update_signals = 2;
                                 gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
+                                gvc_mixer_stream_push_volume (manager->priv->stream);
                                 gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
                         } else {
                                 manager->priv->num_expected_update_signals = 1;
-- 
1.6.2.5


0004-Update-volume-control-code-for-new-API.patch:
 gsd-media-keys-manager.c |   89 ++++++++++++++---------------------------------
 1 file changed, 27 insertions(+), 62 deletions(-)

--- NEW FILE 0004-Update-volume-control-code-for-new-API.patch ---
>From 98664ae573c2fb96ff2ff56294c2d69e4bfcc73b Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess at hadess.net>
Date: Tue, 18 Aug 2009 14:00:09 +0100
Subject: [PATCH 4/4] Update volume control code for new API

Only push volume changes if they are necessary, and
remove the "expected signals" crud.

We shouldn't be waiting for PA to actually do the changes
before displaying them, and we should always show feedback
to the user when they pressed a key.
---
 plugins/media-keys/gsd-media-keys-manager.c |   88 ++++++++------------------
 1 files changed, 27 insertions(+), 61 deletions(-)

diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 2b14bcc..33b4675 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -87,9 +87,6 @@ struct GsdMediaKeysManagerPrivate
         /* Volume bits */
         GvcMixerControl *volume;
         GvcMixerStream  *stream;
-        /* Number of expected update signals, zero meaning we
-         * shouldn't be showing any update dialogues */
-        guint            num_expected_update_signals;
 #endif /* HAVE_PULSE */
         GtkWidget       *dialog;
         GConfClient     *conf_client;
@@ -616,23 +613,10 @@ do_eject_action (GsdMediaKeysManager *manager)
 
 #ifdef HAVE_PULSE
 static void
-update_dialog (GsdMediaKeysManager *manager)
+update_dialog (GsdMediaKeysManager *manager,
+               guint vol,
+               gboolean muted)
 {
-        gboolean muted;
-        guint vol;
-
-	/* Not expecting a dialogue to show up */
-        if (manager->priv->num_expected_update_signals == 0)
-                return;
-
-	/* If we aren't expecting any more updates, show the dialogue */
-        manager->priv->num_expected_update_signals--;
-        if (manager->priv->num_expected_update_signals != 0)
-                return;
-
-        vol = gvc_mixer_stream_get_volume (manager->priv->stream);
-        muted = gvc_mixer_stream_get_is_muted (manager->priv->stream);
-
         dialog_init (manager);
         gsd_media_keys_window_set_volume_muted (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
                                                 muted);
@@ -644,14 +628,6 @@ update_dialog (GsdMediaKeysManager *manager)
 }
 
 static void
-on_stream_event_notify (GObject             *object,
-                        GParamSpec          *pspec,
-                        GsdMediaKeysManager *manager)
-{
-        update_dialog (manager);
-}
-
-static void
 do_sound_action (GsdMediaKeysManager *manager,
                  int                  type)
 {
@@ -674,56 +650,52 @@ do_sound_action (GsdMediaKeysManager *manager,
         /* FIXME: this is racy */
         vol = gvc_mixer_stream_get_volume (manager->priv->stream);
         muted = gvc_mixer_stream_get_is_muted (manager->priv->stream);
-        /* By default, we would be showing a dialogue
-         * based on the current values, eg. an unchanged dialogue */
-        manager->priv->num_expected_update_signals = 0;
 
         switch (type) {
         case MUTE_KEY:
-                manager->priv->num_expected_update_signals = 1;
-                gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
+                muted = !muted;
+                gvc_mixer_stream_change_is_muted (manager->priv->stream, muted);
                 break;
         case VOLUME_DOWN_KEY:
                 if (!muted && (vol <= norm_vol_step)) {
-                        manager->priv->num_expected_update_signals = 2;
-                        gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
-                        gvc_mixer_stream_set_volume (manager->priv->stream, 0);
-                        gvc_mixer_stream_push_volume (manager->priv->stream);
+                        muted = !muted;
+                        vol = 0;
+                        gvc_mixer_stream_change_is_muted (manager->priv->stream, muted);
+                        if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE)
+                                gvc_mixer_stream_push_volume (manager->priv->stream);
                 } else if (!muted) {
-                        manager->priv->num_expected_update_signals = 1;
-                        gvc_mixer_stream_set_volume (manager->priv->stream, vol - norm_vol_step);
-                        gvc_mixer_stream_push_volume (manager->priv->stream);
+                        vol = vol - norm_vol_step;
+                        if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE)
+                                gvc_mixer_stream_push_volume (manager->priv->stream);
                 }
                 break;
         case VOLUME_UP_KEY:
                 if (muted) {
+                        muted = !muted;
                         if (vol == 0) {
-                                manager->priv->num_expected_update_signals = 2;
-                                gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
-                                gvc_mixer_stream_push_volume (manager->priv->stream);
-                                gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
+                               vol = vol + norm_vol_step;
+                               gvc_mixer_stream_change_is_muted (manager->priv->stream, muted);
+                               if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE)
+                                        gvc_mixer_stream_push_volume (manager->priv->stream);
                         } else {
-                                manager->priv->num_expected_update_signals = 1;
-                                gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
+                                gvc_mixer_stream_change_is_muted (manager->priv->stream, muted);
                         }
                 } else {
                         if (vol < MAX_VOLUME) {
-                                manager->priv->num_expected_update_signals = 1;
+                                gboolean set;
                                 if (vol + norm_vol_step >= MAX_VOLUME) {
-                                        gvc_mixer_stream_set_volume (manager->priv->stream, MAX_VOLUME);
+                                        vol = MAX_VOLUME;
                                 } else {
-                                        gvc_mixer_stream_set_volume (manager->priv->stream, vol + norm_vol_step);
+                                        vol = vol + norm_vol_step;
                                 }
+                                if (gvc_mixer_stream_set_volume (manager->priv->stream, vol) != FALSE)
+                                        gvc_mixer_stream_push_volume (manager->priv->stream);
                         }
                 }
                 break;
         }
 
-        /* We didn't actually make any changes, so force showing the dialogue */
-        if (manager->priv->num_expected_update_signals == 0) {
-                manager->priv->num_expected_update_signals = 1;
-                update_dialog (manager);
-        }
+        update_dialog (manager, vol, muted);
 }
 
 static void
@@ -736,18 +708,12 @@ update_default_sink (GsdMediaKeysManager *manager)
                 return;
 
         if (manager->priv->stream != NULL) {
-                g_signal_handlers_disconnect_by_func (G_OBJECT (manager->priv->stream),
-                                                      G_CALLBACK (on_stream_event_notify), manager);
                 g_object_unref (manager->priv->stream);
                 manager->priv->stream = NULL;
         }
 
         if (stream != NULL) {
                 manager->priv->stream = g_object_ref (stream);
-                g_signal_connect (G_OBJECT (manager->priv->stream), "notify::volume",
-                                  G_CALLBACK (on_stream_event_notify), manager);
-                g_signal_connect (G_OBJECT (manager->priv->stream), "notify::is-muted",
-                                  G_CALLBACK (on_stream_event_notify), manager);
         } else {
                 g_warning ("Unable to get default sink");
         }
@@ -755,7 +721,7 @@ update_default_sink (GsdMediaKeysManager *manager)
 
 static void
 on_control_ready (GvcMixerControl     *control,
-		  GsdMediaKeysManager *manager)
+                  GsdMediaKeysManager *manager)
 {
         update_default_sink (manager);
 }
@@ -1081,7 +1047,7 @@ gsd_media_keys_manager_start (GsdMediaKeysManager *manager,
          */
         gnome_settings_profile_start ("gvc_mixer_control_new");
 
-        manager->priv->volume = gvc_mixer_control_new ();
+        manager->priv->volume = gvc_mixer_control_new ("GNOME Volume Control Media Keys");
 
         g_signal_connect (manager->priv->volume,
                           "ready",
-- 
1.6.2.5



Index: gnome-settings-daemon.spec
===================================================================
RCS file: /cvs/pkgs/rpms/gnome-settings-daemon/F-11/gnome-settings-daemon.spec,v
retrieving revision 1.109
retrieving revision 1.110
diff -u -p -r1.109 -r1.110
--- gnome-settings-daemon.spec	26 Aug 2009 04:00:05 -0000	1.109
+++ gnome-settings-daemon.spec	7 Sep 2009 15:02:29 -0000	1.110
@@ -1,6 +1,6 @@
 Name:		gnome-settings-daemon
 Version:	2.26.1
-Release:	9%{?dist}
+Release:	10%{?dist}
 Summary:	The daemon sharing settings from GNOME to GTK+/KDE applications
 
 Group:		System Environment/Daemons
@@ -53,6 +53,12 @@ Patch16:	there-can-be-only-one.patch
 Patch17:	syndaemon-k.patch
 Patch18:	buttonmapping.patch
 
+# Updated from 2.27 branch
+Patch90:	0001-Update-gnome-volume-control-code-from-master.patch
+Patch91:	0002-Bug-590073-gnome-settings-daemon-crashed-with-SI.patch
+Patch92:	0003-Update-gnome-volume-control-code.patch
+Patch93:	0004-Update-volume-control-code-for-new-API.patch
+
 %description
 A daemon to share settings from GNOME to other applications. It also
 handles global keybindings, as well as a number of desktop-wide settings.
@@ -81,6 +87,11 @@ developing applications that use %{name}
 %patch17 -p1 -b .syndaemon-k
 %patch18 -p1 -b .buttonmapping
 
+%patch90 -p1
+%patch91 -p1
+%patch92 -p1
+%patch93 -p1
+
 autoreconf -i -f
 
 %build
@@ -192,6 +203,9 @@ fi
 %{_libdir}/pkgconfig/gnome-settings-daemon.pc
 
 %changelog
+* Mon Sep 07 2009 Bastien Nocera <bnocera at redhat.com> 2.26.1-10
+- Update volume code from 2.27
+
 * Wed Aug 26 2009 Peter Hutterer <peter.hutterer at redhat.com> 2.26.1-9
 - buttonmapping.patch: Don't check for IsXExtensionDevice, only skip button
   mappings for core devices instead (#502129).




More information about the fedora-extras-commits mailing list