Index: gnome-control-center/gnome-settings-daemon/actions/acme-volume-gstreamer.c
===================================================================
--- gnome-control-center.orig/gnome-settings-daemon/actions/acme-volume-gstreamer.c	2005-06-05 15:07:34.000000000 +0200
+++ gnome-control-center/gnome-settings-daemon/actions/acme-volume-gstreamer.c	2005-06-10 18:07:18.478980040 +0200
@@ -31,16 +31,19 @@
 #include <gst/mixer/mixer.h>
 #include <gst/propertyprobe/propertyprobe.h>
 
+#include <gconf/gconf-client.h>
+
 #include <string.h>
 
 #define TIMEOUT	4000
  
 struct AcmeVolumeGStreamerPrivate
 {
-  	GstMixer      *mixer;
-  	GstMixerTrack *track;
+  	GstMixer *mixer;
+  	GList *tracks;
  	guint timer_id;
-	gint state;
+	gboolean muted;
+	gdouble volume; /* in acme-scale */
 };
 
 static GObjectClass *parent_class = NULL;
@@ -81,27 +84,33 @@
 acme_volume_gstreamer_set_mute (AcmeVolume *vol, gboolean val)
 {
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
+	GList *item;
 	
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return;
-		
-	gst_mixer_set_mute (self->_priv->mixer,
-			    self->_priv->track,
-			    val);
+
+	for (item = self->_priv->tracks; item != NULL; item = item->next) {
+		gst_mixer_set_mute (self->_priv->mixer, item->data, val);
+	}
 
 	if (val)
-		self->_priv->state |= 1;
+		self->_priv->muted = TRUE;
 	else {
-		GstMixerTrack *track = self->_priv->track;
+		GstMixerTrack *track;
 		gint *volumes, n;
 
-		self->_priv->state &= ~1;
-		volumes = g_new0 (gint, track->num_channels);
-		for (n = 0; n < track->num_channels; n++)
-			volumes[n] = (self->_priv->state >> 1) /
-				track->num_channels;
-		gst_mixer_set_volume (self->_priv->mixer, track, volumes);
-		g_free (volumes);
+		self->_priv->muted = FALSE;
+		for (item = self->_priv->tracks; item != NULL; item = item->next) {
+			track = item->data;
+			volumes = g_new0 (gint, track->num_channels);
+			for (n = 0; n < track->num_channels; n++) {
+				volumes[n] = track->min_volume +
+					self->_priv->volume * (track->max_volume -
+					track->min_volume) / 100.0;
+			}
+			gst_mixer_set_volume (self->_priv->mixer, track, volumes);
+			g_free (volumes);
+		}
 	}
 
 	acme_volume_gstreamer_close (self);
@@ -110,22 +119,42 @@
 static void
 update_state (AcmeVolumeGStreamer * self)
 {
-	gint *volumes, vol = 0, n;
-	GstMixerTrack *track = self->_priv->track;
+	gint *volumes, n, vol;
+	gdouble total_vol = 0;
+	GstMixerTrack *track;
+	GList *item;
 
 	/* 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);
+	for (item = self->_priv->tracks; item != NULL; item = item->next) {
+		track = item->data;
+		volumes = g_new0 (gint, track->num_channels);
+		gst_mixer_get_volume (self->_priv->mixer, track, volumes);
+		vol = 0;
+		for (n = 0; n < track->num_channels; n++)
+			vol += volumes[n];
+
+		/* mute - first only, so ignore */
+		if (item == self->_priv->tracks && vol == 0)
+			break;
+		vol /= track->num_channels;
+		total_vol += 100.0 * (vol - track->min_volume) /
+			(track->max_volume - track->min_volume);
+		g_free (volumes);
+	}
+	total_vol /= g_list_length (self->_priv->tracks);
 
 	/* update mute flag, and volume if not muted */
+	track = self->_priv->tracks->data;
 	if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE) ||
-	    (vol == 0 && (self->_priv->state >> 1) != 0))
-		self->_priv->state |= 1;
-	else
-		self->_priv->state = vol << 1;
+	    (total_vol == 0 && self->_priv->volume != 0)) {
+		self->_priv->muted = TRUE;
+		if (total_vol != 0) {
+			self->_priv->volume = total_vol;
+		}
+	} else {
+		self->_priv->volume = total_vol;
+		self->_priv->muted = FALSE;
+	}
 }
 
 static gboolean
@@ -135,33 +164,24 @@
 
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return FALSE;
-
 	update_state (self);
 	acme_volume_gstreamer_close (self);
 
