r3815 - in trunk/src/target/OM-2007.2/applications/openmoko-dialer2: . src/dialer

chris at sita.openmoko.org chris at sita.openmoko.org
Fri Jan 11 15:26:01 CET 2008


Author: chris
Date: 2008-01-11 15:25:55 +0100 (Fri, 11 Jan 2008)
New Revision: 3815

Added:
   trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.c
   trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.h
Modified:
   trunk/src/target/OM-2007.2/applications/openmoko-dialer2/ChangeLog
   trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.c
   trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.h
Log:
        * src/dialer/moko-alsa-volume-button.[ch]:
        * src/dialer/moko-alsa-volume-control.[ch]:
        Refactor into separate control a widget objects for flexibility


Modified: trunk/src/target/OM-2007.2/applications/openmoko-dialer2/ChangeLog
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-dialer2/ChangeLog	2008-01-11 13:50:29 UTC (rev 3814)
+++ trunk/src/target/OM-2007.2/applications/openmoko-dialer2/ChangeLog	2008-01-11 14:25:55 UTC (rev 3815)
@@ -1,6 +1,12 @@
 2008-01-11  Chris Lord  <chris at openedhand.com>
 
 	* src/dialer/moko-alsa-volume-button.[ch]:
+	* src/dialer/moko-alsa-volume-control.[ch]:
+	Refactor into separate control a widget objects for flexibility
+
+2008-01-11  Chris Lord  <chris at openedhand.com>
+
+	* src/dialer/moko-alsa-volume-button.[ch]:
 	Add an alsa volume control widget (requires refactoring)
 
 2008-01-09  Chris Lord  <chris at openedhand.com>

Modified: trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.c	2008-01-11 13:50:29 UTC (rev 3814)
+++ trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.c	2008-01-11 14:25:55 UTC (rev 3815)
@@ -1,7 +1,5 @@
 
 #include "moko-alsa-volume-button.h"
-#include <gtk/gtk.h>
-#include <alsa/asoundlib.h>
 
 G_DEFINE_TYPE (MokoAlsaVolumeButton, moko_alsa_volume_button, \
 	GTK_TYPE_SCALE_BUTTON)
@@ -13,250 +11,23 @@
 typedef struct _MokoAlsaVolumeButtonPrivate MokoAlsaVolumeButtonPrivate;
 
 struct _MokoAlsaVolumeButtonPrivate {
-	gchar *device;
-	snd_mixer_selem_id_t *element;
-	
-	snd_mixer_t *mixer_handle;
-	snd_mixer_elem_t *mixer_elem;
-	gint control_type;
-	
-	glong min;
-	glong max;
+	MokoAlsaVolumeControl *control;
 };
 
 enum {
-	PROP_DEVICE = 1,
-	PROP_ELEMENT,
+	PROP_CONTROL = 1,
 };
 
-enum {
-	PLAYBACK,
-	CAPTURE,
-	CONTROL
-};
-
-static gboolean
-io_func (GIOChannel *source, GIOCondition condition, MokoAlsaVolumeButton *self)
-{
-	switch (condition) {
-	    case G_IO_IN : {
-		MokoAlsaVolumeButtonPrivate *priv =
-			ALSA_VOLUME_BUTTON_PRIVATE (self);
-		snd_mixer_handle_events (priv->mixer_handle);
-		
-		break;
-	    }
-	    case G_IO_ERR :
-	    case G_IO_NVAL :
-		g_warning ("Encountered an error, stopping IO watch");
-		return FALSE;
-	    default :
-		g_warning ("Unhandled IO condition");
-		break;
-	}
-	
-	return TRUE;
-}
-
 static void
