 configure.ac                                |   41 +++
 plugins/media-keys/Makefile.am              |   23 ++-
 plugins/media-keys/actions/Makefile.am      |   44 +++
 plugins/media-keys/actions/acme-volume.c    |  403 +++++++++++++++++++++++++++
 plugins/media-keys/actions/acme-volume.h    |   56 ++++
 plugins/media-keys/gsd-media-keys-manager.c |  104 ++++++--
 6 files changed, 647 insertions(+), 24 deletions(-)

diff --git a/configure.ac b/configure.ac
index 9cff33f..913ceab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -253,11 +253,50 @@ if test x$WANT_PULSE = xyes ; then
               AC_DEFINE(HAVE_PULSE, 1, [Define if PULSE sound server should be used])],
              [have_pulse=false])
 fi
+
 AM_CONDITIONAL(HAVE_PULSE, test "x$have_pulse" = "xtrue")
 
 AC_SUBST(PULSE_CFLAGS)
 AC_SUBST(PULSE_LIBS)
 
+dnl ==============================================
+dnl GStreamer section
+dnl ==============================================
+GST_MAJORMINOR=auto
+
+AC_ARG_ENABLE(gstreamer,
+AC_HELP_STRING([--enable-gstreamer],[use gstreamer if available (and optionally specify a version)]),
+[case "${enableval}" in
+ yes) ENABLE_GSTREAMER=yes ;;
+ 0.10) ENABLE_GSTREAMER=yes && GST_MAJORMINOR=0.10 ;;
+ no)  ENABLE_GSTREAMER=no ;;
+ *) AC_MSG_ERROR([
+                  *** Bad value ${enableval} for --enable-gstreamer
+                  *** Please use one of the following:
+                  ***    --enable-gstreamer=0.10
+               ]) ;;
+esac],
+[ENABLE_GSTREAMER=yes]) dnl Default value
+
+have_gstreamer=no
+if test "x$ENABLE_GSTREAMER" = "xyes"; then
+   GST_REQS=0.10.1.2
+   PKGS="gstreamer-0.10 >= $GST_REQS gstreamer-plugins-base-0.10 >= $GST_REQS"
+
+   PKG_CHECK_MODULES(GST, $PKGS, have_gstreamer=yes,
+                     AC_MSG_RESULT([no]))
+
+   if test "x$have_gstreamer" = "xyes"; then
+      GST_LIBS="$GST_LIBS -lgstinterfaces-0.10 -lgstaudio-0.10"
+      AC_DEFINE(HAVE_GSTREAMER,1,[enable gstreamer])
+   fi
+else
+   AC_MSG_NOTICE([*** GStreamer support disabled ***])
+fi
+AM_CONDITIONAL(HAVE_GSTREAMER, test "x$have_gstreamer" = "xyes")
+AC_SUBST(GST_LIBS)
+AC_SUBST(GST_CFLAGS)
+
 # ---------------------------------------------------------------------------
 # Enable Profiling
 # ---------------------------------------------------------------------------
@@ -355,6 +394,7 @@ plugins/housekeeping/Makefile
 plugins/keybindings/Makefile
 plugins/keyboard/Makefile
 plugins/media-keys/Makefile
+plugins/media-keys/actions/Makefile
 plugins/media-keys/cut-n-paste/Makefile
 plugins/mouse/Makefile
 plugins/screensaver/Makefile
@@ -397,6 +437,7 @@ echo "
         dbus-1 system.d dir:      ${DBUS_SYS_DIR}
 
         Libnotify support:        ${have_libnotify}
+        GStreamer support:        ${have_gstreamer}
         PulseAudio support:       ${have_pulse}
         Profiling support:        ${enable_profiling}
 "
diff --git a/plugins/media-keys/Makefile.am b/plugins/media-keys/Makefile.am
index b308edb..34aa464 100644
--- a/plugins/media-keys/Makefile.am
+++ b/plugins/media-keys/Makefile.am
@@ -1,11 +1,12 @@
 NULL =
 
 SUBDIRS =