-	return (self->_priv->state & 1);
+	return self->_priv->muted;
 }
 
 static int
 acme_volume_gstreamer_get_volume (AcmeVolume *vol)
 {
-	double volume;
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
-	GstMixerTrack *track = self->_priv->track;
 	
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return 0;
-
 	update_state (self);
 
-	/* normalize to [0,100] scale that acme wants */
-	volume = (self->_priv->state >> 1) / (double) track->num_channels;
-	volume = 100 * (volume - track->min_volume) /
-		(track->max_volume - track->min_volume);
-
 	acme_volume_gstreamer_close (self);
 
-	return (gint) volume;
+	return (gint) self->_priv->volume;
 }
 
 static void
@@ -170,26 +190,26 @@
 	gint i, *volumes, volume;
 	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
 	GstMixerTrack *track;
+	GList *item;
 
 	if (acme_volume_gstreamer_open (self) == FALSE)
 		return;
 
-	track = self->_priv->track;
 	val = CLAMP (val, 0, 100);
+	for (item = self->_priv->tracks; item != NULL; item = item->next) {
+		track = item->data;
 
-	/* Rescale the volume from [0, 100] to [track min, track max]. */
-	volume = (val / 100.0) * (track->max_volume - track->min_volume) +
-		track->min_volume;
-
-	volumes = g_new (gint, track->num_channels);
-	for (i = 0; i < track->num_channels; ++i)
-		volumes[i] = (gint) volume;
-	gst_mixer_set_volume (self->_priv->mixer, track, volumes);
-	g_free (volumes);
- 	
-	/* update state */
-	self->_priv->state = (self->_priv->state & 1) |
-		((volume * track->num_channels) << 1);
+		/* Rescale the volume from [0, 100] to [track min, track max]. */
+		volume = (val / 100.0) * (track->max_volume - track->min_volume) +
+			track->min_volume;
+
+		volumes = g_new (gint, track->num_channels);
+		for (i = 0; i < track->num_channels; ++i)
+			volumes[i] = (gint) volume;
+		gst_mixer_set_volume (self->_priv->mixer, track, volumes);
+		g_free (volumes);
+	}
+	self->_priv->volume = val;
 
  	acme_volume_gstreamer_close (self);
 }
@@ -203,44 +223,46 @@
 	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_object_unref (G_OBJECT (self->_priv->track));
-		self->_priv->mixer=NULL;
-		self->_priv->track=NULL;
+		gst_object_unref (GST_OBJECT (self->_priv->mixer));
+		g_list_foreach (self->_priv->tracks, (GFunc) g_object_unref, NULL);
+		g_list_free (self->_priv->tracks);
+		self->_priv->mixer = NULL;
+		self->_priv->tracks = NULL;
 	}
 	
 	self->_priv->timer_id = 0;
 	return FALSE;
 }
 
-/* This is a modified version of code from gnome-media's gst-mixer */
 static gboolean
