File xfce4-mixer-alsa.patch of Package xfce4-volumed

---
 configure.ac           |   21 -
 src/Makefile.am        |   18 
 src/main.c             |    1 
 src/xfce4-mixer-alsa.c |  975 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/xfce4-mixer-alsa.h |  192 +++++++++
 src/xvd_data_types.h   |    5 
 src/xvd_mixer.c        |   43 +-
 src/xvd_mixer.h        |    5 
 8 files changed, 1244 insertions(+), 16 deletions(-)

--- a/configure.ac
+++ b/configure.ac
@@ -15,13 +15,24 @@ PKG_CHECK_MODULES(XFCONF, [libxfconf-0])
 AC_SUBST(XFCONF_CFLAGS)
 AC_SUBST(XFCONF_LIBS)
 
-PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10])
+AC_MSG_CHECKING(for audio engine)
+AC_ARG_WITH(audio,
+  AS_HELP_STRING([--with-audio],
+    [audio engine, either gstmixer or alsa (default)]),
+  audio="$withval", audio="alsa")
+
+if test "$audio" = "gstmixer"; then
+  AC_MSG_RESULT(gstreamer-0.10 native mixer)
+  PKG_CHECK_MODULES([GSTREAMER], [gstreamer-plugins-base-0.10])
+else
+  AC_MSG_RESULT(gstreamer-1.0 ALSA mixer)
+  PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0])
+  PKG_CHECK_MODULES([ALSA], [alsa])
+  AC_DEFINE([XFCE4_MIXER_ALSA], 1, [Built-in ALSA-based gstreamer mixer i/f])
+fi
 AC_SUBST(GSTREAMER_CFLAGS)
 AC_SUBST(GSTREAMER_LIBS)
-
-PKG_CHECK_MODULES(GSTREAMER_AUDIO, [gstreamer-audio-0.10])
-AC_SUBST(GSTREAMER_AUDIO_CFLAGS)
-AC_SUBST(GSTREAMER_AUDIO_LIBS)
+AM_CONDITIONAL([XFCE4_MIXER_ALSA], [test x"$audio" != x"gstmixer"])
 
 PKG_CHECK_MODULES(LIBKEYBINDER, [keybinder])
 AC_SUBST(LIBKEYBINDER_CFLAGS)
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,19 +12,33 @@ xfce4_volumed_SOURCES = \
 	xvd_xfconf.c		\
 	xvd_xfconf.h
 
+if XFCE4_MIXER_ALSA
+xfce4_volumed_SOURCES +=	\
+	xfce4-mixer-alsa.h	\
+	xfce4-mixer-alsa.c
+endif
+
 INCLUDES = 						\
 	@GLIB_CFLAGS@				\
 	@XFCONF_CFLAGS@				\
 	@GSTREAMER_CFLAGS@			\
-	@GSTREAMER_AUDIO_CFLAGS@	\
 	@LIBKEYBINDER_CFLAGS@		\
 	@LIBNOTIFY_CFLAGS@
 
+if XFCE4_MIXER_ALSA
+INCLUDES += 			\
+	$(ALSA_CFLAGS)
+endif
+
 xfce4_volumed_LDADD = 		\
 	@GLIB_LIBS@				\
 	@XFCONF_LIBS@			\
 	@GSTREAMER_LIBS@		\
-	@GSTREAMER_AUDIO_LIBS@	\
 	@LIBKEYBINDER_LIBS@		\
 	@LIBNOTIFY_LIBS@
 