-plugin_LTLIBRARIES =
+plugin_LTLIBRARIES = libmedia-keys.la
 
 if HAVE_PULSE
 SUBDIRS += cut-n-paste
-plugin_LTLIBRARIES += libmedia-keys.la
+else
+SUBDIRS += actions
 endif
 
 BUILT_SOURCES = 			\
@@ -39,7 +40,6 @@ libmedia_keys_la_SOURCES = 		\
 libmedia_keys_la_CPPFLAGS = \
 	-I$(top_srcdir)/gnome-settings-daemon			\
 	-I$(top_srcdir)/plugins/common				\
-	-I$(top_srcdir)/plugins/media-keys/cut-n-paste		\
 	-DPIXMAPDIR=\""$(pkgdatadir)"\"				\
 	-DGLADEDIR=\""$(pkgdatadir)"\"				\
 	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\"	\
@@ -55,7 +55,6 @@ libmedia_keys_la_LDFLAGS = 		\
 libmedia_keys_la_LIBADD  = 		\
 	$(top_builddir)/gnome-settings-daemon/libgsd-plugins.la		\
 	$(top_builddir)/plugins/common/libcommon.la			\
-	$(top_builddir)/plugins/media-keys/cut-n-paste/libgvc.la	\
 	$(SETTINGS_PLUGIN_LIBS)						\
 	$(XF86MISC_LIBS)
 
@@ -77,7 +76,6 @@ test_media_window_SOURCES =			\
 
 test_media_window_CPPFLAGS = \
 	-I$(top_srcdir)/gnome-settings-daemon			\
-	-I$(top_srcdir)/plugins/media-keys/cut-n-paste		\
 	-DPIXMAPDIR=\""$(pkgdatadir)"\"				\
 	-DGLADEDIR=\""$(pkgdatadir)"\"				\
 	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\"	\
@@ -105,7 +103,6 @@ test_media_keys_SOURCES =			\
 test_media_keys_CPPFLAGS = \
 	-I$(top_srcdir)/gnome-settings-daemon			\
 	-I$(top_srcdir)/plugins/common				\
-	-I$(top_srcdir)/plugins/media-keys/cut-n-paste		\
 	-DPIXMAPDIR=\""$(pkgdatadir)"\"				\
 	-DGLADEDIR=\""$(pkgdatadir)"\"				\
 	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\"	\
@@ -124,7 +121,17 @@ test_media_keys_LDADD = \
 	$(GST_LIBS)
 
 if HAVE_PULSE
+libmedia_keys_la_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/cut-n-paste
+libmedia_keys_la_LIBADD += $(top_builddir)/plugins/media-keys/cut-n-paste/libgvc.la
+test_media_window_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/cut-n-paste
+test_media_keys_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/cut-n-paste
 test_media_keys_LDADD += $(top_builddir)/plugins/media-keys/cut-n-paste/libgvc.la
+else
+libmedia_keys_la_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/actions
+libmedia_keys_la_LIBADD += $(top_builddir)/plugins/media-keys/actions/libacme.la
+test_media_window_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/actions
+test_media_keys_CPPFLAGS += -I$(top_builddir)/plugins/media-keys/actions
+test_media_keys_LDADD += $(top_builddir)/plugins/media-keys/actions/libacme.la
 endif
 
 gladedir = $(pkgdatadir)
@@ -137,7 +144,11 @@ pixmaps_DATA = 		\
 	acme-eject.png	\
 	$(NULL)
 
+if HAVE_PULSE
 DIST_SUBDIRS = cut-n-paste
+else
+DIST_SUBDIRS = actions
+endif
 
 EXTRA_DIST = 				\
 	gsd-media-keys-manager.xml	\