-update_adjustment (MokoAlsaVolumeButton *button)
-{
-	long volume, old_volume;
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
-	
-	/* TODO: Average out volume across channels? */
-	
-	switch (priv->control_type) {
-	    case PLAYBACK :
-		snd_mixer_selem_get_playback_volume (
-			priv->mixer_elem, 0, &volume);
-		break;
-	    case CAPTURE :
-		snd_mixer_selem_get_capture_volume (
-			priv->mixer_elem, 0, &volume);
-		break;
-	    case CONTROL :
-	    default :
-		/* TODO: Handle switches? */
-		g_warning ("Unhandled control type");
-		return;
-	}
-	
-	old_volume = (long)((gtk_scale_button_get_value (
-		GTK_SCALE_BUTTON (button)) / 100.0) *
-		(gdouble)(priv->max-priv->min)) + priv->min;
-	if (volume != old_volume)
-		gtk_scale_button_set_value (GTK_SCALE_BUTTON (button),
-			((gdouble)(volume-priv->min)) /
-				((gdouble)priv->max-priv->min) * 100.0);
-}
-
-static int
-mixer_event_cb (snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
-{
-	/*MokoAlsaVolumeButton *button = MOKO_ALSA_VOLUME_BUTTON (
-		snd_mixer_get_callback_private (mixer));
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);*/
-	
-	return 0;
-}
-
-static int
-mixer_elem_event_cb (snd_mixer_elem_t *elem, unsigned int mask)
-{
-	MokoAlsaVolumeButton *button = MOKO_ALSA_VOLUME_BUTTON (
-		snd_mixer_elem_get_callback_private (elem));
-
-	update_adjustment (button);
-
-	return 0;
-}
-
-static void
-open_mixer (MokoAlsaVolumeButton *self)
-{
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	if (snd_mixer_open (&priv->mixer_handle, 0) != 0) {
-		g_warning ("Failed to get mixer handle");
-		priv->mixer_handle = NULL;
-		return;
-	}
-	
-	snd_mixer_set_callback (priv->mixer_handle, mixer_event_cb);
-	snd_mixer_set_callback_private (priv->mixer_handle, self);
-	
-	g_debug ("Opened mixer");
-}
-
-static void
-start_polling (MokoAlsaVolumeButton *self)
-{
-	struct pollfd *fds;
-	gint i, nfds;
-
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	if ((nfds = snd_mixer_poll_descriptors_count (priv->mixer_handle)) <= 0){
-		g_warning ("No poll descriptors on mixer?");
-		return;
-	}
-	
-	fds = g_new0 (struct pollfd, nfds);
-	if (snd_mixer_poll_descriptors (priv->mixer_handle, fds, nfds) < 0) {
-		g_warning ("Error getting polling descriptors for sound mixer");
-		g_free (fds);
-		return;
-	}
-	
-	for (i = 0; i < nfds; i++) {
-		GIOChannel *channel = g_io_channel_unix_new (fds[i].fd);
-		g_debug ("Adding IO watch (IN: %d, OUT: %d)",
-			fds[i].events & POLLIN, fds[i].events & POLLOUT);
-		g_io_add_watch (channel,
-			((fds[i].events & POLLIN) ? G_IO_IN : 0) |
-			((fds[i].events & POLLOUT) ? G_IO_OUT : 0) |
-			((fds[i].events & POLLPRI) ? G_IO_PRI : 0) |
-			((fds[i].events & POLLERR) ? G_IO_ERR : 0) |
-			((fds[i].events & POLLHUP) ? G_IO_HUP : 0) |
-			((fds[i].events & POLLNVAL) ? G_IO_NVAL : 0),
-			(GIOFunc)io_func, self);
-	}
-	g_free (fds);
-	
-	g_debug ("Polling for events...");
-}
-
-static void
-close_mixer (MokoAlsaVolumeButton *self)
-{
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	if (!priv->mixer_handle) return;
-	
-	snd_mixer_close (priv->mixer_handle);
-	priv->mixer_handle = NULL;
-	g_debug ("Closed mixer");
-}
-
-static void
-detach_mixer (MokoAlsaVolumeButton *self)
-{
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	if (priv->mixer_handle && priv->device &&
-	    priv->element && priv->mixer_elem) {
-		snd_mixer_detach (priv->mixer_handle, priv->device);
-		priv->mixer_elem = NULL;
-		g_debug ("Detached from mixer");
-		close_mixer (self);
-	}
-}
-
-static void
-attach_mixer (MokoAlsaVolumeButton *self)
-{
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	g_debug ("Trying to attach... %p, %s, %p", priv->mixer_handle,
-		priv->device, priv->element);
-	
-	open_mixer (self);
-	
-	if (priv->mixer_handle && priv->device && priv->element &&
-	    (snd_mixer_attach (priv->mixer_handle, priv->device) == 0) &&
-	    (snd_mixer_selem_register (priv->mixer_handle, NULL, NULL) == 0) &&
-	    (snd_mixer_load (priv->mixer_handle) == 0)) {
-		priv->mixer_elem = snd_mixer_find_selem (
-			priv->mixer_handle, priv->element);
-		if (!priv->mixer_elem) {
-			g_warning ("Unable to find mixer element");
-			snd_mixer_detach (priv->mixer_handle, priv->device);
-			close_mixer (self);
-		} else {
-			g_debug ("Attached to mixer");
-			
-			if (snd_mixer_selem_has_playback_volume (
-			    priv->mixer_elem)) {
-				priv->control_type = PLAYBACK;
-				snd_mixer_selem_get_playback_volume_range (
-					priv->mixer_elem,
-					&priv->min, &priv->max);
-			} else if (snd_mixer_selem_has_capture_volume (
-				 priv->mixer_elem)) {
-				priv->control_type = CAPTURE;
-				snd_mixer_selem_get_capture_volume_range (
-					priv->mixer_elem,
-					&priv->min, &priv->max);
-			} else
-				priv->control_type = CONTROL;
-			
-			snd_mixer_elem_set_callback (
-				priv->mixer_elem, mixer_elem_event_cb);
-			snd_mixer_elem_set_callback_private (
-				priv->mixer_elem, self);
-			
-			start_polling (self);
-			update_adjustment (self);
-		}
-	} else {
-		close_mixer (self);
-	}
-}
-
-static void
 moko_alsa_volume_button_get_property (GObject *object, guint property_id,
 				      GValue *value, GParamSpec *pspec)
 {
 	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (object);
 	
 	switch (property_id) {
-	    case PROP_DEVICE :
-		g_value_set_string (value, priv->device);
+	    case PROP_CONTROL :
+		g_value_set_object (value, priv->control);
 		break;
-	    case PROP_ELEMENT :
-		g_value_set_pointer (value, priv->element);
-		break;
 	    default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 	}
@@ -267,17 +38,11 @@
                               const GValue *value, GParamSpec *pspec)
 {
 	switch (property_id) {
-	    case PROP_DEVICE :
-		moko_alsa_volume_button_set_device (
+	    case PROP_CONTROL :
+		moko_alsa_volume_button_set_control (
 			MOKO_ALSA_VOLUME_BUTTON (object),
-			g_value_get_string (value));
+			g_value_get_object (value));
 		break;
-		
-	    case PROP_ELEMENT :
-		moko_alsa_volume_button_set_element (
-			MOKO_ALSA_VOLUME_BUTTON (object),
-			(snd_mixer_selem_id_t *)g_value_get_pointer (value));
-		break;
 	    default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 	}
@@ -289,13 +54,8 @@
 	MokoAlsaVolumeButton *button = MOKO_ALSA_VOLUME_BUTTON (object);
 	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
 	
-	detach_mixer (button);
+	if (priv->control) g_object_unref (priv->control);
 	
-	g_free (priv->device);
-	if (priv->element) {
-		snd_mixer_selem_id_free (priv->element);
-	}
-	
 	G_OBJECT_CLASS (moko_alsa_volume_button_parent_class)->
 		finalize (object);
 }
@@ -303,33 +63,15 @@
 static void
 moko_alsa_volume_button_value_changed (GtkScaleButton *button, gdouble value)
 {
-	long volume;
-	
 	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
 	
-	if (!priv->mixer_elem) return;
-	
 	if (GTK_SCALE_BUTTON_CLASS (moko_alsa_volume_button_parent_class)->
 	    value_changed)
 		GTK_SCALE_BUTTON_CLASS (moko_alsa_volume_button_parent_class)->
 			value_changed (button, value);
 	
-	volume = (long)((gtk_scale_button_get_value (
-		GTK_SCALE_BUTTON (button)) / 100.0) *
-		(gdouble)(priv->max-priv->min)) + priv->min;
-
-	switch (priv->control_type) {
-	    case PLAYBACK :
-		snd_mixer_selem_set_playback_volume_all (
-			priv->mixer_elem, volume);
-		break;
-	    case CAPTURE :
-		snd_mixer_selem_set_capture_volume_all (
-			priv->mixer_elem, volume);
-		break;
-	    default :
-		g_warning ("Unhandled control type");
-	}
+	moko_alsa_volume_control_set_volume (priv->control,
+		gtk_scale_button_get_value (button) / 100.0);
 }
 
 static void
@@ -348,30 +90,18 @@
 
 	g_object_class_install_property (
 		object_class,
-		PROP_DEVICE,
-		g_param_spec_string (
-			"device",
-			"gchar *",
-			"The alsa device name.",
-			"default",
+		PROP_CONTROL,
+		g_param_spec_object (
+			"control",
+			"MokoAlsaVolumeControl",
+			"The volume control object to hook onto.",
+			MOKO_TYPE_ALSA_VOLUME_CONTROL,
 			G_PARAM_READWRITE));
-
-	g_object_class_install_property (
-		object_class,
-		PROP_ELEMENT,
-		g_param_spec_pointer (
-			"element",
-			"snd_mixer_selem_id_t",
-			"The alsa simple mixer element ID.",
-			G_PARAM_READWRITE));
 }
 
 static void
 moko_alsa_volume_button_init (MokoAlsaVolumeButton *self)
 {
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (self);
-	
-	priv->device = g_strdup ("default");
 }
 
 GtkWidget *
@@ -380,144 +110,40 @@
 	return GTK_WIDGET (g_object_new (MOKO_TYPE_ALSA_VOLUME_BUTTON, NULL));
 }
 