+if XFCE4_MIXER_ALSA
+xfce4_volumed_LDADD +=		\
+	$(ALSA_LIBS)
+endif
+
--- /dev/null
+++ b/src/xfce4-mixer-alsa.c
@@ -0,0 +1,975 @@
+/*
+ * Simple alternative GstMixer implementation with ALSA-native API
+ */
+
+#include "config.h"
+#include <gst/gst.h>
+#include "xfce4-mixer-alsa.h"
+
+#include <alsa/asoundlib.h>
+
+#define GST_MIXER_MESSAGE_NAME "gst-mixer-message"
+
+/*
+ * GstMixer
+ */
+
+G_DEFINE_TYPE (GstMixer, gst_mixer, GST_TYPE_ELEMENT);
+
+static void gst_mixer_init (GstMixer *mixer)
+{
+}
+
+static void gst_mixer_dispose (GObject * object)
+{
+  GstMixer *mixer = GST_MIXER (object);
+
+  if (mixer->src) {
+    g_source_destroy (mixer->src);
+    mixer->src = NULL;
+  }
+
+  if (mixer->handle) {
+    snd_mixer_close (mixer->handle);
+    mixer->handle = NULL;
+  }
+
+  g_list_free_full (mixer->tracklist, g_object_unref);
+  mixer->tracklist = NULL;
+
+  g_free ((gpointer *) mixer->name);
+  mixer->name = NULL;
+
+  g_free ((gpointer *) mixer->card_name);
+  mixer->card_name = NULL;
+
+  G_OBJECT_CLASS (gst_mixer_parent_class)->dispose (object);
+}
+
+static void gst_mixer_class_init (GstMixerClass *klass)
+{
+  GstElementClass *element_klass = GST_ELEMENT_CLASS (klass);
+  GObjectClass *object_klass = G_OBJECT_CLASS (klass);
+
+  gst_element_class_set_static_metadata (element_klass,
+	"ALSA mixer", "Generic/Audio",
+	"Control audio mixer via ALSA API",
+	"Takashi Iwai <tiwai@suse.de>");
+
+  object_klass->dispose = gst_mixer_dispose;
+}
+
+/*
+ * GstMixerTrack
+ */
+
+G_DEFINE_TYPE (GstMixerTrack, gst_mixer_track, G_TYPE_OBJECT);
+
+static void gst_mixer_track_init (GstMixerTrack *track)
+{
+}
+
+static void notify_mute_change (GstMixer *mixer, GstMixerTrack *track,
+				gboolean mute)
+{
+  GstStructure *s;
+  GstMessage *m;
+
+  s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
+			 "type", G_TYPE_STRING, "mute-toggled",
+			 "track", GST_TYPE_MIXER_TRACK, track,
+			 "mute", G_TYPE_BOOLEAN, mute,
+			 NULL);
+  m = gst_message_new_element (GST_OBJECT (mixer), s);
+  gst_element_post_message (GST_ELEMENT (mixer), m);
+}
+
+static void update_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute)
+{
+  int old_flag = track->flags & GST_MIXER_TRACK_MUTE;
+
+  if (mute) {
+    track->flags |= GST_MIXER_TRACK_MUTE;
+    if (track->shared_mute)
+      track->shared_mute->flags |= GST_MIXER_TRACK_MUTE;
+  } else {
+    track->flags &= ~GST_MIXER_TRACK_MUTE;
+    if (track->shared_mute)
+      track->shared_mute->flags &= ~GST_MIXER_TRACK_MUTE;
+  }
+
+  if ((track->flags & GST_MIXER_TRACK_MUTE) != old_flag)
+    notify_mute_change (mixer, track, mute);
+}
+
+static void notify_recording_change (GstMixer *mixer, GstMixerTrack *track,
+				     gboolean recording)
+{
+  GstStructure *s;
+  GstMessage *m;
+
+  s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
+			 "type", G_TYPE_STRING, "record-toggled",
+			 "track", GST_TYPE_MIXER_TRACK, track,
+			 "record", G_TYPE_BOOLEAN, recording,
+			 NULL);
+  m = gst_message_new_element (GST_OBJECT (mixer), s);
+  gst_element_post_message (GST_ELEMENT (mixer), m);
+}
+
+static void update_recording (GstMixer *mixer, GstMixerTrack *track,
+			      gboolean recording)
+{
+  int old_flag = track->flags & GST_MIXER_TRACK_RECORD;
+
+  if (recording)
+    track->flags |= GST_MIXER_TRACK_RECORD;
+  else
+    track->flags &= ~GST_MIXER_TRACK_RECORD;
+
+  if ((track->flags & GST_MIXER_TRACK_RECORD) != old_flag)
+    notify_recording_change (mixer, track, recording);
+}
+
+static void notify_volume_change (GstMixer *mixer, GstMixerTrack *track)
+{
+  GstStructure *s;
+  GstMessage *m;
+  GValue l = { 0, };
+  GValue v = { 0, };
+  int i;
+
+  s = gst_structure_new (GST_MIXER_MESSAGE_NAME,
+			 "type", G_TYPE_STRING, "volume-changed",
+			 "track", GST_TYPE_MIXER_TRACK, track,
+			 NULL);
+  g_value_init (&l, GST_TYPE_ARRAY);
+  g_value_init (&v, G_TYPE_INT);
+
+  for (i = 0; i < track->num_channels; i++) {
+    g_value_set_int (&v, track->volumes[i]);
+    gst_value_array_append_value (&l, &v);
+  }
+
+  gst_structure_set_value (s, "volumes", &l);
+  g_value_unset (&v);
+  g_value_unset (&l);
+
+  m = gst_message_new_element (GST_OBJECT (mixer), s);
+  gst_element_post_message (GST_ELEMENT (mixer), m);
+}
+
+static void track_update (GstMixer *mixer, GstMixerTrack *track)
+{
+  gboolean vol_changed = FALSE;
+  int i;
+
+  if (track->flags & GST_MIXER_TRACK_OUTPUT) {
+    int audible = 0;
+    if (track->has_switch) {
+      for (i = 0; i < track->num_channels; i++) {
+	int v = 0;
+	snd_mixer_selem_get_playback_switch (track->element, i, &v);
+	if (v)
+	  audible = 1;
+      }
+    }
+
+    if (track->has_volume) {
+      for (i = 0; i < track->num_channels; i++) {
+	long vol = 0;
+	snd_mixer_selem_get_playback_volume (track->element, i, &vol);
+	if (track->volumes[i] != vol)
+	  vol_changed = TRUE;
+	track->volumes[i] = vol;
+	if (!track->has_switch &&
+	    vol > track->min_volume)
+	  audible = 1;
+      }
+    }
+
+    update_mute (mixer, track, !audible);
+  }
+
+  if (track->flags & GST_MIXER_TRACK_INPUT) {
+    int recording = 0;
+    if (track->has_switch) {
+      for (i = 0; i < track->num_channels; i++) {
+	int v = 0;
+	snd_mixer_selem_get_capture_switch (track->element, i, &v);
+	if (v)
+	  recording = 1;
+      }
+    }
+
+    if (track->has_volume) {
+      for (i = 0; i < track->num_channels; i++) {
+	long vol = 0;
+	snd_mixer_selem_get_capture_volume (track->element, i, &vol);
+	if (track->volumes[i] != vol)
+	  vol_changed = TRUE;
+	track->volumes[i] = vol;
+	if (!track->has_switch &&
+	    vol > track->min_volume)
+	  recording = 1;
+      }
+    }
+
+    update_recording (mixer, track, recording);
+  }
+
+  if (vol_changed)
+    notify_volume_change (mixer, track);
+}
+
+static GstMixerTrack *track_new (snd_mixer_elem_t *element, int num,
+				int flags, gboolean append_capture)
+{
+  GstMixerTrack *track;
+  const char *name;
+
+  track = (GstMixerTrack *) g_object_new (GST_TYPE_MIXER_TRACK, NULL);
+  track->index = snd_mixer_selem_get_index (element);
+  track->element = element;
+  track->flags = flags;
+
+  if (flags & GST_MIXER_TRACK_OUTPUT) {
+    while (snd_mixer_selem_has_playback_channel (element,
+						 track->num_channels))
+      track->num_channels++;
+  } else if (flags & GST_MIXER_TRACK_INPUT) {
+    while (snd_mixer_selem_has_capture_channel (element,
+						track->num_channels))
+      track->num_channels++;
+  }
+
+  track->volumes = g_new (gint, track->num_channels);
+
+  name = snd_mixer_selem_get_name (element);
+  track->untranslated_label = g_strdup (name);
+
+  if (!num)
+    track->label = g_strdup_printf ("%s%s", name,
+				    append_capture ? " Capture" : "");
+  else
+    track->label = g_strdup_printf ("%s%s %d", name,
+				    append_capture ? " Capture" : "",
+				    num);
+
+  return track;
+}
+
+enum {
+  ARG_0,
+  ARG_LABEL,
+  ARG_UNTRANSLATED_LABEL,
+  ARG_INDEX,
+};
+
+static void gst_mixer_track_get_property (GObject *object, guint prop_id,
+					  GValue *value, GParamSpec *pspec)
+{
+  GstMixerTrack *track = GST_MIXER_TRACK (object);
+
+  switch (prop_id) {
+  case ARG_LABEL:
+    g_value_set_string (value, track->label);
+    break;
+  case ARG_UNTRANSLATED_LABEL:
+    g_value_set_string (value, track->untranslated_label);
+    break;
+  case ARG_INDEX:
+    g_value_set_uint (value, track->index);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void gst_mixer_track_set_property (GObject *object,
+					  guint prop_id,
+					  const GValue *value,
+					  GParamSpec * pspec)
+{
+  GstMixerTrack *track;
+
+  track = GST_MIXER_TRACK (object);
+
+  switch (prop_id) {
+    case ARG_LABEL:
+      g_free (track->label);
+      track->label = g_value_dup_string (value);
+      break;
+    case ARG_UNTRANSLATED_LABEL:
+      g_free (track->untranslated_label);
+      track->untranslated_label = g_value_dup_string (value);
+      break;
+    case ARG_INDEX:
+      track->index = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void gst_mixer_track_dispose (GObject * object)
+{
+  GstMixerTrack *track = GST_MIXER_TRACK (object);
+
+  if (track->label) {
+    g_free (track->label);
+    track->label = NULL;
+  }
+
+  if (track->untranslated_label) {
+    g_free (track->untranslated_label);
+    track->untranslated_label = NULL;
+  }
+
+  G_OBJECT_CLASS (gst_mixer_track_parent_class)->dispose (object);
+}
+
+static void gst_mixer_track_class_init (GstMixerTrackClass * klass)
+{
+  GObjectClass *object_klass = G_OBJECT_CLASS (klass);
+
+  object_klass->get_property = gst_mixer_track_get_property;
+  object_klass->set_property = gst_mixer_track_set_property;
+
+  g_object_class_install_property (object_klass, ARG_UNTRANSLATED_LABEL,
+      g_param_spec_string ("untranslated-label",
+			   "Untranslated track label",
+			   "The untranslated label assigned to the track (since 0.10.13)",
+			   NULL,
+			   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_klass, ARG_LABEL,
+	g_param_spec_string ("label", "Track label",
+			     "The label assigned to the track (may be translated)",
+			     NULL,
+			     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_klass, ARG_INDEX,
+	g_param_spec_uint ("index", "Index",
+			   "Track index",
+			   0, G_MAXUINT, 0,
+			   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  object_klass->dispose = gst_mixer_track_dispose;
+}
+
+static void get_playback_min_max (GstMixerTrack *track)
+{
+  if (track->has_volume) {
+    long min = 0, max = 0;
+    snd_mixer_selem_get_playback_volume_range (track->element, &min, &max);
+    track->min_volume = min;
+    track->max_volume = max;
+  }
+}
+	
+static void get_capture_min_max (GstMixerTrack *track)
+{
+  if (track->has_volume) {
+    long min = 0, max = 0;
+    snd_mixer_selem_get_capture_volume_range (track->element, &min, &max);
+    track->min_volume = min;
+    track->max_volume = max;
+  }
+}
+	
+static GstMixerTrack *get_named_playback_track (GstMixer *mixer,
+						const char *name)
+{
+  GList *item;
+  GstMixerTrack *track;
+
+  for (item = mixer->tracklist; item; item = item->next) {
+    track = GST_MIXER_TRACK (item->data);
+    if (! (track->flags & GST_MIXER_TRACK_OUTPUT))
+      continue;
+    if (!strcmp (track->label, name))
+      return track;
+  }
+  return NULL;
+}
+
+static void mark_master_track (GstMixer *mixer)
+{
+  GList *item;
+  GstMixerTrack *track;
+
+  if ((track = get_named_playback_track (mixer, "Master")) ||
+      (track = get_named_playback_track (mixer, "Front")) ||
+      (track = get_named_playback_track (mixer, "PCM")) ||
+      (track = get_named_playback_track (mixer, "Speaker")))
+    goto found;
+	    
+  /* If not found, take a mono track with both volume and switch */
+  for (item = mixer->tracklist; item; item = item->next) {
+    track = GST_MIXER_TRACK (item->data);
+    if (! (track->flags & GST_MIXER_TRACK_OUTPUT))
+      continue;
+    if (track->has_volume && track->has_switch &&
+	track->num_channels == 1)
+      goto found;
+  }
+
+  /* If not found, take any track with both volume and switch */
+  for (item = mixer->tracklist; item; item = item->next) {
+    track = GST_MIXER_TRACK (item->data);
+    if (! (track->flags & GST_MIXER_TRACK_OUTPUT))
+      continue;
+    if (track->has_volume && track->has_switch)
+      goto found;
+  }
+
+  /* If not found, take any track with volume */
+  for (item = mixer->tracklist; item; item = item->next) {
+    track = GST_MIXER_TRACK (item->data);
+    if (! (track->flags & GST_MIXER_TRACK_OUTPUT))
+      continue;
+    if (track->has_volume)
+      goto found;
+  }
+
+  return;
+
+ found:
+  track->flags |= GST_MIXER_TRACK_MASTER;
+  return;
+}
+
+static int mixer_elem_callback (snd_mixer_elem_t *elem, unsigned int mask)
+{
+  GstMixer *mixer = snd_mixer_elem_get_callback_private (elem);
+  GList *item;
+
+  for (item = mixer->tracklist; item; item = item->next) {
+    GstMixerTrack *track = GST_MIXER_TRACK (item->data);
+    if (track->element == elem)
+      track_update (mixer, track);
+  }
+
+  return 0;
+}
+
+static int mixer_callback (snd_mixer_t *ctl, unsigned int mask,
+			   snd_mixer_elem_t *elem)
+{
+  GstMixer *mixer = snd_mixer_get_callback_private (ctl);
+	
+  snd_mixer_handle_events (mixer->handle);
+  return 0;
+}
+
+const GList *gst_mixer_list_tracks (GstMixer *mixer)
+{
+  return mixer->tracklist;
+}
+
+static gboolean same_volumes (gint num_channels, const gint *volumes)
+{
+  gint i;
+
+  for (i = 1; i < num_channels; i++) {
+    if (volumes[0] != volumes[i])
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+void gst_mixer_set_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes)
+{
+  gint i;
+
+  track_update (mixer, track);
+
+  if (!track->has_volume)
+    return;
+
+  for (i = 0; i < track->num_channels; i++)
+    track->volumes[i] = volumes[i];
+
+  if (track->flags & GST_MIXER_TRACK_OUTPUT) {
+    if (!track->has_switch && (track->flags & GST_MIXER_TRACK_MUTE))
+      return;
+    if (same_volumes (track->num_channels, volumes)) {
+      snd_mixer_selem_set_playback_volume_all (track->element,
+					       volumes[0]);
+    } else {
+      for (i = 0; i < track->num_channels; i++)
+	snd_mixer_selem_set_playback_volume (track->element, i,
+					     volumes[i]);
+    }
+  } else {
+    if (!track->has_switch && ! (track->flags & GST_MIXER_TRACK_RECORD))
+      return;
+    if (same_volumes (track->num_channels, volumes)) {
+      snd_mixer_selem_set_capture_volume_all (track->element,
+					      volumes[0]);
+    } else {
+      for (i = 0; i < track->num_channels; i++)
+	snd_mixer_selem_set_capture_volume (track->element, i,
+					    volumes[i]);
+    }
+  }
+}
+
+void gst_mixer_get_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes)
+{
+  int i;
+
+  if (!track->has_volume)
+    return;
+
+  track_update (mixer, track);
+  for (i = 0; i < track->num_channels; i++)
+    volumes[i] = track->volumes[i];
+}
+
+void gst_mixer_set_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute)
+{
+  int i;
+
+  if (track->flags & GST_MIXER_TRACK_INPUT) {
+    if (track->shared_mute)
+      track = track->shared_mute;
+    else
+      return;
+  }
+
+  track_update (mixer, track);
+
+  mute = !!mute;
+  if (mute == !! (track->flags & GST_MIXER_TRACK_MUTE))
+    return;
+
+  update_mute (mixer, track, mute);
+
+  if (track->has_switch) {
+    snd_mixer_selem_set_playback_switch_all (track->element, !mute);
+  } else {
+    for (i = 0; i < track->num_channels; i++) {
+      long vol = mute ? track->min_volume : track->volumes[i];
+      snd_mixer_selem_set_playback_volume (track->element, i, vol);
+    }
+  }
+}
+
+void gst_mixer_set_record (GstMixer * mixer, GstMixerTrack *track, gboolean record)
+{
+  int i;
+
+  if (! (track->flags & GST_MIXER_TRACK_INPUT))
+    return;
+
+  track_update (mixer, track);
+
+  record = !!record;
+  if (record == !! (track->flags & GST_MIXER_TRACK_RECORD))
+    return;
+
+  if (record)
+    track->flags |= GST_MIXER_TRACK_RECORD;
+  else
+    track->flags &= ~GST_MIXER_TRACK_RECORD;
+
+  if (track->has_switch) {
+    snd_mixer_selem_set_capture_switch_all (track->element, record);
+  } else {
+    for (i = 0; i < track->num_channels; i++) {
+      long vol = record ? track->volumes[i] : track->min_volume;
+      snd_mixer_selem_set_capture_volume (track->element, i, vol);
+    }
+  }
+}
+
+GstMixerMessageType
+gst_mixer_message_get_type (GstMessage * message)
+{
+  const GstStructure *s;
+  const gchar *m_type;
+
+  s = gst_message_get_structure (message);
+  m_type = gst_structure_get_string (s, "type");
+  if (!m_type)
+    return GST_MIXER_MESSAGE_INVALID;
+
+  if (g_str_equal (m_type, "mute-toggled"))
+    return GST_MIXER_MESSAGE_MUTE_TOGGLED;
+  else if (g_str_equal (m_type, "record-toggled"))
+    return GST_MIXER_MESSAGE_RECORD_TOGGLED;
+  else if (g_str_equal (m_type, "volume-changed"))
+    return GST_MIXER_MESSAGE_VOLUME_CHANGED;
+  else if (g_str_equal (m_type, "option-changed"))
+    return GST_MIXER_MESSAGE_OPTION_CHANGED;
+  else if (g_str_equal (m_type, "options-list-changed"))
+    return GST_MIXER_MESSAGE_OPTIONS_LIST_CHANGED;
+  else if (g_str_equal (m_type, "mixer-changed"))
+    return GST_MIXER_MESSAGE_MIXER_CHANGED;
+
+  return GST_MIXER_MESSAGE_INVALID;
+}
+
+static void message_parse_track (const GstStructure *s, GstMixerTrack **track)
+{
+  if (track) {
+    const GValue *v = gst_structure_get_value (s, "track");
+    *track = (GstMixerTrack *)g_value_get_object (v);
+  }
+}
+
+void gst_mixer_message_parse_mute_toggled (GstMessage *message,
+					   GstMixerTrack **track,
+					   gboolean *mute)
+{
+  const GstStructure *s = gst_message_get_structure (message);
+
+  message_parse_track (s, track);
+  if (mute)
+    gst_structure_get_boolean (s, "mute", mute);
+}
+
+void gst_mixer_message_parse_record_toggled (GstMessage *message,
+					     GstMixerTrack **track,
+					     gboolean *record)
+{
+  const GstStructure *s = gst_message_get_structure (message);
+
+  message_parse_track (s, track);
+  if (record)
+    gst_structure_get_boolean (s, "record", record);
+}
+
+void gst_mixer_message_parse_volume_changed (GstMessage *message,
+					    GstMixerTrack **track,
+					    gint **volumes,
+					    gint *num_channels)
+{
+  const GstStructure *s = gst_message_get_structure (message);
+
+  message_parse_track (s, track);
+  if (volumes || num_channels) {
+    gint n_chans, i;
+    const GValue *v = gst_structure_get_value (s, "volumes");
+
+    n_chans = gst_value_array_get_size (v);
+    if (num_channels)
+      *num_channels = n_chans;
+
+    if (volumes) {
+      *volumes = g_new (gint, n_chans);
+      for (i = 0; i < n_chans; i++) {
+	const GValue *e = gst_value_array_get_value (v, i);
+
+	(*volumes)[i] = g_value_get_int (e);
+      }
+    }
+  }
+}
+
+/*
+ * GstMixerOptions
+ */
+
+G_DEFINE_TYPE (GstMixerOptions, gst_mixer_options, GST_TYPE_MIXER_TRACK);
+
+static GstMixerOptions *
+mixer_options_new (snd_mixer_elem_t *element, int num)
+{
+  GstMixerOptions *opt;
+  GstMixerTrack *track;
+  const char *label;
+  int i;
+
+  label = snd_mixer_selem_get_name (element);
+  opt = g_object_new (GST_TYPE_MIXER_OPTIONS,
+		      "untranslated-label", label,
+		      "index", snd_mixer_selem_get_index (element),
+		      NULL);
+  track = GST_MIXER_TRACK (opt);
+  track->element = element;
+  if (!num)
+    track->label = g_strdup (label);
+  else
+    track->label = g_strdup_printf ("%s %d", label, num);
+
+  num = snd_mixer_selem_get_enum_items (element);
+  for (i = 0; i < num; i++) {
+    char str[256];
+    if (snd_mixer_selem_get_enum_item_name (element, i, sizeof(str), str) < 0)
+      break;
+    opt->values = g_list_append (opt->values, g_strdup (str));
+  }
+
+  return opt;
+}
+
+static void gst_mixer_options_dispose (GObject * object)
+{
+  GstMixerOptions *opt = GST_MIXER_OPTIONS (object);
+
+  g_list_free_full (opt->values, g_free);
+  opt->values = NULL;
+
+  G_OBJECT_CLASS (gst_mixer_options_parent_class)->dispose (object);
+}
+
+static void gst_mixer_options_init (GstMixerOptions *opt)
+{
+}
+
+static void gst_mixer_options_class_init (GstMixerOptionsClass * klass)
+{
+  GObjectClass *object_klass = G_OBJECT_CLASS (klass);
+
+  object_klass->dispose = gst_mixer_options_dispose;
+}
+
+const gchar *gst_mixer_get_option (GstMixer *mixer, GstMixerOptions *opt)
+{
+  unsigned int idx;
+
+  if (snd_mixer_selem_get_enum_item (opt->parent.element, 0, &idx) < 0)
+    return "error";
+  return g_list_nth_data (opt->values, idx);
+}
+
+void gst_mixer_set_option (GstMixer *mixer, GstMixerOptions *opt,
+			   gchar *value)
+{
+  int n = 0;
+  GList *item;
+
+  for (item = opt->values; item; item = item->next, n++) {
+    if (!strcmp (item->data, value)) {
+      snd_mixer_selem_set_enum_item (opt->parent.element, 0, n);
+      break;
+    }
+  }
+}
+
+GList *gst_mixer_options_get_values (GstMixerOptions *opt)
+{
+  return opt->values;
+}
+
+static void message_parse_options (const GstStructure *s,
+				   GstMixerOptions ** options)
+{
+  if (options) {
+    const GValue *v = gst_structure_get_value (s, "options");
+    *options = (GstMixerOptions *) g_value_get_object (v);
+  }
+}
+
+void gst_mixer_message_parse_option_changed (GstMessage *message,
+					     GstMixerOptions ** options,
+					     const gchar **value)
+{
+  const GstStructure *s = gst_message_get_structure (message);
+
+  message_parse_options (s, options);
+  if (value)
+    *value = gst_structure_get_string (s, "value");
+}
+
+void gst_mixer_message_parse_options_list_changed (GstMessage *message,
+						   GstMixerOptions **options)
+{
+  const GstStructure *s = gst_message_get_structure (message);
+
+  message_parse_options (s, options);
+}
+
+/*
+ */
+
+static void create_track_list (GstMixer *mixer)
+{
+  snd_mixer_elem_t *element, *temp;
+  GList *item;
+
+  if (mixer->tracklist)
+    return;
+
+  for (element = snd_mixer_first_elem (mixer->handle); element;
+       element = snd_mixer_elem_next (element)) {
+    GstMixerTrack *play_track = NULL;
+    GstMixerTrack *cap_track = NULL;
+    const gchar *name = snd_mixer_selem_get_name (element);
+    int index = 0;
+    int has_volume, has_switch;
+
+    for (item = mixer->tracklist; item; item = item->next) {
+      temp = GST_MIXER_TRACK (item->data)->element;
+      if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0)
+	index++;
+    }
+
+    has_volume = snd_mixer_selem_has_playback_volume (element);
+    has_switch = snd_mixer_selem_has_playback_switch (element);
+    if (has_volume || has_switch) {
+      play_track = track_new (element, index,
+			      GST_MIXER_TRACK_OUTPUT, FALSE);
+      play_track->has_volume = has_volume;
+      play_track->has_switch = has_switch;
+      get_playback_min_max (play_track);
+    }
+
+    has_volume = snd_mixer_selem_has_capture_volume (element);
+    has_switch = snd_mixer_selem_has_capture_switch (element);
+    if (play_track && snd_mixer_selem_has_common_volume (element))
+      has_volume = 0;
+    if (play_track && snd_mixer_selem_has_common_switch (element))
+      has_switch = 0;
+    if (has_volume || has_switch) {
+      cap_track = track_new (element, index,
+			     GST_MIXER_TRACK_INPUT,
+			     play_track != NULL);
+      cap_track->has_volume = has_volume;
+      cap_track->has_switch = has_switch;
+      get_capture_min_max (cap_track);
+    }
+
+    if (play_track && cap_track) {
+      play_track->shared_mute = cap_track;
+      cap_track->shared_mute = play_track;
+    }
+
+    if (play_track) {
+      track_update (mixer, play_track);
+      mixer->tracklist = g_list_append (mixer->tracklist, play_track);
+    }
+
+    if (cap_track) {
+      track_update (mixer, cap_track);
+      mixer->tracklist = g_list_append (mixer->tracklist, cap_track);
+    }
+
+    if (snd_mixer_selem_is_enumerated (element)) {
+      mixer->tracklist = g_list_append (mixer->tracklist,
+					mixer_options_new (element, index));
+    }
+
+    snd_mixer_elem_set_callback_private (element, mixer);
+    snd_mixer_elem_set_callback (element, mixer_elem_callback);
+  }
+
+  mark_master_track (mixer);
+}
+
+static gboolean mixer_src_callback (gpointer user_data)
+{
+  GstMixer *mixer = (GstMixer *)user_data;
+
+  snd_mixer_handle_events (mixer->handle);
+  return TRUE;
+}
+
+static gboolean mixer_src_dispatch (GSource *source,
+				    GSourceFunc callback,
+				    gpointer user_data)
+{
+  return callback (user_data);
+}
+
+static void mixer_src_attach (GstMixer *mixer)
+{
+  static GSourceFuncs func = {
+    .dispatch = mixer_src_dispatch,
+  };
+  struct pollfd pfd;
+
+  if (snd_mixer_poll_descriptors (mixer->handle, &pfd, 1) != 1)
+    return;
+
+  mixer->src = g_source_new (&func, sizeof (*mixer->src));
+  g_source_add_unix_fd (mixer->src, pfd.fd, G_IO_IN | G_IO_ERR);
+  g_source_set_callback (mixer->src, mixer_src_callback, mixer, NULL);
+  g_source_attach (mixer->src, g_main_context_default ());
+}
+
+/*
+ * These are new functions that didn't exist in the original gstreamer API;
+ * instead of lengthy probing using factory, just provide a simpler method
+ */
+
+int gst_mixer_new (const char *name, GstMixer **mixer_ret)
+{
+  GstMixer *mixer;
+  snd_hctl_t *hctl;
+  int err;
+
+  mixer = (GstMixer *) g_object_new (GST_TYPE_MIXER, NULL);
+  mixer->name = g_strdup (name);
+
+  err = snd_mixer_open ((snd_mixer_t **) &mixer->handle, 0);
+  if (err < 0)
+    return err;
+
+  err = snd_mixer_attach (mixer->handle, name);
+  if (err < 0)
+    goto error;
+
+  err = snd_mixer_selem_register (mixer->handle, NULL, NULL);
+  if (err < 0)
+    goto error;
+
+  err = snd_mixer_load (mixer->handle);
+  if (err < 0)
+    goto error;
+
+  snd_mixer_get_hctl (mixer->handle, name, &hctl);
+  {
+    snd_ctl_card_info_t *info;
+
+    snd_ctl_card_info_alloca (&info);
+    snd_ctl_card_info (snd_hctl_ctl (hctl), info);
+    mixer->card_name = g_strdup_printf ("%s (Alsa mixer)",
+					snd_ctl_card_info_get_name (info));
+  }
+
+  snd_mixer_set_callback_private (mixer->handle, mixer);
+  snd_mixer_set_callback (mixer->handle, mixer_callback);
+  
+  create_track_list (mixer);
+
+  mixer_src_attach (mixer);
+
+  *mixer_ret = mixer;
+  return 0;
+
+ error:
+  gst_object_unref (mixer);
+  return err;
+}
+
+GList *gst_mixer_probe_devices (void)
+{
+  int card = -1;
+  GList *card_list = NULL;
+
+  while (snd_card_next(&card) >= 0 && card >= 0) {
+    GstMixer *mixer;
+    char name [16];
+    int err;
+
+    sprintf (name, "hw:%d", card);
+    err = gst_mixer_new (name, &mixer);
+    if (err < 0)
+      continue;
+    card_list = g_list_append (card_list, mixer);
+  }
+
+  return card_list;
+}
+
+const gchar *gst_mixer_get_card_name (GstMixer *mixer)
+{
+  return mixer->card_name;
+}
--- /dev/null
+++ b/src/xfce4-mixer-alsa.h
@@ -0,0 +1,192 @@
+/*
+ * Simple alternative GstMixer implementation with ALSA-native API
+ */
+
+#ifndef __XFCE4_MIXER_ALSA_H
+#define __XFCE4_MIXER_ALSA_H
+
+G_BEGIN_DECLS
+
+/*
+ * GstMixer
+ */
+
+GType gst_mixer_get_type (void);
+
+#define GST_TYPE_MIXER \
+  (gst_mixer_get_type ())
+#define GST_MIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER, GstMixer))
+#define GST_MIXER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER, GstMixerClass))
+#define GST_IS_MIXER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER))
+#define GST_IS_MIXER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER))
+#define GST_MIXER_GET_CLASS(inst) \
+  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_MIXER, GstMixerClass))
+
+typedef struct _GstMixer GstMixer;
+typedef struct _GstMixerClass GstMixerClass;
+
+typedef enum {
+  GST_MIXER_FLAG_NONE                = 0,
+  GST_MIXER_FLAG_AUTO_NOTIFICATIONS  = (1<<0),
+  GST_MIXER_FLAG_HAS_WHITELIST       = (1<<1),
+  GST_MIXER_FLAG_GROUPING            = (1<<2),
+} GstMixerFlags;
+
+typedef enum {
+  GST_MIXER_MESSAGE_INVALID,
+  GST_MIXER_MESSAGE_MUTE_TOGGLED,
+  GST_MIXER_MESSAGE_RECORD_TOGGLED,
+  GST_MIXER_MESSAGE_VOLUME_CHANGED,
+  GST_MIXER_MESSAGE_OPTION_CHANGED,
+  GST_MIXER_MESSAGE_OPTIONS_LIST_CHANGED,
+  GST_MIXER_MESSAGE_MIXER_CHANGED
+} GstMixerMessageType;
+
+struct _GstMixer {
+  GstElement element;
+  GList *tracklist;
+  void *handle; /* snd_mixer_t */
+  const char *name;
+  const gchar *card_name;
+  GSource *src;
+};
+
+struct _GstMixerClass {
+  GstElementClass parent_class;
+};
+
+const GList *gst_mixer_list_tracks (GstMixer *mixer);
+
+static inline GstMixerFlags
+gst_mixer_get_mixer_flags (GstMixer * mixer)
+{
+  return GST_MIXER_FLAG_AUTO_NOTIFICATIONS;
+}
+
+GstMixerMessageType gst_mixer_message_get_type (GstMessage *message);
+
+int gst_mixer_new (const char *name, GstMixer **mixer_ret);
+GList *gst_mixer_probe_devices (void);
+const gchar *gst_mixer_get_card_name (GstMixer *mixer);
+
+/*
+ * GstMixerTrack
+ */
+
+GType gst_mixer_track_get_type (void);
+
+#define GST_TYPE_MIXER_TRACK \
+  (gst_mixer_track_get_type ())
+#define GST_MIXER_TRACK(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER_TRACK, \
+                               GstMixerTrack))
+#define GST_MIXER_TRACK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER_TRACK, \
+                            GstMixerTrackClass))
+#define GST_IS_MIXER_TRACK(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER_TRACK))
+#define GST_IS_MIXER_TRACK_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER_TRACK))
+
+typedef struct _GstMixerTrack GstMixerTrack;
+typedef struct _GstMixerTrackClass GstMixerTrackClass;
+
+typedef enum {
+  GST_MIXER_TRACK_INPUT  = (1<<0),
+  GST_MIXER_TRACK_OUTPUT = (1<<1),
+  GST_MIXER_TRACK_MUTE   = (1<<2),
+  GST_MIXER_TRACK_RECORD = (1<<3),
+  GST_MIXER_TRACK_MASTER = (1<<4),
+  GST_MIXER_TRACK_SOFTWARE = (1<<5),
+  GST_MIXER_TRACK_NO_RECORD = (1<<6),
+  GST_MIXER_TRACK_NO_MUTE = (1<<7),
+  GST_MIXER_TRACK_WHITELIST = (1<<8),
+  GST_MIXER_TRACK_READONLY = (1<<9),
+  GST_MIXER_TRACK_WRITEONLY = (1<<10)
+} GstMixerTrackFlags;
+
+struct _GstMixerTrack {
+  GObject parent;
+  void *element;
+  gchar *label;
+  gchar *untranslated_label;
+  guint index;
+  GstMixerTrackFlags flags;
+  gint num_channels;
+  gint *volumes;
+  gint min_volume;
+  gint max_volume;
+  GstMixerTrack *shared_mute;
+  gboolean has_volume;
+  gboolean has_switch;
+};
+
+struct _GstMixerTrackClass {
+  GObjectClass parent;
+};
+
+#define GST_MIXER_TRACK_HAS_FLAG(track, flag)	((track)->flags & (flag))
+
+void gst_mixer_get_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes);
+void gst_mixer_set_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes);
+void gst_mixer_set_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute);
+void gst_mixer_set_record (GstMixer *mixer, GstMixerTrack *track, gboolean record);
+
+void gst_mixer_message_parse_mute_toggled (GstMessage *message,
+					   GstMixerTrack **track,
+					   gboolean *mute);
+void gst_mixer_message_parse_record_toggled (GstMessage *message,
+					     GstMixerTrack **track,
+					     gboolean *record);
+void gst_mixer_message_parse_volume_changed (GstMessage *message,
+					     GstMixerTrack **track,
+					     gint **volumes,
+					     gint *num_channels);
+
+/*
+ * GstMixerOptions
+ */
+
+GType gst_mixer_options_get_type (void);
+
+#define GST_TYPE_MIXER_OPTIONS \
+  (gst_mixer_options_get_type ())
+#define GST_MIXER_OPTIONS(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER_OPTIONS, GstMixerOptions))
+#define GST_MIXER_OPTIONS_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER_OPTIONS, GstMixerOptionsClass))
+#define GST_IS_MIXER_OPTIONS(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER_OPTIONS))
+#define GST_IS_MIXER_OPTIONS_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER_OPTIONS))
+#define GST_MIXER_OPTIONS_GET_CLASS(inst) \
+  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_MIXER_OPTIONS, GstMixerOptionsClass))
+
+typedef struct _GstMixerOptions GstMixerOptions;
+typedef struct _GstMixerOptionsClass GstMixerOptionsClass;
+
+struct _GstMixerOptions {
+  GstMixerTrack parent;
+  GList *values;
+};
+
+struct _GstMixerOptionsClass {
+  GstMixerTrackClass parent;
+};
+
+void gst_mixer_set_option (GstMixer * mixer, GstMixerOptions * opts, gchar * value);
+const gchar * gst_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts);
+GList * gst_mixer_options_get_values (GstMixerOptions *mixer_options);
+void gst_mixer_message_parse_option_changed (GstMessage *message,
+					     GstMixerOptions ** options,
+					     const gchar **value);
+void gst_mixer_message_parse_options_list_changed (GstMessage *message,
+						   GstMixerOptions **options);
+
+G_END_DECLS
+
+#endif /* __XFCE4_MIXER_ALSA_H */
--- a/src/xvd_mixer.c
+++ b/src/xvd_mixer.c
@@ -30,6 +30,9 @@
 #include "xvd_notify.h"
 #endif
 