diff --git a/plugins/media-keys/actions/Makefile.am b/plugins/media-keys/actions/Makefile.am
new file mode 100644
index 0000000..4bf215a
--- /dev/null
+++ b/plugins/media-keys/actions/Makefile.am
@@ -0,0 +1,44 @@
+NULL =
+
+noinst_LTLIBRARIES = libacme.la
+
+libacme_la_SOURCES = 		\
+	../acme.h		\
+	$(NULL)
+
+libacme_la_CPPFLAGS = \
+	-I$(top_srcdir)/gnome-settings-daemon		\
+	-I$(top_srcdir)/plugins/common			\
+	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \
+	$(AM_CPPFLAGS)
+
+libacme_la_CFLAGS = 			\
+	$(SETTINGS_PLUGIN_CFLAGS)	\
+	$(AM_CFLAGS)
+
+libacme_la_LIBADD =			\
+	$(NULL)
+
+if HAVE_GSTREAMER
+libacme_la_SOURCES += \
+	acme-volume.c \
+	acme-volume.h \
+	$(NULL)
+libacme_la_CFLAGS += $(GST_CFLAGS)
+libacme_la_LIBADD += $(GST_LIBS)
+endif
+
+gladedir = $(pkgdatadir)
+glade_DATA = 		\
+	../acme.glade	\
+	$(NULL)
+
+pixmapsdir = $(pkgdatadir)
+pixmaps_DATA = 		\
+	../acme-eject.png	\
+	$(NULL)
+
+EXTRA_DIST =			\
+	$(glade_DATA)		\
+	$(pixmaps_DATA)		\
+	$(NULL)
diff --git a/plugins/media-keys/actions/acme-volume.c b/plugins/media-keys/actions/acme-volume.c
new file mode 100644
index 0000000..77e548a
--- /dev/null
+++ b/plugins/media-keys/actions/acme-volume.c
@@ -0,0 +1,403 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/* acme-volume.c
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+   Copyright (C) 2004 Novell, Inc.
+   Copyright (C) 2009 PERIER Romain <mrpouet@tuxfamily.org>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+           Jon Trowbridge <trow@ximian.com>
+*/
+
+#include "config.h"
+#include "acme-volume.h"
+
+#include <gst/gst.h>
+#include <gst/audio/mixerutils.h>
+#include <gst/interfaces/mixer.h>
+#include <gst/interfaces/propertyprobe.h>
+
+#include <gconf/gconf-client.h>
+
+#include <string.h>
+
+#define TIMEOUT	4
+
+#define DEFAULT_MIXER_DEVICE_KEY   "/desktop/gnome/sound/default_mixer_device"
+#define DEFAULT_MIXER_TRACKS_KEY   "/desktop/gnome/sound/default_mixer_tracks"
+
+#define ACME_VOLUME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ACME_TYPE_VOLUME, AcmeVolumePrivate))
+
+struct AcmeVolumePrivate {
+	GstMixer     *mixer;
+	GList        *mixer_tracks;
+	guint         timer_id;
+	gdouble       volume;
+	gboolean      mute;
+	GConfClient  *gconf_client;
+};
+
+G_DEFINE_TYPE (AcmeVolume, acme_volume, G_TYPE_OBJECT)
+
+static gboolean acme_volume_open  (AcmeVolume *acme);
+static void     acme_volume_close (AcmeVolume *acme);
+static gboolean acme_volume_close_real (AcmeVolume *self);
+
+static gpointer acme_volume_object = NULL;
+
+static void
+acme_volume_finalize (GObject *object)
+{
+	AcmeVolume *self;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (ACME_IS_VOLUME (object));
+
+	self = ACME_VOLUME (object);
+
+	if (self->_priv->timer_id != 0)
+		g_source_remove (self->_priv->timer_id);
+	acme_volume_close_real (self);
+
+	if (self->_priv->gconf_client != NULL) {
+		g_object_unref (self->_priv->gconf_client);
+		self->_priv->gconf_client = NULL;
+	}
+
+	G_OBJECT_CLASS (acme_volume_parent_class)->finalize (object);
+}
+
+void
+acme_volume_set_mute (AcmeVolume *self, gboolean val)
+{
+	GList *t;
+
+	g_return_if_fail(ACME_IS_VOLUME(self));
+	g_return_if_fail(acme_volume_open(self));
+
+	for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
+		GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+		gst_mixer_set_mute (self->_priv->mixer, track, val);
+	}
+	self->_priv->mute = val;
+	acme_volume_close (self);
+}
+
+static void
+update_state (AcmeVolume * self)
+{
+	gint *volumes, n;
+	gdouble vol = 0;
+	GstMixerTrack *track = GST_MIXER_TRACK (self->_priv->mixer_tracks->data);
+
+	/* update mixer by getting volume */
+	volumes = g_new0 (gint, track->num_channels);
+	gst_mixer_get_volume (self->_priv->mixer, track, volumes);
+	for (n = 0; n < track->num_channels; n++)
+		vol += volumes[n];
+	g_free (volumes);
+	vol /= track->num_channels;
+	vol = 100 * vol / (track->max_volume - track->min_volume);
+
+	/* update mute flag, and volume if not muted */
+	if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))
+		self->_priv->mute = TRUE;
+	self->_priv->volume = vol;
+}
+
+gboolean
+acme_volume_get_mute (AcmeVolume *self)
+{
+	g_return_val_if_fail(acme_volume_open(self), FALSE);
+
+	update_state (self);
+	acme_volume_close (self);
+
+	return self->_priv->mute;
+}
+
+gint
+acme_volume_get_volume (AcmeVolume *self)
+{
+
+	g_return_val_if_fail(acme_volume_open(self), 0);
+
+	update_state (self);
+
+	acme_volume_close (self);
+	
+	return (gint) (self->_priv->volume + 0.5);
+}
+
+void
+acme_volume_set_volume (AcmeVolume *self, gint val)
+{
+	GList *t;
+
+	g_return_if_fail(acme_volume_open(self));
+
+	val = CLAMP (val, 0, 100);
+
+	for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
+		GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+		gint *volumes, n;
+		gdouble scale = (track->max_volume - track->min_volume) / 100.0;
+		gint vol = (gint) (val * scale + track->min_volume + 0.5);
+
+		volumes = g_new (gint, track->num_channels);
+		for (n = 0; n < track->num_channels; n++)
+			volumes[n] = vol;
+		gst_mixer_set_volume (self->_priv->mixer, track, volumes);
+		g_free (volumes);
+	}
+
+	/* update state */
+	self->_priv->volume = val;
+
+	acme_volume_close (self);
+}
+
+void
+acme_volume_mute_toggle (AcmeVolume *self)
+{
+	gboolean muted;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (ACME_IS_VOLUME(self));
+
+	muted = acme_volume_get_mute(self);
+	acme_volume_set_mute(self, !muted);
+}
+
+gint
+acme_volume_get_threshold (AcmeVolume *self)
+{
+	GList *t;
+	gint steps = 101;
+
+	g_return_val_if_fail(acme_volume_open(self), 1);
+
+	for (t = self->_priv->mixer_tracks; t != NULL; t = t->next) {
+		GstMixerTrack *track = GST_MIXER_TRACK (t->data);
+		gint track_steps = track->max_volume - track->min_volume;
+		if (track_steps > 0 && track_steps < steps)
+			steps = track_steps;
+	}
+
+	acme_volume_close (self);
+
+	return 100 / steps + 1;
+}
+
+static gboolean
+acme_volume_close_real (AcmeVolume *self)
+{
+	if (self->_priv->mixer != NULL)
+	{
+		gst_element_set_state (GST_ELEMENT (self->_priv->mixer), GST_STATE_NULL);
+		gst_object_unref (GST_OBJECT (self->_priv->mixer));
+		g_list_foreach (self->_priv->mixer_tracks, (GFunc) g_object_unref, NULL);
+		g_list_free (self->_priv->mixer_tracks);
+		self->_priv->mixer = NULL;
+		self->_priv->mixer_tracks = NULL;
+	}
+
+	self->_priv->timer_id = 0;
+	return FALSE;
+}
+
+/*
+ * _acme_set_mixer
+ * @mixer  A pointer to mixer element
+ * @data   A pointer to user data (AcmeVolume instance to be modified)
+ * @return A gboolean indicating success if Master track was found, failed otherwises.
+ */
+static gboolean
+_acme_set_mixer(GstMixer *mixer, gpointer user_data)
+{
+	const GList *tracks;
+
+	for (tracks = gst_mixer_list_tracks (mixer); tracks != NULL; tracks = tracks->next) {
+		GstMixerTrack *track = GST_MIXER_TRACK (tracks->data);
+
+		if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
+			AcmeVolume *self;
+
+			self = ACME_VOLUME (user_data);
+
+			self->_priv->mixer = mixer;
+			self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
+			return TRUE;
+		}
+
+		continue;
+	}
+
+	return FALSE;
+}
+
+/* This is a modified version of code from gnome-media's gst-mixer */
+static gboolean
+acme_volume_open (AcmeVolume *self)
+{
+	gchar *mixer_device, **factory_and_device = NULL;
+	GList *mixer_list;
+
+	if (self->_priv->timer_id != 0) {
+		g_source_remove (self->_priv->timer_id);
+		self->_priv->timer_id = 0;
+		return TRUE;
+	}
+
+	mixer_device = gconf_client_get_string (self->_priv->gconf_client, DEFAULT_MIXER_DEVICE_KEY, NULL);
+	if (mixer_device != NULL)
+		factory_and_device = g_strsplit (mixer_device, ":", 2);
+
+	if (factory_and_device != NULL && factory_and_device[0] != NULL) {
+		GstElement *element;
+
+		element = gst_element_factory_make (factory_and_device[0], NULL);
+
+		if (element != NULL) {
+			if (factory_and_device[1] != NULL &&
+			    g_object_class_find_property (G_OBJECT_GET_CLASS (element), "device"))
+				g_object_set (G_OBJECT (element), "device", factory_and_device[1], NULL);
+			gst_element_set_state (element, GST_STATE_READY);
+
+			if (GST_IS_MIXER (element))
+				self->_priv->mixer = GST_MIXER (element);
+			else {
+				gst_element_set_state (element, GST_STATE_NULL);
+				gst_object_unref (element);
+			}
+		}
+	}
+
+	g_free (mixer_device);
+	g_strfreev (factory_and_device);
+
+	if (self->_priv->mixer != NULL) {
+		const GList *m;
+		GSList *tracks, *t;
+		GError *error;
+
+		/* Try to use tracks saved in GConf 
+		   Note: errors need to be treated , for example if the user set a non type list for this key
+		   or if the elements type_list are not "matched" */
+		error = NULL;
+		tracks = gconf_client_get_list (self->_priv->gconf_client, DEFAULT_MIXER_TRACKS_KEY, 
+						GCONF_VALUE_STRING, &error);
+
+		if (error) {
+			g_warning("ERROR: %s\n", error->message);
+			g_error_free(error);
+		}
+
+		/* We use these tracks ONLY if they are supported on the system with the following mixer */
+		for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next) {
+			GstMixerTrack *track = GST_MIXER_TRACK (m->data);
+
+			for (t = tracks; t != NULL; t = t->next)
+				if (!strcmp (t->data, track->label))
+					self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
+
+		}
+
+		g_slist_foreach (tracks, (GFunc)g_free, NULL);
+		g_slist_free (tracks);
+
+		/* If no track stored in GConf is avaiable try to use Master track */
+		if (self->_priv->mixer_tracks == NULL) {
+			for (m = gst_mixer_list_tracks (self->_priv->mixer); m != NULL; m = m->next) {
+				GstMixerTrack *track = GST_MIXER_TRACK (m->data);
+
+				if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
+					self->_priv->mixer_tracks = g_list_append (self->_priv->mixer_tracks, g_object_ref (track));
+					break;
+				}
+			}
+		}
+
+		if (self->_priv->mixer_tracks != NULL)
+			return TRUE;
+		else {
+			gst_element_set_state (GST_ELEMENT (self->_priv->mixer), GST_STATE_NULL);
+			gst_object_unref (self->_priv->mixer);
+		}
+	}
+
+	/* Go through all elements of a certain class and check whether
+	 * they implement a mixer. If so, walk through the tracks and look
+	 * for first one named "volume".
+	 *
+	 * We should probably do something intelligent if we don't find an
+	 * appropriate mixer/track.  But now we do something stupid...
+	 * everything just becomes a no-op.
+	 */
+	mixer_list = gst_audio_default_registry_mixer_filter (_acme_set_mixer,
+			TRUE,
+			self);
+
+	if (mixer_list == NULL)
+		return FALSE;
+
+	/* do not unref the mixer as we keep the ref for self->priv->mixer */
+	g_list_free (mixer_list);
+
+	return TRUE;
+}
+
+static void
+acme_volume_close (AcmeVolume *self)
+{
+	self->_priv->timer_id = g_timeout_add_seconds (TIMEOUT,
+			(GSourceFunc) acme_volume_close_real, self);
+}
+
+static void
+acme_volume_init (AcmeVolume *self)
+{
+	self->_priv = ACME_VOLUME_GET_PRIVATE (self);
+	self->_priv->gconf_client = gconf_client_get_default ();
+}
+
+static void
+acme_volume_class_init (AcmeVolumeClass *klass)
+{
+	G_OBJECT_CLASS (klass)->finalize = acme_volume_finalize;
+
+	gst_init (NULL, NULL);
+
+	g_type_class_add_private (klass, sizeof (AcmeVolumePrivate));
+}
+
+/* acme_volume_new
+ * @return A singleton instance of type AcmeVolume
+ */
+AcmeVolume *
+acme_volume_new (void)
+{
+	if (acme_volume_object == NULL) {
+		acme_volume_object = g_object_new (ACME_TYPE_VOLUME, NULL);
+		return ACME_VOLUME(acme_volume_object);
+	}
+	g_object_ref(acme_volume_object);
+	return ACME_VOLUME(acme_volume_object);
+}
+
diff --git a/plugins/media-keys/actions/acme-volume.h b/plugins/media-keys/actions/acme-volume.h
new file mode 100644
index 0000000..c14ebc8
--- /dev/null
+++ b/plugins/media-keys/actions/acme-volume.h
@@ -0,0 +1,56 @@
+/* acme-volume.h
+
+   Copyright (C) 2002, 2003 Bastien Nocera
+   Copyright (C) 2004 Novell, Inc.
+   Copyright (C) 2009 PERIER Romain <mrpouet@tuxfamily.org>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Bastien Nocera <hadess@hadess.net>
+           Jon Trowbridge <trow@ximian.com>
+ */
+
+#include <glib-object.h>
+
+#define ACME_TYPE_VOLUME                        (acme_volume_get_type ())
+#define ACME_VOLUME(obj)		        (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME, AcmeVolume))
+#define ACME_VOLUME_CLASS(klass)	        (G_TYPE_CHECK_CLASS_CAST ((klass),  ACME_TYPE_VOLUME, AcmeVolumeClass))
+#define ACME_IS_VOLUME(obj)	                (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME))
+#define ACME_VOLUME_GET_CLASS(obj)	        (G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME, AcmeVolumeClass))
+
+typedef struct AcmeVolume AcmeVolume;
+typedef struct AcmeVolumeClass AcmeVolumeClass;
+typedef struct AcmeVolumePrivate AcmeVolumePrivate;
+
+struct AcmeVolume {
+	GObject parent;
+	AcmeVolumePrivate *_priv;
+};
+
+struct AcmeVolumeClass {
+	GObjectClass parent;
+};
+
+GType       acme_volume_get_type      (void);
+AcmeVolume *acme_volume_new           (void);
+void        acme_volume_set_mute      (AcmeVolume *self, gboolean val);
+void        acme_volume_mute_toggle   (AcmeVolume *self);
+gboolean    acme_volume_get_mute      (AcmeVolume *self);
+void        acme_volume_set_volume    (AcmeVolume *self, gint val);
+gint        acme_volume_get_volume    (AcmeVolume *self);
+gint        acme_volume_get_threshold (AcmeVolume *self);
+
+
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index d35048c..ff5ee0e 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -48,11 +48,12 @@
 
 #include "eggaccelerators.h"
 #include "acme.h"