-void
-moko_alsa_volume_button_set_device (MokoAlsaVolumeButton *button,
-				    const gchar *device)
+static void
+volume_changed_cb (MokoAlsaVolumeControl *control, gdouble volume,
+		   MokoAlsaVolumeButton *button)
 {
-	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
-
-	detach_mixer (button);
-	g_free (priv->device);
-	priv->device = g_strdup (device);
-	g_debug ("Device set: %s", device);
-	attach_mixer (button);
+	gtk_scale_button_set_value (GTK_SCALE_BUTTON (button), volume * 100.0);
 }
 
 void
-moko_alsa_volume_button_set_element (MokoAlsaVolumeButton *button,
-				     snd_mixer_selem_id_t *element)
+moko_alsa_volume_button_set_control (MokoAlsaVolumeButton *button,
+				     MokoAlsaVolumeControl *control)
 {
 	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
-
-	detach_mixer (button);
-	if (priv->element) {
-		snd_mixer_selem_id_free (priv->element);
-		priv->element = NULL;
-	}
 	
-	if (snd_mixer_selem_id_malloc (&priv->element) != 0) {
-		g_warning ("Unable to allocate mixer element id");
-	} else if (element) {
-		snd_mixer_selem_id_copy (priv->element, element);
-		g_debug ("Element set");
-		attach_mixer (button);
+	if (priv->control) {
+		g_signal_handlers_disconnect_by_func (priv->control,
+			volume_changed_cb, button);
+		g_object_unref (priv->control);
+		priv->control = NULL;
 	}
-}
-
-void
-moko_alsa_volume_button_set_device_from_card_number (
-	MokoAlsaVolumeButton *button, gint number)
-{
-	void **hints;
 	
-	if (snd_device_name_hint (number, "pcm", &hints) == 0) {
-		gchar *device = strdup (snd_device_name_get_hint (
-			hints[0], "NAME"));
-		snd_device_name_free_hint (hints);
-		strchr (device, ':')[0] = '\0';
-		
-		moko_alsa_volume_button_set_device (button, device);
-		g_free (device);
-	} else
-		g_warning ("Unable to find card number %d", number);
-}
-
-void
-moko_alsa_volume_button_set_device_from_name (MokoAlsaVolumeButton *button,
-					      const gchar *name)
-{
-	gint i = -1;
-	
-	if (!name) {
-		moko_alsa_volume_button_set_device (button, NULL);
-		return;
+	if (control) {
+		priv->control = g_object_ref (control);
+		g_signal_connect (priv->control, "volume_changed",
+			G_CALLBACK (volume_changed_cb), button);
+		gtk_scale_button_set_value (GTK_SCALE_BUTTON (button),
+			moko_alsa_volume_control_get_volume (control) * 100.0);
 	}
-
-	while (snd_card_next (&i) == 0) {
-		void **hints;
-	
-		if (snd_device_name_hint (i, "pcm", &hints) == 0) {
-			gchar *device = strdup (snd_device_name_get_hint (
-				hints[0], "NAME"));
-			snd_device_name_free_hint (hints);
-			strchr (device, ':')[0] = '\0';
-			
-			if (strcmp (device, name) == 0) {
-				moko_alsa_volume_button_set_device (
-					button, device);
-				g_free (device);
-				return;
-			}
-			g_free (device);
-		}
-	}
-	
-	g_warning ("Card '%s' not found", name);
 }
 
-void
-moko_alsa_volume_button_set_element_from_name (MokoAlsaVolumeButton *button,
-					       const gchar *name)
+MokoAlsaVolumeControl *
+moko_alsa_volume_button_get_control (MokoAlsaVolumeButton *button)
 {
 	MokoAlsaVolumeButtonPrivate *priv = ALSA_VOLUME_BUTTON_PRIVATE (button);
-
-	if (!priv->device) return;
 	
-	detach_mixer (button);
-	
-	if (!name) {
-		moko_alsa_volume_button_set_element (button, NULL);
-		return;
-	}
-	
-	open_mixer (button);
-	if ((snd_mixer_attach (priv->mixer_handle, priv->device) == 0) &&
-	    (snd_mixer_selem_register (priv->mixer_handle, NULL, NULL) == 0) &&
-	    (snd_mixer_load (priv->mixer_handle) == 0)) {
-		snd_mixer_elem_t *elem;
-		
-		elem = snd_mixer_first_elem (priv->mixer_handle);
-		while (elem) {
-			const char *elem_name = snd_mixer_selem_get_name (elem);
-			if (strcmp (elem_name, name) == 0)
-				break;
-			elem = snd_mixer_elem_next (elem);
-		}
-		
-		if (!elem) {
-			snd_mixer_detach (priv->mixer_handle, priv->device);
-			close_mixer (button);
-			g_warning ("Mixer element '%s' not found", name);
-			attach_mixer (button);
-		} else {
-			snd_mixer_selem_id_t *id;
-			if (snd_mixer_selem_id_malloc (&id) != 0) {
-				g_warning ("Unable to allocate element id");
-				snd_mixer_detach (
-					priv->mixer_handle, priv->device);
-				close_mixer (button);
-			} else {
-				snd_mixer_selem_get_id (elem, id);
-				snd_mixer_detach (
-					priv->mixer_handle, priv->device);
-				close_mixer (button);
-				g_debug ("Setting element ID");
-				moko_alsa_volume_button_set_element (
-					button, id);
-				snd_mixer_selem_id_free (id);
-			}
-		}
-	} else
-		g_warning ("Unable to open mixer on card '%s'", priv->device);
+	return priv->control;
 }
 

Modified: trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.h	2008-01-11 13:50:29 UTC (rev 3814)
+++ trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-button.h	2008-01-11 14:25:55 UTC (rev 3815)
@@ -3,7 +3,7 @@
 
 #include <glib-object.h>
 #include <gtk/gtk.h>
-#include <alsa/asoundlib.h>
+#include "moko-alsa-volume-control.h"
 
 G_BEGIN_DECLS
 
@@ -41,21 +41,12 @@
 
 GtkWidget *moko_alsa_volume_button_new (void);
 
-void moko_alsa_volume_button_set_device (MokoAlsaVolumeButton *button,
-					 const gchar *device);
+void moko_alsa_volume_button_set_control (MokoAlsaVolumeButton *button,
+					  MokoAlsaVolumeControl *control);
 
-void moko_alsa_volume_button_set_element (MokoAlsaVolumeButton *button,
-					  snd_mixer_selem_id_t *element);
+MokoAlsaVolumeControl *moko_alsa_volume_button_get_control (
+			MokoAlsaVolumeButton *button);
 
-void moko_alsa_volume_button_set_device_from_card_number (
-			MokoAlsaVolumeButton *button, gint number);
-
-void moko_alsa_volume_button_set_device_from_name (MokoAlsaVolumeButton *button,
-						   const gchar *name);
-
-void moko_alsa_volume_button_set_element_from_name (
-			MokoAlsaVolumeButton *button, const gchar *name);
-
 G_END_DECLS
 
 #endif /* _MOKO_ALSA_VOLUME_BUTTON */

Added: trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.c
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.c	2008-01-11 13:50:29 UTC (rev 3814)
+++ trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.c	2008-01-11 14:25:55 UTC (rev 3815)
@@ -0,0 +1,541 @@
+
+#include "moko-alsa-volume-control.h"
+
+G_DEFINE_TYPE (MokoAlsaVolumeControl, moko_alsa_volume_control, \
+	G_TYPE_OBJECT)
+
+#define ALSA_VOLUME_CONTROL_PRIVATE(o) \
+	(G_TYPE_INSTANCE_GET_PRIVATE ((o), MOKO_TYPE_ALSA_VOLUME_CONTROL, \
+	 MokoAlsaVolumeControlPrivate))
+
+typedef struct _MokoAlsaVolumeControlPrivate MokoAlsaVolumeControlPrivate;
+
+struct _MokoAlsaVolumeControlPrivate {
+	gchar *device;
+	snd_mixer_selem_id_t *element;
+	
+	snd_mixer_t *mixer_handle;
+	snd_mixer_elem_t *mixer_elem;
+	gint control_type;
+	
+	glong min;
+	glong max;
+	glong volume;
+};
+
+enum {
+	PROP_DEVICE = 1,
+	PROP_ELEMENT,
+};
+
+enum {
+	PLAYBACK,
+	CAPTURE,
+	CONTROL
+};
+
+enum {
+	VOLUME_CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static gboolean
+io_func (GIOChannel *source, GIOCondition condition, MokoAlsaVolumeControl *self)
+{
+	switch (condition) {
+	    case G_IO_IN : {
+		MokoAlsaVolumeControlPrivate *priv =
+			ALSA_VOLUME_CONTROL_PRIVATE (self);
+		snd_mixer_handle_events (priv->mixer_handle);
+		
+		break;
+	    }
+	    case G_IO_ERR :
+	    case G_IO_NVAL :
+		g_warning ("Encountered an error, stopping IO watch");
+		return FALSE;
+	    default :
+		g_warning ("Unhandled IO condition");
+		break;
+	}
+	
+	return TRUE;
+}
+
+static void
+update_volume (MokoAlsaVolumeControl *control)
+{
+	long volume;
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (control);
+	
+	/* TODO: Average out volume across channels? */
+	
+	switch (priv->control_type) {
+	    case PLAYBACK :
+		snd_mixer_selem_get_playback_volume (
+			priv->mixer_elem, 0, &volume);
+		break;
+	    case CAPTURE :
+		snd_mixer_selem_get_capture_volume (
+			priv->mixer_elem, 0, &volume);
+		break;
+	    case CONTROL :
+	    default :
+		/* TODO: Handle switches? */
+		g_warning ("Unhandled control type");
+		return;
+	}
+	
+	if (priv->volume != volume) {
+		priv->volume = volume;
+		g_signal_emit (control, signals[VOLUME_CHANGED],
+			0, (gdouble)(priv->volume - priv->min) /
+				(gdouble)(priv->max - priv->min));
+	}
+}
+
+static int
+mixer_event_cb (snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
+{
+	/* Do we need to update here? */
+	
+	return 0;
+}
+
+static int
+mixer_elem_event_cb (snd_mixer_elem_t *elem, unsigned int mask)
+{
+	MokoAlsaVolumeControl *control = MOKO_ALSA_VOLUME_CONTROL (
+		snd_mixer_elem_get_callback_private (elem));
+
+	update_volume (control);
+
+	return 0;
+}
+
+static void
+open_mixer (MokoAlsaVolumeControl *self)
+{
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	if (snd_mixer_open (&priv->mixer_handle, 0) != 0) {
+		g_warning ("Failed to get mixer handle");
+		priv->mixer_handle = NULL;
+		return;
+	}
+	
+	snd_mixer_set_callback (priv->mixer_handle, mixer_event_cb);
+	snd_mixer_set_callback_private (priv->mixer_handle, self);
+	
+	g_debug ("Opened mixer");
+}
+
+static void
+start_polling (MokoAlsaVolumeControl *self)
+{
+	struct pollfd *fds;
+	gint i, nfds;
+
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	if ((nfds = snd_mixer_poll_descriptors_count (priv->mixer_handle))<=0) {
+		g_warning ("No poll descriptors on mixer?");
+		return;
+	}
+	
+	fds = g_new0 (struct pollfd, nfds);
+	if (snd_mixer_poll_descriptors (priv->mixer_handle, fds, nfds) < 0) {
+		g_warning ("Error getting polling descriptors for sound mixer");
+		g_free (fds);
+		return;
+	}
+	
+	for (i = 0; i < nfds; i++) {
+		GIOChannel *channel = g_io_channel_unix_new (fds[i].fd);
+		g_debug ("Adding IO watch (IN: %d, OUT: %d)",
+			fds[i].events & POLLIN, fds[i].events & POLLOUT);
+		g_io_add_watch (channel,
+			((fds[i].events & POLLIN) ? G_IO_IN : 0) |
+			((fds[i].events & POLLOUT) ? G_IO_OUT : 0) |
+			((fds[i].events & POLLPRI) ? G_IO_PRI : 0) |
+			((fds[i].events & POLLERR) ? G_IO_ERR : 0) |
+			((fds[i].events & POLLHUP) ? G_IO_HUP : 0) |
+			((fds[i].events & POLLNVAL) ? G_IO_NVAL : 0),
+			(GIOFunc)io_func, self);
+	}
+	g_free (fds);
+	
+	g_debug ("Polling for events...");
+}
+
+static void
+close_mixer (MokoAlsaVolumeControl *self)
+{
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	if (!priv->mixer_handle) return;
+	
+	snd_mixer_close (priv->mixer_handle);
+	priv->mixer_handle = NULL;
+	g_debug ("Closed mixer");
+}
+
+static void
+detach_mixer (MokoAlsaVolumeControl *self)
+{
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	if (priv->mixer_handle && priv->device &&
+	    priv->element && priv->mixer_elem) {
+		snd_mixer_detach (priv->mixer_handle, priv->device);
+		priv->mixer_elem = NULL;
+		g_debug ("Detached from mixer");
+		close_mixer (self);
+	}
+}
+
+static void
+attach_mixer (MokoAlsaVolumeControl *self)
+{
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	g_debug ("Trying to attach... %p, %s, %p", priv->mixer_handle,
+		priv->device, priv->element);
+	
+	open_mixer (self);
+	
+	if (priv->mixer_handle && priv->device && priv->element &&
+	    (snd_mixer_attach (priv->mixer_handle, priv->device) == 0) &&
+	    (snd_mixer_selem_register (priv->mixer_handle, NULL, NULL) == 0) &&
+	    (snd_mixer_load (priv->mixer_handle) == 0)) {
+		priv->mixer_elem = snd_mixer_find_selem (
+			priv->mixer_handle, priv->element);
+		if (!priv->mixer_elem) {
+			g_warning ("Unable to find mixer element");
+			snd_mixer_detach (priv->mixer_handle, priv->device);
+			close_mixer (self);
+		} else {
+			g_debug ("Attached to mixer");
+			
+			if (snd_mixer_selem_has_playback_volume (
+			    priv->mixer_elem)) {
+				priv->control_type = PLAYBACK;
+				snd_mixer_selem_get_playback_volume_range (
+					priv->mixer_elem,
+					&priv->min, &priv->max);
+			} else if (snd_mixer_selem_has_capture_volume (
+				 priv->mixer_elem)) {
+				priv->control_type = CAPTURE;
+				snd_mixer_selem_get_capture_volume_range (
+					priv->mixer_elem,
+					&priv->min, &priv->max);
+			} else
+				priv->control_type = CONTROL;
+			
+			snd_mixer_elem_set_callback (
+				priv->mixer_elem, mixer_elem_event_cb);
+			snd_mixer_elem_set_callback_private (
+				priv->mixer_elem, self);
+			
+			start_polling (self);
+			update_volume (self);
+		}
+	} else {
+		close_mixer (self);
+	}
+}
+
+static void
+moko_alsa_volume_control_get_property (GObject *object, guint property_id,
+				      GValue *value, GParamSpec *pspec)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (object);
+	
+	switch (property_id) {
+	    case PROP_DEVICE :
+		g_value_set_string (value, priv->device);
+		break;
+	    case PROP_ELEMENT :
+		g_value_set_pointer (value, priv->element);
+		break;
+	    default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+static void
+moko_alsa_volume_control_set_property (GObject *object, guint property_id,
+                              const GValue *value, GParamSpec *pspec)
+{
+	switch (property_id) {
+	    case PROP_DEVICE :
+		moko_alsa_volume_control_set_device (
+			MOKO_ALSA_VOLUME_CONTROL (object),
+			g_value_get_string (value));
+		break;
+		
+	    case PROP_ELEMENT :
+		moko_alsa_volume_control_set_element (
+			MOKO_ALSA_VOLUME_CONTROL (object),
+			(snd_mixer_selem_id_t *)g_value_get_pointer (value));
+		break;
+	    default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+	}
+}
+
+static void
+moko_alsa_volume_control_finalize (GObject *object)
+{
+	MokoAlsaVolumeControl *control = MOKO_ALSA_VOLUME_CONTROL (object);
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+	
+	detach_mixer (control);
+	
+	g_free (priv->device);
+	if (priv->element) {
+		snd_mixer_selem_id_free (priv->element);
+	}
+	
+	G_OBJECT_CLASS (moko_alsa_volume_control_parent_class)->
+		finalize (object);
+}
+
+static void
+moko_alsa_volume_control_class_init (MokoAlsaVolumeControlClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (MokoAlsaVolumeControlPrivate));
+
+	object_class->get_property = moko_alsa_volume_control_get_property;
+	object_class->set_property = moko_alsa_volume_control_set_property;
+	object_class->finalize = moko_alsa_volume_control_finalize;
+	
+	g_object_class_install_property (
+		object_class,
+		PROP_DEVICE,
+		g_param_spec_string (
+			"device",
+			"gchar *",
+			"The alsa device name.",
+			"default",
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ELEMENT,
+		g_param_spec_pointer (
+			"element",
+			"snd_mixer_selem_id_t",
+			"The alsa simple mixer element ID.",
+			G_PARAM_READWRITE));
+	
+	signals[VOLUME_CHANGED] =
+		g_signal_new ("volume_changed",
+			G_OBJECT_CLASS_TYPE (object_class),
+			G_SIGNAL_RUN_LAST,
+			G_STRUCT_OFFSET (MokoAlsaVolumeControlClass,
+				volume_changed),
+			NULL, NULL,
+			g_cclosure_marshal_VOID__DOUBLE,
+			G_TYPE_NONE, 1, G_TYPE_DOUBLE);
+}
+
+static void
+moko_alsa_volume_control_init (MokoAlsaVolumeControl *self)
+{
+	MokoAlsaVolumeControlPrivate *priv = ALSA_VOLUME_CONTROL_PRIVATE (self);
+	
+	priv->device = g_strdup ("default");
+}
+
+MokoAlsaVolumeControl *
+moko_alsa_volume_control_new (void)
+{
+	return g_object_new (MOKO_TYPE_ALSA_VOLUME_CONTROL, NULL);
+}
+
+void
+moko_alsa_volume_control_set_device (MokoAlsaVolumeControl *control,
+				    const gchar *device)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+
+	detach_mixer (control);
+	g_free (priv->device);
+	priv->device = g_strdup (device);
+	g_debug ("Device set: %s", device);
+	attach_mixer (control);
+}
+
+void
+moko_alsa_volume_control_set_element (MokoAlsaVolumeControl *control,
+				     snd_mixer_selem_id_t *element)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+
+	detach_mixer (control);
+	if (priv->element) {
+		snd_mixer_selem_id_free (priv->element);
+		priv->element = NULL;
+	}
+	
+	if (snd_mixer_selem_id_malloc (&priv->element) != 0) {
+		g_warning ("Unable to allocate mixer element id");
+	} else if (element) {
+		snd_mixer_selem_id_copy (priv->element, element);
+		g_debug ("Element set");
+		attach_mixer (control);
+	}
+}
+
+void
+moko_alsa_volume_control_set_device_from_card_number (
+	MokoAlsaVolumeControl *control, gint number)
+{
+	void **hints;
+	
+	if (snd_device_name_hint (number, "pcm", &hints) == 0) {
+		gchar *device = strdup (snd_device_name_get_hint (
+			hints[0], "NAME"));
+		snd_device_name_free_hint (hints);
+		strchr (device, ':')[0] = '\0';
+		
+		moko_alsa_volume_control_set_device (control, device);
+		g_free (device);
+	} else
+		g_warning ("Unable to find card number %d", number);
+}
+
+void
+moko_alsa_volume_control_set_device_from_name (MokoAlsaVolumeControl *control,
+					      const gchar *name)
+{
+	gint i = -1;
+	
+	if (!name) {
+		moko_alsa_volume_control_set_device (control, NULL);
+		return;
+	}
+
+	while (snd_card_next (&i) == 0) {
+		void **hints;
+	
+		if (snd_device_name_hint (i, "pcm", &hints) == 0) {
+			gchar *device = strdup (snd_device_name_get_hint (
+				hints[0], "NAME"));
+			snd_device_name_free_hint (hints);
+			strchr (device, ':')[0] = '\0';
+			
+			if (strcmp (device, name) == 0) {
+				moko_alsa_volume_control_set_device (
+					control, device);
+				g_free (device);
+				return;
+			}
+			g_free (device);
+		}
+	}
+	
+	g_warning ("Card '%s' not found", name);
+}
+
+void
+moko_alsa_volume_control_set_element_from_name (MokoAlsaVolumeControl *control,
+					       const gchar *name)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+
+	if (!priv->device) return;
+	
+	detach_mixer (control);
+	
+	if (!name) {
+		moko_alsa_volume_control_set_element (control, NULL);
+		return;
+	}
+	
+	open_mixer (control);
+	if ((snd_mixer_attach (priv->mixer_handle, priv->device) == 0) &&
+	    (snd_mixer_selem_register (priv->mixer_handle, NULL, NULL) == 0) &&
+	    (snd_mixer_load (priv->mixer_handle) == 0)) {
+		snd_mixer_elem_t *elem;
+		
+		elem = snd_mixer_first_elem (priv->mixer_handle);
+		while (elem) {
+			const char *elem_name = snd_mixer_selem_get_name (elem);
+			if (strcmp (elem_name, name) == 0)
+				break;
+			elem = snd_mixer_elem_next (elem);
+		}
+		
+		if (!elem) {
+			snd_mixer_detach (priv->mixer_handle, priv->device);
+			close_mixer (control);
+			g_warning ("Mixer element '%s' not found", name);
+			attach_mixer (control);
+		} else {
+			snd_mixer_selem_id_t *id;
+			if (snd_mixer_selem_id_malloc (&id) != 0) {
+				g_warning ("Unable to allocate element id");
+				snd_mixer_detach (
+					priv->mixer_handle, priv->device);
+				close_mixer (control);
+			} else {
+				snd_mixer_selem_get_id (elem, id);
+				snd_mixer_detach (
+					priv->mixer_handle, priv->device);
+				close_mixer (control);
+				g_debug ("Setting element ID");
+				moko_alsa_volume_control_set_element (
+					control, id);
+				snd_mixer_selem_id_free (id);
+			}
+		}
+	} else
+		g_warning ("Unable to open mixer on card '%s'", priv->device);
+}
+
+gdouble
+moko_alsa_volume_control_get_volume (MokoAlsaVolumeControl *control)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+
+	return (gdouble)(priv->volume - priv->min) /
+		(gdouble)(priv->max - priv->min);
+}
+
+void
+moko_alsa_volume_control_set_volume (MokoAlsaVolumeControl *control,
+				     gdouble volume)
+{
+	MokoAlsaVolumeControlPrivate *priv =
+		ALSA_VOLUME_CONTROL_PRIVATE (control);
+	
+	if (!priv->mixer_elem) return;
+	
+	volume = (long)(volume * (gdouble)(priv->max-priv->min)) + priv->min;
+
+	switch (priv->control_type) {
+	    case PLAYBACK :
+		snd_mixer_selem_set_playback_volume_all (
+			priv->mixer_elem, volume);
+		break;
+	    case CAPTURE :
+		snd_mixer_selem_set_capture_volume_all (
+			priv->mixer_elem, volume);
+		break;
+	    default :
+		g_warning ("Unhandled control type");
+	}
+}
+

Added: trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.h
===================================================================
--- trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.h	2008-01-11 13:50:29 UTC (rev 3814)
+++ trunk/src/target/OM-2007.2/applications/openmoko-dialer2/src/dialer/moko-alsa-volume-control.h	2008-01-11 14:25:55 UTC (rev 3815)
@@ -0,0 +1,74 @@
+#ifndef _MOKO_ALSA_VOLUME_CONTROL
+#define _MOKO_ALSA_VOLUME_CONTROL
+
+#include <glib.h>
+#include <glib-object.h>
+#include <alsa/asoundlib.h>
+
+G_BEGIN_DECLS
+
+#define MOKO_TYPE_ALSA_VOLUME_CONTROL moko_alsa_volume_control_get_type()
+
+#define MOKO_ALSA_VOLUME_CONTROL(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+	MOKO_TYPE_ALSA_VOLUME_CONTROL, MokoAlsaVolumeControl))
+
+#define MOKO_ALSA_VOLUME_CONTROL_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST ((klass), \
+	MOKO_TYPE_ALSA_VOLUME_CONTROL, MokoAlsaVolumeControlClass))
+
+#define MOKO_IS_ALSA_VOLUME_CONTROL(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+	MOKO_TYPE_ALSA_VOLUME_CONTROL))
+
+#define MOKO_IS_ALSA_VOLUME_CONTROL_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), \
+	MOKO_TYPE_ALSA_VOLUME_CONTROL))
+
+#define MOKO_ALSA_VOLUME_CONTROL_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), \
+	MOKO_TYPE_ALSA_VOLUME_CONTROL, MokoAlsaVolumeControlClass))
+
+typedef struct {
+	GObject parent;
+} MokoAlsaVolumeControl;
+
+typedef struct {
+	GObjectClass parent_class;
+	
+	/* Signals */
+	void	(*volume_changed)	(MokoAlsaVolumeControl *control,
+					 gdouble volume);
+} MokoAlsaVolumeControlClass;
+
+GType moko_alsa_volume_control_get_type (void);
+
+MokoAlsaVolumeControl *moko_alsa_volume_control_new (void);
+
+void moko_alsa_volume_control_set_device (MokoAlsaVolumeControl *control,
+					 const gchar *device);
+
+void moko_alsa_volume_control_set_element (MokoAlsaVolumeControl *control,
+					  snd_mixer_selem_id_t *element);
+
+void moko_alsa_volume_control_set_device_from_card_number (
+			MokoAlsaVolumeControl *control,
+			gint number);
+
+void moko_alsa_volume_control_set_device_from_name (
+			MokoAlsaVolumeControl *control,
+			const gchar *name);
+
+void moko_alsa_volume_control_set_element_from_name (
+			MokoAlsaVolumeControl *control,
+			const gchar *name);
+
+gdouble moko_alsa_volume_control_get_volume (MokoAlsaVolumeControl *control);
+
+void moko_alsa_volume_control_set_volume (MokoAlsaVolumeControl *control,
+					  gdouble volume);
+
+G_END_DECLS
+
+#endif /* _MOKO_ALSA_VOLUME_CONTROL */
+





More information about the commitlog mailing list