+static void set_mixer_name (GstMixer *mixer, const gchar *name);
+
+#ifndef XFCE4_MIXER_ALSA
 static gboolean 
 _xvd_mixer_filter_mixer (GstMixer *tmp_mixer, 
 						 gpointer user_data)
@@ -37,10 +40,7 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m
 	GstElementFactory *factory;
 	const gchar       *long_name;
 	gchar             *device_name;
-	gchar             *internal_name;
 	gchar             *name;
-	gchar             *p;
-	gint               length;
 	gint              *counter = user_data;
 
 	/* Get long name of the mixer element */
@@ -61,6 +61,20 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m
 	/* Free device name */
 	g_free (device_name);
 	
+	set_mixer_name (mixer, name);
+
+	g_free (name);
+	
+	return TRUE;
+}
+#endif /* !XFCE4_MIXER_ALSA */
+
+static void set_mixer_name (GstMixer *mixer, const gchar *name)
+{
+	gint               length;
+	gchar             *internal_name;
+	const gchar       *p;
+
 	/* Count alpha-numeric characters in the name */
 	for (length = 0, p = name; *p != '\0'; ++p)
 		if (g_ascii_isalnum (*p))
@@ -74,12 +88,9 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m
 	internal_name[length] = '\0';
 
 	/* Remember name for use by xfce4-mixer */
-	g_object_set_data_full (G_OBJECT (tmp_mixer), "xfce-mixer-internal-name", internal_name, (GDestroyNotify) g_free);
-			
-	g_free (name);
-	
-	return TRUE;
-}
+	g_object_set_data_full (G_OBJECT (mixer), "xfce-mixer-internal-name",
+				internal_name, (GDestroyNotify) g_free);
+}			
 
 #ifdef HAVE_LIBNOTIFY
 static void 