-#include "gsd-media-keys-window.h"
-
 #ifdef HAVE_PULSE
 #include "gvc-mixer-control.h"
+#elif defined(HAVE_GSTREAMER)
+#include "actions/acme-volume.h"
 #endif /* HAVE_PULSE */
+#include "gsd-media-keys-window.h"
 
 #define GSD_DBUS_PATH "/org/gnome/SettingsDaemon"
 #define GSD_DBUS_NAME "org.gnome.SettingsDaemon"
@@ -90,6 +91,8 @@ struct GsdMediaKeysManagerPrivate
         /* Number of expected update signals, zero meaning we
          * shouldn't be showing any update dialogues */
         guint            num_expected_update_signals;
+#elif defined(HAVE_GSTREAMER)
+        AcmeVolume      *volume;
 #endif /* HAVE_PULSE */
         GtkWidget       *dialog;
         GConfClient     *conf_client;
@@ -131,9 +134,8 @@ init_screens (GsdMediaKeysManager *manager)
                 GdkScreen *screen;
 
                 screen = gdk_display_get_screen (display, i);
-                if (screen == NULL) {
+                if (screen == NULL)
                         continue;
-                }
                 manager->priv->screens = g_slist_append (manager->priv->screens, screen);
         }
 
@@ -650,40 +652,67 @@ on_stream_event_notify (GObject             *object,
 {
         update_dialog (manager);
 }
+#endif /* HAVE_PULSE */
 
+#if defined(HAVE_PULSE) || defined(HAVE_GSTREAMER)
 static void
 do_sound_action (GsdMediaKeysManager *manager,
                  int                  type)
 {
         gboolean muted;
+        GError *error = NULL;
+#ifdef HAVE_PULSE
         guint vol, norm_vol_step;
         int vol_step;
-
         if (manager->priv->stream == NULL)
+#else
+        int      vol;
+        int      vol_step;
+        if (manager->priv->volume == NULL)
+#endif
                 return;
 
-        vol_step = gconf_client_get_int (manager->priv->conf_client,
+         vol_step = gconf_client_get_int (manager->priv->conf_client,
                                          GCONF_MISC_DIR "/volume_step",
-                                         NULL);
-
-        if (vol_step <= 0 || vol_step > 100)
+                                         &error);
+#ifdef HAVE_PULSE
+        if (vol_step <= 0 || vol_step > 100) {
+#else
+        if (error) {
+#endif
                 vol_step = VOLUME_STEP;
+                g_error_free (error);
+	}
 
+#ifdef HAVE_PULSE
         norm_vol_step = PA_VOLUME_NORM * vol_step / 100;
-
-        /* 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;
-
+#else
+        if (vol_step > 0) {
+                int threshold = acme_volume_get_threshold (manager->priv->volume);
+                if (vol_step < threshold)
+                        vol_step = threshold;
+                g_debug ("Using volume step of %d", vol_step);
+        }
+        vol = acme_volume_get_volume (manager->priv->volume);
+        muted = acme_volume_get_mute (manager->priv->volume);
+#endif
         switch (type) {
         case MUTE_KEY:
+#ifdef HAVE_PULSE
                 manager->priv->num_expected_update_signals = 1;
                 gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
+#else
+                acme_volume_mute_toggle (manager->priv->volume);
+#endif
                 break;
         case VOLUME_DOWN_KEY:
+#ifdef HAVE_PULSE
                 if (!muted && (vol <= norm_vol_step)) {
                         manager->priv->num_expected_update_signals = 2;
                         gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
@@ -692,18 +721,31 @@ do_sound_action (GsdMediaKeysManager *manager,
                         manager->priv->num_expected_update_signals = 1;
                         gvc_mixer_stream_change_volume (manager->priv->stream, vol - norm_vol_step);
                 }
+#else
+                if (!muted && (vol <= vol_step)) {
+                        acme_volume_mute_toggle (manager->priv->volume);
+                }
+                acme_volume_set_volume (manager->priv->volume, vol - vol_step);
+#endif
                 break;
         case VOLUME_UP_KEY:
                 if (muted) {
                         if (vol == 0) {
+#ifdef HAVE_PULSE
                                 manager->priv->num_expected_update_signals = 2;
                                 gvc_mixer_stream_change_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;
                                 gvc_mixer_stream_change_is_muted (manager->priv->stream, !muted);
+#else
+                                acme_volume_set_volume (manager->priv->volume, vol + vol_step);
+                                /* We need to unmute otherwise vol is blocked (and muted) */
+                                acme_volume_set_mute   (manager->priv->volume, FALSE);
+#endif
                         }
-                } else {
+                } else { 
+#ifdef HAVE_PULSE
                         if (vol < MAX_VOLUME) {
                                 manager->priv->num_expected_update_signals = 1;
                                 if (vol + norm_vol_step >= MAX_VOLUME) {
@@ -712,17 +754,39 @@ do_sound_action (GsdMediaKeysManager *manager,
                                         gvc_mixer_stream_change_volume (manager->priv->stream, vol + norm_vol_step);
                                 }
                         }
+#else
+                        acme_volume_set_volume (manager->priv->volume, vol + vol_step);
+#endif
                 }
                 break;
         }
 
+#ifdef HAVE_PULSE
         /* 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);
         }
-}
+#else
+        muted = acme_volume_get_mute (manager->priv->volume);
+        vol = acme_volume_get_volume (manager->priv->volume);
+
+        /* FIXME: AcmeVolume should probably emit signals
+           instead of doing it like this */
+        dialog_init (manager);
+        gsd_media_keys_window_set_volume_muted (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
+                                                muted);
+        gsd_media_keys_window_set_volume_level (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
+                                                vol);
+        gsd_media_keys_window_set_action (GSD_MEDIA_KEYS_WINDOW (manager->priv->dialog),
+                                          GSD_MEDIA_KEYS_WINDOW_ACTION_VOLUME);
+        dialog_show (manager);
+#endif
+        }
+
+#endif /* defined(HAVE_PULSE) || defined(HAVE_GSTREAMER) */
 