-acme_volume_gstreamer_open (AcmeVolumeGStreamer *vol)
+find_element_track (AcmeVolumeGStreamer * self,
+		    const char * element_name,
+		    const char * track_name)
 {
-  	AcmeVolumeGStreamer *self = (AcmeVolumeGStreamer *) vol;
-  	const GList *elements;
+  	GList *elements, *olist;
   	gint num = 0;
-  
-	if (self->_priv == NULL)
-		return FALSE;
-	
-	if (self->_priv->timer_id != 0)
-	{
-		g_source_remove (self->_priv->timer_id);
-		self->_priv->timer_id = 0;
-		return TRUE;
+	gchar **track_names;
+
+	if (track_name) {
+		if (!(track_names = g_strsplit (track_name, ":", 0)))
+			return FALSE;
+	} else {
+		track_names = NULL;
 	}
-		
+
 	/* 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".
+	 * for one:
+	 * if element_name and track_name are set, find that element/track(s).
+	 * if element_name is set, find the default master track.
+	 * if nothing is set, find the first master track.
 	 * 
 	 * 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.
 	 */
-	elements = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
-	for ( ; elements != NULL && self->_priv->mixer == NULL; elements = elements->next) {
+	olist = elements = gst_registry_pool_feature_list (GST_TYPE_ELEMENT_FACTORY);
+	for ( ; elements != NULL; elements = elements->next) {
 		GstElementFactory *factory = GST_ELEMENT_FACTORY (elements->data);
 		gchar *title = NULL;
 		const gchar *klass;
@@ -259,6 +281,7 @@
 		/* create element */
 		title = g_strdup_printf ("gst-mixer-%d", num);
 		element = gst_element_factory_create (factory, title);
+		g_free (title);
 		if (!element)
 			goto next;
 		
@@ -288,38 +311,139 @@
 				gst_element_set_state (element, GST_STATE_NULL);
 				continue;
 			}
-			
+
+			/* check name if name is given */
+			if (element_name) {
+				GObjectClass *klass;
+				gchar *dev_name = NULL;
+
+				klass = G_OBJECT_GET_CLASS (element);
+				if (!g_object_class_find_property (klass, "device-name"))
+					continue;
+				g_object_get (G_OBJECT (element), "device-name",
+					      &dev_name, NULL);
+				if (!dev_name)
+					continue;
+				if (!g_str_has_prefix (element_name,
+						       dev_name)) {
+					g_free (dev_name);
+					continue;
+				}
+				g_free (dev_name);
+			}
+
+			/* check tracks */
 			tracks = gst_mixer_list_tracks (GST_MIXER (element));
 			for (; tracks != NULL; tracks = tracks->next) {
 				GstMixerTrack *track = tracks->data;
-				
-				if (GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MASTER)) {
-					self->_priv->mixer = GST_MIXER (element);
-					self->_priv->track = track;
+				gboolean add = FALSE;
+
+				if (track_names) {
+					gint x;
+
+					for (x = 0; track_names[x] != NULL; x++) {
+						if (!strcmp (track_names[x], track->label)) {
+							add = TRUE;
+							break;
+						}
+					}
+				} else if (GST_MIXER_TRACK_HAS_FLAG (track,
+							GST_MIXER_TRACK_MASTER)) {
+					add = TRUE;
+				}
 
-					g_object_ref (self->_priv->mixer);
-					g_object_ref (self->_priv->track);
-					if (array)
-						g_value_array_free (array);
-					return TRUE;
+				if (add) {
+					self->_priv->mixer = GST_MIXER (element);
+					self->_priv->tracks =
+						g_list_append (self->_priv->tracks, track);
 				}
 			}
-			
-			num++;
-			
-			/* and recreate this object, since we give it to the mixer */
-			title = g_strdup_printf ("gst-mixer-%d", num);
-			element = gst_element_factory_create (factory, title);
+			if (self->_priv->tracks)
+				break;
+			gst_element_set_state (element, GST_STATE_NULL);
 		}
-		
+
 	next:
-		if (element)
-			gst_object_unref (GST_OBJECT (element));
 		if (array)
 			g_value_array_free (array);
-		g_free (title);
+
+		/* found anything? */
+		if (self->_priv->tracks) {
+			gst_object_ref (GST_OBJECT (self->_priv->mixer));
+			g_list_foreach (self->_priv->tracks, (GFunc) g_object_ref, NULL);
+			break;
+		}
+
+		self->_priv->mixer = NULL;
+		if (element)
+			gst_object_unref (GST_OBJECT (element));
 	}
-	return FALSE;
+
+	if (track_names)
+		g_strfreev (track_names);
+	g_list_free (olist);
+
+	return (self->_priv->mixer != NULL);
+}
+
+/* This is a modified version of code from gnome-media's gst-mixer */
+static gboolean
+acme_volume_gstreamer_open (AcmeVolumeGStreamer * self)
+{
+	GConfClient *client;
+	gboolean res;
+	char *el_name = NULL, *track_name = NULL;
+	gint n;
+
+	if (self->_priv == NULL)
+		return FALSE;
+	
+	if (self->_priv->timer_id != 0)
+	{
+		g_source_remove (self->_priv->timer_id);
+		self->_priv->timer_id = 0;
+		return TRUE;
+	}
+
+	client = gconf_client_get_default ();
+	for (n = 0;; n++) {
+		gchar *dir, *id, *iid;
+
+		dir = g_strdup_printf ("/apps/panel/profiles/default/applets/applet_%d", n);
+		id = g_strdup_printf ("%s/bonobo_iid", dir);
+		iid = gconf_client_get_string (client, id, NULL);
+		g_free (id);
+		if (!iid) {
+			g_free (dir);
+			break;
+		}
+		if (!strcmp (iid, "OAFIID:GNOME_MixerApplet")) {
+			gchar *tkey, *ekey;
+
+			ekey = g_strdup_printf ("%s/prefs/active-element", dir);
+			tkey = g_strdup_printf ("%s/prefs/active-track", dir);
+			el_name = gconf_client_get_string (client, ekey, NULL);
+			track_name = gconf_client_get_string (client, tkey, NULL);
+			g_free (ekey);
+			g_free (tkey);
+			g_free (dir);
+			g_free (iid);
+			break;
+		}
+		g_free (dir);
+		g_free (iid);
+	}
+	g_object_unref (G_OBJECT (client));
+
+	res = ((el_name != NULL && track_name != NULL &&
+		find_element_track (self, el_name, track_name)) ||
+	       (el_name != NULL && find_element_track (self, el_name, NULL)) ||
+	       find_element_track (self, NULL, NULL));
+
+	g_free (el_name);
+	g_free (track_name);
+
+	return res;
 }
 
 static void