@@ -133,11 +144,25 @@ _xvd_mixer_bus_message (GstBus *bus, Gst
 }
 #endif
 
+#ifdef XFCE4_MIXER_ALSA
+static void init_mixer (gpointer data, gpointer user_data)
+{
+	GstMixer *card = GST_MIXER (data);
+
+	set_mixer_name (card, gst_mixer_get_card_name (card));
+}
+#endif
+
 void 
 xvd_mixer_init(XvdInstance *Inst)
 {
 	/* Get list of all available mixer devices */
+#ifdef XFCE4_MIXER_ALSA
+	Inst->mixers = gst_mixer_probe_devices ();
+	g_list_foreach (Inst->mixers, (GFunc) init_mixer, NULL);
+#else
 	Inst->mixers = gst_audio_default_registry_mixer_filter (_xvd_mixer_filter_mixer, FALSE, &(Inst->nameless_cards_count));
+#endif
 }
 
 #ifdef HAVE_LIBNOTIFY
--- a/src/xvd_mixer.h
+++ b/src/xvd_mixer.h
@@ -24,7 +24,12 @@
 #ifndef _XVD_MIXER_H
 #define _XVD_MIXER_H
 
+#ifdef XFCE4_MIXER_ALSA
+#include <gst/gst.h>
+#include "xfce4-mixer-alsa.h"
+#else
 #include <gst/audio/mixerutils.h>
+#endif
 
 #include "xvd_data_types.h"
 
--- a/src/xvd_data_types.h
+++ b/src/xvd_data_types.h
@@ -38,7 +38,12 @@
 
 #include <xfconf/xfconf.h>
 
+#ifdef XFCE4_MIXER_ALSA
+#include <gst/gst.h>
+#include "xfce4-mixer-alsa.h"
+#else
 #include <gst/audio/mixerutils.h>
+#endif
 
 #include <keybinder.h>
 #ifdef HAVE_LIBNOTIFY
--- a/src/main.c
+++ b/src/main.c
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include <gtk/gtk.h>
 #include "xvd_keys.h"
 #include "xvd_data_types.h"
 #include "xvd_mixer.h"
openSUSE Build Service is sponsored by