+#ifdef HAVE_PULSE
 static void
 update_default_sink (GsdMediaKeysManager *manager)
 {
@@ -887,9 +951,9 @@ do_action (GsdMediaKeysManager *manager,
         case MUTE_KEY:
         case VOLUME_DOWN_KEY:
         case VOLUME_UP_KEY:
-#ifdef HAVE_PULSE
+#if defined(HAVE_PULSE) || defined(HAVE_GSTREAMER)
                 do_sound_action (manager, type);
-#endif /* HAVE_PULSE */
+#endif
                 break;
         case POWER_KEY:
                 do_exit_action (manager);
@@ -1069,13 +1133,13 @@ gsd_media_keys_manager_start (GsdMediaKeysManager *manager,
 {
         gnome_settings_profile_start (NULL);
 
-#ifdef HAVE_PULSE
         /* initialise Volume handler
          *
          * We do this one here to force checking gstreamer cache, etc.
          * The rest (grabbing and setting the keys) can happen in an
          * idle.
          */
+#ifdef HAVE_PULSE
         gnome_settings_profile_start ("gvc_mixer_control_new");
 
         manager->priv->volume = gvc_mixer_control_new ();
@@ -1092,6 +1156,10 @@ gsd_media_keys_manager_start (GsdMediaKeysManager *manager,
         gvc_mixer_control_open (manager->priv->volume);
 
         gnome_settings_profile_end ("gvc_mixer_control_new");
+#elif defined(HAVE_GSTREAMER)
+        gnome_settings_profile_start ("acme_volume_new");
+        manager->priv->volume = acme_volume_new ();
+        gnome_settings_profile_end ("acme_volume_new");
 #endif /* HAVE_PULSE */
         g_idle_add ((GSourceFunc) start_media_keys_idle_cb, manager);
 
@@ -1164,13 +1232,13 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
                 g_object_unref (priv->stream);
                 priv->stream = NULL;
         }
+#elif defined(HAVE_GSTREAMER)
 
         if (priv->volume) {
                 g_object_unref (priv->volume);
                 priv->volume = NULL;
         }
 #endif /* HAVE_PULSE */
-
         if (priv->dialog != NULL) {
                 gtk_widget_destroy (priv->dialog);
                 priv->dialog = NULL;
