gnome-media r4019 - in trunk: . gnome-volume-control gnome-volume-control/src po



Author: mccann
Date: Tue Nov  4 01:04:11 2008
New Revision: 4019
URL: http://svn.gnome.org/viewvc/gnome-media?rev=4019&view=rev

Log:
2008-11-03  William Jon McCann  <jmccann redhat com>

	* Makefile.am:
	* configure.ac:
	Added gnome-volume-control sub-module.  Only
	built if pulseaudio is available.



Added:
   trunk/gnome-volume-control/
   trunk/gnome-volume-control/AUTHORS
   trunk/gnome-volume-control/ChangeLog
   trunk/gnome-volume-control/Makefile.am
   trunk/gnome-volume-control/src/
   trunk/gnome-volume-control/src/Makefile.am
   trunk/gnome-volume-control/src/applet-main.c
   trunk/gnome-volume-control/src/dialog-main.c
   trunk/gnome-volume-control/src/gvc-applet.c
   trunk/gnome-volume-control/src/gvc-applet.h
   trunk/gnome-volume-control/src/gvc-channel-bar.c
   trunk/gnome-volume-control/src/gvc-channel-bar.h
   trunk/gnome-volume-control/src/gvc-mixer-control.c
   trunk/gnome-volume-control/src/gvc-mixer-control.h
   trunk/gnome-volume-control/src/gvc-mixer-dialog.c
   trunk/gnome-volume-control/src/gvc-mixer-dialog.h
   trunk/gnome-volume-control/src/gvc-mixer-sink-input.c
   trunk/gnome-volume-control/src/gvc-mixer-sink-input.h
   trunk/gnome-volume-control/src/gvc-mixer-sink.c
   trunk/gnome-volume-control/src/gvc-mixer-sink.h
   trunk/gnome-volume-control/src/gvc-mixer-stream.c
   trunk/gnome-volume-control/src/gvc-mixer-stream.h
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.ac
   trunk/po/ChangeLog
   trunk/po/POTFILES.in

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Tue Nov  4 01:04:11 2008
@@ -1,3 +1,5 @@
+NULL =
+
 SUBDIRS = po
 
 if HAVE_GMP
@@ -20,6 +22,10 @@
 SUBDIRS += gnome-cd
 endif
 
+if HAVE_PULSEAUDIO
+SUBDIRS += gnome-volume-control
+endif
+
 if HAVE_GSTMIXER
 SUBDIRS += gst-mixer
 endif
@@ -31,21 +37,49 @@
 INTLTOOL_BUILT = \
         intltool-extract \
         intltool-merge \
-        intltool-update
-
-DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper --enable-cddbslave	\
---enable-vumeter --enable-gnomecd --enable-compile-warnings=maximum
-
-DIST_SUBDIRS = po profiles cddb-slave2 \
-	       vu-meter grecord gst-mixer gnome-cd \
-               gstreamer-properties
-
-EXTRA_DIST = autogen.sh configure COPYING COPYING-DOCS COPYING.grecord	\
-	COPYING.gst-mixer COPYING.profiles COPYING.vu-meter MAINTAINERS \
-	m4/as-compiler-flag.m4 m4/as-version.m4 m4/esd.m4		\
-	gnome-doc-utils.make $(INTLTOOL_BUILT:=.in)
+        intltool-update \
+	$(NULL)
 
-DISTCLEANFILES = po/.intltool-merge-cache \
-                 gnome-doc-utils.make $(INTLTOOL_BUILT)
+DISTCHECK_CONFIGURE_FLAGS = \
+	--disable-scrollkeeper	\
+	--enable-cddbslave	\
+	--enable-vumeter	\
+	--enable-gnomecd	\
+	--enable-compile-warnings=maximum
+
+DIST_SUBDIRS = \
+	po 			\
+	profiles		\
+	cddb-slave2		\
+	vu-meter		\
+	gnome-volume-control	\
+	grecord			\
+	gst-mixer		\
+	gnome-cd		\
+	gstreamer-properties	\
+	$(NULL)
+
+EXTRA_DIST = \
+	autogen.sh		\
+	configure		\
+	COPYING			\
+	COPYING-DOCS		\
+	COPYING.grecord		\
+	COPYING.gst-mixer	\
+	COPYING.profiles	\
+	COPYING.vu-meter	\
+	MAINTAINERS		\
+	m4/as-compiler-flag.m4	\
+	m4/as-version.m4	\
+	m4/esd.m4		\
+	gnome-doc-utils.make	\
+	$(INTLTOOL_BUILT:=.in)	\
+	$(NULL)
+
+DISTCLEANFILES = \
+	po/.intltool-merge-cache	\
+	gnome-doc-utils.make		\
+	$(INTLTOOL_BUILT)		\
+	$(NULL)
 
 ACLOCAL_AMFLAGS = -I m4

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Tue Nov  4 01:04:11 2008
@@ -4,7 +4,7 @@
         [2.25.1],
         [http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-media])
 
-AM_INIT_AUTOMAKE([-Wall -Wno-portability -Werror])
+AM_INIT_AUTOMAKE([1.9 no-dist-gzip dist-bzip2 -Wall -Wno-portability])
 
 AS_VERSION
 
@@ -59,11 +59,17 @@
 dnl Start of pkg-config checks - common libs
 dnl=======================================================================
 
+DBUS_GLIB_REQUIRED_VERSION=0.74
+GLIB_REQUIRED_VERSION=2.15.4
+GTK_REQUIRED_VERSION=2.10.0
+LIBGLADE_REQUIRED_VERSION=1.99.2
+GCONF_REQUIRED_VERSION=2.6.1
+PA_REQUIRED_VERSION=0.9.12
+
 PKG_CHECK_MODULES(MEDIA, [
-        glib-2.0 >= 1.3.7
-        gmodule-2.0 >= 1.3.7
-        libgnome-2.0 >= 2.13.7
-        libgnomeui-2.0 >= 2.13.2
+        glib-2.0 >= $GLIB_REQUIRED_VERSION
+        gmodule-2.0 >= $GLIB_REQUIRED_VERSION
+        gtk+-2.0 >= $GTK_REQUIRED_VERSION
 ])
 
 host=`uname -s`
@@ -77,6 +83,42 @@
 AC_SUBST(MEDIA_LIBS)
 
 dnl=======================================================================
+dnl Check for the new volume control modules
+dnl=======================================================================
+
+PKG_CHECK_MODULES(VOLUME_CONTROL,
+        dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
+        gobject-2.0 >= $GLIB_REQUIRED_VERSION
+        gtk+-2.0 >= $GTK_REQUIRED_VERSION
+)
+AC_SUBST(VOLUME_CONTROL_CFLAGS)
+AC_SUBST(VOLUME_CONTROL_LIBS)
+
+PKG_CHECK_MODULES(PULSEAUDIO,
+        libpulse >= $PA_REQUIRED_VERSION libpulse-mainloop-glib >= $PA_REQUIRED_VERSION,
+        have_pulseaudio=yes,
+        have_pulseaudio=no)
+
+AC_ARG_ENABLE([pulseaudio],
+              AS_HELP_STRING([--enable-pulseaudio],
+                             [Enable PulseAudio support @<:@default=auto@:>@]),
+              enable_pulseaudio=$enableval, enable_pulseaudio=auto)
+
+if test "x$enable_pulseaudio" != "xno"; then
+  if test "x$enable_pulseaudio" = "xyes" -a "x$have_pulseaudio" = "xno"; then
+    AC_MSG_ERROR([PulseAudio support explicitly requested but dependencies not found])
+  fi
+
+  if test "x$have_pulseaudio" = "xyes" ; then
+    AC_DEFINE(HAVE_PULSEAUDIO, [], [Define if we have pulseaudio])
+  fi
+fi
+AM_CONDITIONAL(HAVE_PULSEAUDIO, test x$have_pulseaudio = xyes)
+AC_SUBST(HAVE_PULSEAUDIO)
+AC_SUBST(PULSEAUDIO_CFLAGS)
+AC_SUBST(PULSEAUDIO_LIBS)
+
+dnl=======================================================================
 dnl Check for the CDDBSlave2 modules
 dnl=======================================================================
 
@@ -502,7 +544,7 @@
   AS_HELP_STRING([--enable-gstmix], [enable gstreamer mixer]),
   ,enable_gstmix=yes)
 
-if test "x$have_gst" = "xyes" && test "x$enable_gstmix" = "xyes";
+if test "x$have_pulseaudio" = "xno" && test "x$have_gst" = "xyes" && test "x$enable_gstmix" = "xyes";
 then
   PKG_CHECK_MODULES(GSTMIXER, [
     libgnomeui-2.0
@@ -529,7 +571,51 @@
 AM_CONDITIONAL(HAVE_GSTMIXER, [test x$have_gstmixer = xyes])
 
 
-CFLAGS="$CFLAGS $WARN_CFLAGS"
+dnl ---------------------------------------------------------------------------
+dnl - Finish
+dnl ---------------------------------------------------------------------------
+
+# Turn on the additional warnings last, so warnings don't affect other tests.
+AC_ARG_ENABLE(more-warnings,
+	[AC_HELP_STRING([--enable-more-warnings],
+	[Maximum compiler warnings])],
+	set_more_warnings="$enableval",[
+        	if test -d $srcdir/.svn; then
+        		set_more_warnings=yes
+              	else
+                  	set_more_warnings=no
+              	fi
+        ])
+AC_MSG_CHECKING(for more warnings)
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+        AC_MSG_RESULT(yes)
+        CFLAGS="\
+        -Wall \
+        -Wchar-subscripts -Wmissing-declarations -Wmissing-prototypes \
+        -Wnested-externs -Wpointer-arith \
+        -Wcast-align -Wsign-compare \
+        $CFLAGS"
+
+        for option in -Wno-unused-parameter -Wno-strict-aliasing -Wno-sign-compare; do
+                SAVE_CFLAGS="$CFLAGS"
+                CFLAGS="$CFLAGS $option"
+                AC_MSG_CHECKING([whether gcc understands $option])
+                AC_TRY_COMPILE([], [],
+                        has_option=yes,
+                        has_option=no,)
+                if test $has_option = no; then
+                        CFLAGS="$SAVE_CFLAGS"
+                fi
+                AC_MSG_RESULT($has_option)
+                unset has_option
+                unset SAVE_CFLAGS
+        done
+        unset option
+else
+        AC_MSG_RESULT(no)
+fi
+
+
 AC_SUBST(CFLAGS)
 AC_SUBST(CPPFLAGS)
 AC_SUBST(LIBS)
@@ -538,6 +624,8 @@
 AC_CONFIG_FILES([
 Makefile
 po/Makefile.in
+gnome-volume-control/Makefile
+gnome-volume-control/src/Makefile
 vu-meter/Makefile
 cddb-slave2/Makefile
 gnome-cd/Makefile
@@ -586,20 +674,24 @@
 AC_OUTPUT
 
 echo "
-        ${PACKAGE} ${VERSION} has been configured as follows:
+
+                    ${PACKAGE} ${VERSION}
+                    ============
+
 
 	Prefix:                 ${prefix}
         Source code location:   ${srcdir}
         Compiler:               ${CC}
         CFLAGS:                 ${CFLAGS}
 
-        GStreamer mixer:        $have_gstmixer
+        Volume Control          $have_pulseaudio
         GStreamer properties:   $have_gstprops
         GStreamer profiles:     $have_profiles
         Sound recorder:         $have_grecord$grecord_reason
 
   *Deprecated*:
 
+        GStreamer mixer:        $have_gstmixer
         vu-meter:               $have_vumeter$vumeter_reason
         cddb-slave:             $have_cddbslave
         gnome-cd:               $have_gnomecd$gnomecd_reason"

Added: trunk/gnome-volume-control/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/AUTHORS	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,2 @@
+William Jon McCann <mccann jhu edu>
+

Added: trunk/gnome-volume-control/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/Makefile.am	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,13 @@
+NULL =
+
+SUBDIRS =			\
+	src			\
+	$(NULL)
+
+EXTRA_DIST = \
+	ChangeLog		\
+	$(NULL)
+
+MAINTAINERCLEANFILES =		\
+	*~			\
+	$(NULL)

Added: trunk/gnome-volume-control/src/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/Makefile.am	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,68 @@
+NULL =
+
+bin_PROGRAMS = 					\
+	gnome-volume-control-applet		\
+	gnome-volume-control			\
+	$(NULL)
+
+INCLUDES =					\
+	$(WARN_CFLAGS)				\
+	$(VOLUME_CONTROL_CFLAGS)		\
+	$(PULSEAUDIO_CFLAGS)			\
+	-DLOCALE_DIR=\""$(datadir)/locale"\"	\
+	-DLIBEXECDIR=\"$(libexecdir)\"		\
+	-DGLADEDIR=\""$(pkgdatadir)"\"		\
+	$(NULL)
+
+gnome_volume_control_applet_LDADD =		\
+	$(VOLUME_CONTROL_LIBS)			\
+	$(PULSEAUDIO_LIBS)			\
+	$(NULL)
+
+gnome_volume_control_applet_SOURCES =		\
+	gvc-mixer-stream.h			\
+	gvc-mixer-stream.c			\
+	gvc-mixer-sink.h			\
+	gvc-mixer-sink.c			\
+	gvc-mixer-sink-input.h			\
+	gvc-mixer-sink-input.c			\
+	gvc-mixer-control.h			\
+	gvc-mixer-control.c			\
+	gvc-channel-bar.h			\
+	gvc-channel-bar.c			\
+	gvc-applet.h				\
+	gvc-applet.c				\
+	applet-main.c				\
+	$(NULL)
+
+gnome_volume_control_LDADD =			\
+	$(VOLUME_CONTROL_LIBS)			\
+	$(PULSEAUDIO_LIBS)			\
+	$(NULL)
+
+gnome_volume_control_SOURCES =			\
+	gvc-mixer-stream.h			\
+	gvc-mixer-stream.c			\
+	gvc-mixer-sink.h			\
+	gvc-mixer-sink.c			\
+	gvc-mixer-sink-input.h			\
+	gvc-mixer-sink-input.c			\
+	gvc-mixer-control.h			\
+	gvc-mixer-control.c			\
+	gvc-channel-bar.h			\
+	gvc-channel-bar.c			\
+	gvc-mixer-dialog.h			\
+	gvc-mixer-dialog.c			\
+	dialog-main.c				\
+	$(NULL)
+
+BUILT_SOURCES =				\
+	$(NULL)
+
+CLEANFILES =				\
+	$(BUILT_SOURCES)		\
+	$(NULL)
+
+MAINTAINERCLEANFILES =                  \
+        *~                              \
+        Makefile.in

Added: trunk/gnome-volume-control/src/applet-main.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/applet-main.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,190 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/goption.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gvc-applet.h"
+
+#define GVCA_DBUS_NAME "org.gnome.VolumeControlApplet"
+
+#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
+
+static gboolean show_version = FALSE;
+static gboolean debug = FALSE;
+
+static void
+on_bus_name_lost (DBusGProxy *bus_proxy,
+                  const char *name,
+                  gpointer    data)
+{
+        g_warning ("Lost name on bus: %s, exiting", name);
+        exit (1);
+}
+
+static gboolean
+acquire_name_on_proxy (DBusGProxy *bus_proxy,
+                       const char *name)
+{
+        GError     *error;
+        guint       result;
+        gboolean    res;
+        gboolean    ret;
+
+        ret = FALSE;
+
+        if (bus_proxy == NULL) {
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (bus_proxy,
+                                 "RequestName",
+                                 &error,
+                                 G_TYPE_STRING, name,
+                                 G_TYPE_UINT, 0,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_UINT, &result,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                if (error != NULL) {
+                        g_warning ("Failed to acquire %s: %s", name, error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to acquire %s", name);
+                }
+                goto out;
+        }
+
+        if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+                if (error != NULL) {
+                        g_warning ("Failed to acquire %s: %s", name, error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to acquire %s", name);
+                }
+                goto out;
+        }
+
+        /* register for name lost */
+        dbus_g_proxy_add_signal (bus_proxy,
+                                 "NameLost",
+                                 G_TYPE_STRING,
+                                 G_TYPE_INVALID);
+        dbus_g_proxy_connect_signal (bus_proxy,
+                                     "NameLost",
+                                     G_CALLBACK (on_bus_name_lost),
+                                     NULL,
+                                     NULL);
+
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
+static gboolean
+acquire_name (void)
+{
+        DBusGProxy      *bus_proxy;
+        GError          *error;
+        DBusGConnection *connection;
+
+        error = NULL;
+        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+        if (connection == NULL) {
+                g_warning ("Could not connect to session bus: %s",
+                           error->message);
+                exit (1);
+        }
+
+        bus_proxy = dbus_g_proxy_new_for_name (connection,
+                                               DBUS_SERVICE_DBUS,
+                                               DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS);
+
+        if (! acquire_name_on_proxy (bus_proxy, GVCA_DBUS_NAME) ) {
+                g_warning ("Could not acquire name on session bus");
+                exit (1);
+        }
+
+        g_object_unref (bus_proxy);
+
+        return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+        GError             *error;
+        GvcApplet          *applet;
+        static GOptionEntry entries[] = {
+                { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+                { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+                { NULL, 0, 0, 0, NULL, NULL, NULL }
+        };
+
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        error = NULL;
+        gtk_init_with_args (&argc, &argv,
+                            (char *) _(" - GNOME Volume Control Applet"),
+                            entries, GETTEXT_PACKAGE,
+                            &error);
+        if (error != NULL) {
+                g_warning ("%s", error->message);
+                exit (1);
+        }
+
+        if (show_version) {
+                g_print ("%s %s\n", argv [0], VERSION);
+                exit (1);
+        }
+
+        acquire_name ();
+
+        applet = gvc_applet_new ();
+        gvc_applet_start (applet);
+
+        gtk_main ();
+
+        if (applet != NULL) {
+                g_object_unref (applet);
+        }
+
+        return 0;
+}

Added: trunk/gnome-volume-control/src/dialog-main.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/dialog-main.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,217 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/goption.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gvc-mixer-dialog.h"
+
+#define GVCA_DBUS_NAME "org.gnome.VolumeControl"
+
+#define IS_STRING_EMPTY(x) ((x)==NULL||(x)[0]=='\0')
+
+static gboolean show_version = FALSE;
+static gboolean debug = FALSE;
+
+static void
+on_bus_name_lost (DBusGProxy *bus_proxy,
+                  const char *name,
+                  gpointer    data)
+{
+        g_warning ("Lost name on bus: %s, exiting", name);
+        exit (1);
+}
+
+static gboolean
+acquire_name_on_proxy (DBusGProxy *bus_proxy,
+                       const char *name)
+{
+        GError     *error;
+        guint       result;
+        gboolean    res;
+        gboolean    ret;
+
+        ret = FALSE;
+
+        if (bus_proxy == NULL) {
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (bus_proxy,
+                                 "RequestName",
+                                 &error,
+                                 G_TYPE_STRING, name,
+                                 G_TYPE_UINT, 0,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_UINT, &result,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                if (error != NULL) {
+                        g_warning ("Failed to acquire %s: %s", name, error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to acquire %s", name);
+                }
+                goto out;
+        }
+
+        if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+                if (error != NULL) {
+                        g_warning ("Failed to acquire %s: %s", name, error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to acquire %s", name);
+                }
+                goto out;
+        }
+
+        /* register for name lost */
+        dbus_g_proxy_add_signal (bus_proxy,
+                                 "NameLost",
+                                 G_TYPE_STRING,
+                                 G_TYPE_INVALID);
+        dbus_g_proxy_connect_signal (bus_proxy,
+                                     "NameLost",
+                                     G_CALLBACK (on_bus_name_lost),
+                                     NULL,
+                                     NULL);
+
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
+static gboolean
+acquire_name (void)
+{
+        DBusGProxy      *bus_proxy;
+        GError          *error;
+        DBusGConnection *connection;
+
+        error = NULL;
+        connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+        if (connection == NULL) {
+                g_warning ("Could not connect to session bus: %s",
+                           error->message);
+                exit (1);
+        }
+
+        bus_proxy = dbus_g_proxy_new_for_name (connection,
+                                               DBUS_SERVICE_DBUS,
+                                               DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS);
+
+        if (! acquire_name_on_proxy (bus_proxy, GVCA_DBUS_NAME) ) {
+                g_warning ("Could not acquire name on session bus");
+                exit (1);
+        }
+
+        g_object_unref (bus_proxy);
+
+        return TRUE;
+}
+
+static void
+on_dialog_response (GtkDialog *dialog,
+                    guint      response_id,
+                    gpointer   data)
+{
+        gtk_main_quit ();
+}
+
+static void
+on_control_ready (GvcMixerControl *control,
+                  gpointer         data)
+{
+        GvcMixerDialog *dialog;
+        dialog = gvc_mixer_dialog_new (control);
+        g_signal_connect (dialog,
+                          "response",
+                          G_CALLBACK (on_dialog_response),
+                          NULL);
+        gtk_widget_show (GTK_WIDGET (dialog));
+}
+
+int
+main (int argc, char **argv)
+{
+        GError             *error;
+        GvcMixerControl    *control;
+        static GOptionEntry entries[] = {
+                { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+                { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+                { NULL, 0, 0, 0, NULL, NULL, NULL }
+        };
+
+        bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+        textdomain (GETTEXT_PACKAGE);
+
+        error = NULL;
+        gtk_init_with_args (&argc, &argv,
+                            (char *) _(" - GNOME Volume Control"),
+                            entries, GETTEXT_PACKAGE,
+                            &error);
+        if (error != NULL) {
+                g_warning ("%s", error->message);
+                exit (1);
+        }
+
+        if (show_version) {
+                g_print ("%s %s\n", argv [0], VERSION);
+                exit (1);
+        }
+
+        acquire_name ();
+
+        control = gvc_mixer_control_new ();
+        g_signal_connect (control,
+                          "ready",
+                          G_CALLBACK (on_control_ready),
+                          control);
+        gvc_mixer_control_open (control);
+
+        /* FIXME: add timeout in case ready doesn't happen */
+
+        gtk_main ();
+
+        if (control != NULL) {
+                g_object_unref (control);
+        }
+
+        return 0;
+}

Added: trunk/gnome-volume-control/src/gvc-applet.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-applet.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,599 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gvc-applet.h"
+#include "gvc-channel-bar.h"
+#include "gvc-mixer-control.h"
+
+#define GVC_APPLET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_APPLET, GvcAppletPrivate))
+
+#define SCALE_SIZE 128
+
+static const char *icon_names[] = {
+        "audio-volume-muted",
+        "audio-volume-low",
+        "audio-volume-medium",
+        "audio-volume-high",
+        NULL
+};
+
+struct GvcAppletPrivate
+{
+        GtkStatusIcon   *status_icon;
+        GtkWidget       *dock;
+        GtkWidget       *bar;
+        GvcMixerControl *control;
+        GvcMixerStream  *sink_stream;
+        guint            current_icon;
+};
+
+static void     gvc_applet_class_init (GvcAppletClass *klass);
+static void     gvc_applet_init       (GvcApplet      *applet);
+static void     gvc_applet_finalize   (GObject        *object);
+
+G_DEFINE_TYPE (GvcApplet, gvc_applet, G_TYPE_OBJECT)
+
+static void
+maybe_show_status_icon (GvcApplet *applet)
+{
+        gboolean show;
+
+        show = TRUE;
+
+        gtk_status_icon_set_visible (applet->priv->status_icon, show);
+}
+
+void
+gvc_applet_start (GvcApplet *applet)
+{
+        g_return_if_fail (GVC_IS_APPLET (applet));
+
+        maybe_show_status_icon (applet);
+}
+
+static void
+gvc_applet_dispose (GObject *object)
+{
+        GvcApplet *applet = GVC_APPLET (object);
+
+        if (applet->priv->dock != NULL) {
+                gtk_widget_destroy (applet->priv->dock);
+                applet->priv->dock = NULL;
+        }
+
+        G_OBJECT_CLASS (gvc_applet_parent_class)->dispose (object);
+}
+
+static GObject *
+gvc_applet_constructor (GType                  type,
+                        guint                  n_construct_properties,
+                        GObjectConstructParam *construct_params)
+{
+        GObject   *object;
+        GvcApplet *self;
+
+        object = G_OBJECT_CLASS (gvc_applet_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_APPLET (object);
+
+        gvc_mixer_control_open (self->priv->control);
+
+        return object;
+}
+
+static void
+gvc_applet_class_init (GvcAppletClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gvc_applet_finalize;
+        object_class->dispose = gvc_applet_dispose;
+        object_class->constructor = gvc_applet_constructor;
+
+        g_type_class_add_private (klass, sizeof (GvcAppletPrivate));
+}
+
+static void
+on_adjustment_value_changed (GtkAdjustment *adjustment,
+                             GvcApplet     *applet)
+{
+        gdouble volume;
+
+        volume = gtk_adjustment_get_value (adjustment);
+        gvc_mixer_stream_change_volume (applet->priv->sink_stream,
+                                        (guint)volume);
+}
+
+static gboolean
+popup_dock (GvcApplet *applet,
+            guint      time)
+{
+        GtkAdjustment *adj;
+        GdkRectangle   area;
+        GtkOrientation orientation;
+        GdkDisplay    *display;
+        GdkScreen     *screen;
+        gboolean       res;
+        int            x, y;
+
+        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (applet->priv->bar)));
+        gtk_adjustment_set_value (adj,
+                                  gvc_mixer_stream_get_volume (applet->priv->sink_stream));
+
+        screen = gtk_status_icon_get_screen (applet->priv->status_icon);
+        res = gtk_status_icon_get_geometry (applet->priv->status_icon,
+                                            &screen,
+                                            &area,
+                                            &orientation);
+        if (! res) {
+                g_warning ("Unable to determine geometry of status icon");
+                return FALSE;
+        }
+
+        /* position roughly */
+        gtk_window_set_screen (GTK_WINDOW (applet->priv->dock), screen);
+        x = area.x + area.width;
+        y = area.y + area.height;
+
+        if (orientation == GTK_ORIENTATION_VERTICAL) {
+                gtk_window_move (GTK_WINDOW (applet->priv->dock), x, area.y);
+        } else {
+                gtk_window_move (GTK_WINDOW (applet->priv->dock), area.x, y);
+        }
+
+        /* FIXME: without this, the popup window appears as a square
+         * after changing the orientation
+         */
+        gtk_window_resize (GTK_WINDOW (applet->priv->dock), 1, 1);
+
+        gtk_widget_show_all (applet->priv->dock);
+
+
+        /* grab focus */
+        gtk_grab_add (applet->priv->dock);
+
+        if (gdk_pointer_grab (applet->priv->dock->window, TRUE,
+                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                              GDK_POINTER_MOTION_MASK, NULL, NULL,
+                              time)
+            != GDK_GRAB_SUCCESS) {
+                gtk_grab_remove (applet->priv->dock);
+                gtk_widget_hide (applet->priv->dock);
+                return FALSE;
+        }
+
+        if (gdk_keyboard_grab (applet->priv->dock->window, TRUE, time) != GDK_GRAB_SUCCESS) {
+                display = gtk_widget_get_display (applet->priv->dock);
+                gdk_display_pointer_ungrab (display, time);
+                gtk_grab_remove (applet->priv->dock);
+                gtk_widget_hide (applet->priv->dock);
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (applet->priv->dock);
+
+        return TRUE;
+}
+
+static void
+on_status_icon_activate (GtkStatusIcon *status_icon,
+                         GvcApplet     *applet)
+{
+        popup_dock (applet, GDK_CURRENT_TIME);
+}
+
+static void
+on_menu_activate_open_volume_control (GtkMenuItem *item,
+                                      GvcApplet   *applet)
+{
+        GError *error;
+
+        error = NULL;
+        gdk_spawn_command_line_on_screen (gtk_widget_get_screen (applet->priv->dock),
+                                          "gnome-volume-control",
+                                          &error);
+
+        if (error != NULL) {
+                GtkWidget *dialog;
+
+                dialog = gtk_message_dialog_new (NULL,
+                                                 0,
+                                                 GTK_MESSAGE_ERROR,
+                                                 GTK_BUTTONS_CLOSE,
+                                                 _("Failed to start Volume Control: %s"),
+                                                 error->message);
+                g_signal_connect (dialog,
+                                  "response",
+                                  G_CALLBACK (gtk_widget_destroy),
+                                  NULL);
+                gtk_widget_show (dialog);
+                g_error_free (error);
+        }
+}
+
+static void
+on_status_icon_popup_menu (GtkStatusIcon *status_icon,
+                           guint          button,
+                           guint          activate_time,
+                           GvcApplet     *applet)
+{
+        GtkWidget *menu;
+        GtkWidget *item;
+        GtkWidget *image;
+
+        menu = gtk_menu_new ();
+        item = gtk_image_menu_item_new_with_mnemonic (_("_Open Volume Control"));
+        image = gtk_image_new_from_icon_name ("multimedia-volume-control",
+                                              GTK_ICON_SIZE_MENU);
+        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+        g_signal_connect (item,
+                          "activate",
+                          G_CALLBACK (on_menu_activate_open_volume_control),
+                          applet);
+        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+        gtk_widget_show_all (menu);
+        gtk_menu_popup (GTK_MENU (menu),
+                        NULL,
+                        NULL,
+                        gtk_status_icon_position_menu,
+                        status_icon,
+                        button,
+                        activate_time);
+}
+
+static gboolean
+on_status_icon_scroll_event (GtkStatusIcon  *status_icon,
+                             GdkEventScroll *event,
+                             GvcApplet      *applet)
+{
+        GtkAdjustment *adj;
+
+        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (applet->priv->bar)));
+
+        switch (event->direction) {
+        case GDK_SCROLL_UP:
+        case GDK_SCROLL_DOWN: {
+                gdouble volume;
+
+                volume = gtk_adjustment_get_value (adj);
+
+                if (event->direction == GDK_SCROLL_UP) {
+                        volume += adj->step_increment;
+                        if (volume > adj->upper) {
+                                volume = adj->upper;
+                        }
+                } else {
+                        volume -= adj->step_increment;
+                        if (volume < adj->lower) {
+                                volume = adj->lower;
+                        }
+                }
+
+                gtk_adjustment_set_value (adj, volume);
+                return TRUE;
+        }
+        default:
+                break;
+        }
+
+        return FALSE;
+}
+
+static void
+gvc_applet_release_grab (GvcApplet      *applet,
+                         GdkEventButton *event)
+{
+        GdkDisplay     *display;
+
+        /* ungrab focus */
+        display = gtk_widget_get_display (GTK_WIDGET (applet->priv->dock));
+        gdk_display_keyboard_ungrab (display, event->time);
+        gdk_display_pointer_ungrab (display, event->time);
+        gtk_grab_remove (applet->priv->dock);
+
+        /* hide again */
+        gtk_widget_hide (applet->priv->dock);
+}
+
+static gboolean
+on_dock_button_press (GtkWidget      *widget,
+                      GdkEventButton *event,
+                      GvcApplet      *applet)
+{
+        if (event->type == GDK_BUTTON_PRESS) {
+                gvc_applet_release_grab (applet, event);
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+popdown_dock (GvcApplet *applet)
+{
+        GdkDisplay *display;
+
+        /* ungrab focus */
+        display = gtk_widget_get_display (applet->priv->dock);
+        gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+        gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+        gtk_grab_remove (applet->priv->dock);
+
+        /* hide again */
+        gtk_widget_hide (applet->priv->dock);
+}
+
+/* This is called when the grab is broken for
+ * either the dock, or the scale itself */
+static void
+gvc_applet_grab_notify (GvcApplet *applet,
+                        gboolean   was_grabbed)
+{
+        if (was_grabbed != FALSE) {
+                return;
+        }
+
+        if (!GTK_WIDGET_HAS_GRAB (applet->priv->dock)) {
+                return;
+        }
+
+        if (gtk_widget_is_ancestor (gtk_grab_get_current (), applet->priv->dock)) {
+                return;
+        }
+
+        popdown_dock (applet);
+}
+
+static void
+on_dock_grab_notify (GtkWidget *widget,
+                     gboolean   was_grabbed,
+                     GvcApplet *applet)
+{
+        gvc_applet_grab_notify (applet, was_grabbed);
+}
+
+static gboolean
+on_dock_grab_broken_event (GtkWidget *widget,
+                           gboolean   was_grabbed,
+                           GvcApplet *applet)
+{
+        gvc_applet_grab_notify (applet, FALSE);
+
+        return FALSE;
+}
+
+static gboolean
+on_dock_key_release (GtkWidget   *widget,
+                     GdkEventKey *event,
+                     GvcApplet   *applet)
+{
+        if (event->keyval == GDK_Escape) {
+                popdown_dock (applet);
+                return TRUE;
+        }
+
+#if 0
+        if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event)) {
+                /* The popup hasn't managed the event, pass onto the button */
+                gtk_bindings_activate_event (GTK_OBJECT (user_data), event);
+        }
+#endif
+        return TRUE;
+}
+
+static void
+update_icon (GvcApplet *applet)
+{
+        guint    volume;
+        gboolean is_muted;
+        guint    n;
+
+        volume = gvc_mixer_stream_get_volume (applet->priv->sink_stream);
+        is_muted = gvc_mixer_stream_get_is_muted (applet->priv->sink_stream);
+
+        /* select image */
+        if (volume <= 0 || is_muted) {
+                n = 0;
+        } else {
+                n = 3 * volume / PA_VOLUME_NORM + 1;
+                if (n < 1) {
+                        n = 1;
+                } else if (n > 3) {
+                        n = 3;
+                }
+        }
+
+        /* apparently status icon will reset icon even if
+         * if doesn't change */
+        if (applet->priv->current_icon != n) {
+                gtk_status_icon_set_from_icon_name (GTK_STATUS_ICON (applet->priv->status_icon), icon_names [n]);
+                applet->priv->current_icon = n;
+        }
+}
+
+static void
+on_stream_volume_notify (GObject    *object,
+                         GParamSpec *pspec,
+                         GvcApplet  *applet)
+{
+        update_icon (applet);
+        /* FIXME: update dock too */
+}
+
+static void
+on_stream_is_muted_notify (GObject    *object,
+                           GParamSpec *pspec,
+                           GvcApplet  *applet)
+{
+        update_icon (applet);
+        /* FIXME: update dock too */
+}
+
+static void
+on_control_ready (GvcMixerControl *control,
+                  GvcApplet       *applet)
+{
+        applet->priv->sink_stream = gvc_mixer_control_get_default_sink (control);
+        if (applet->priv->sink_stream != NULL) {
+                GtkAdjustment *adj;
+
+                adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (applet->priv->bar)));
+                gtk_adjustment_set_value (adj,
+                                          gvc_mixer_stream_get_volume (applet->priv->sink_stream));
+
+                g_signal_connect (applet->priv->sink_stream,
+                                  "notify::volume",
+                                  G_CALLBACK (on_stream_volume_notify),
+                                  applet);
+                g_signal_connect (applet->priv->sink_stream,
+                                  "notify::is-muted",
+                                  G_CALLBACK (on_stream_is_muted_notify),
+                                  applet);
+                update_icon (applet);
+        } else {
+                g_warning ("Unable to get default sink");
+        }
+}
+
+static void
+on_bar_is_muted_notify (GObject    *object,
+                        GParamSpec *pspec,
+                        GvcApplet  *applet)
+{
+        gboolean is_muted;
+
+        is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
+        gvc_mixer_stream_change_is_muted (applet->priv->sink_stream,
+                                          is_muted);
+}
+
+static void
+gvc_applet_init (GvcApplet *applet)
+{
+        GtkWidget *frame;
+        GtkWidget *box;
+        GtkAdjustment *adj;
+
+        applet->priv = GVC_APPLET_GET_PRIVATE (applet);
+
+        applet->priv->status_icon = gtk_status_icon_new_from_icon_name (icon_names[0]);
+        g_signal_connect (applet->priv->status_icon,
+                          "activate",
+                          G_CALLBACK (on_status_icon_activate),
+                          applet);
+        g_signal_connect (applet->priv->status_icon,
+                          "popup-menu",
+                          G_CALLBACK (on_status_icon_popup_menu),
+                          applet);
+        g_signal_connect (applet->priv->status_icon,
+                          "scroll-event",
+                          G_CALLBACK (on_status_icon_scroll_event),
+                          applet);
+
+
+        /* window */
+        applet->priv->dock = gtk_window_new (GTK_WINDOW_POPUP);
+        gtk_widget_set_name (applet->priv->dock, "gvc-applet-popup-window");
+        g_signal_connect (applet->priv->dock,
+                          "button-press-event",
+                          G_CALLBACK (on_dock_button_press),
+                          applet);
+        g_signal_connect (applet->priv->dock,
+                          "key-release-event",
+                          G_CALLBACK (on_dock_key_release),
+                          applet);
+        g_signal_connect (applet->priv->dock,
+                          "grab-notify",
+                          G_CALLBACK (on_dock_grab_notify),
+                          applet);
+        g_signal_connect (applet->priv->dock,
+                          "grab-broken-event",
+                          G_CALLBACK (on_dock_grab_broken_event),
+                          applet);
+
+        gtk_window_set_decorated (GTK_WINDOW (applet->priv->dock), FALSE);
+
+        frame = gtk_frame_new (NULL);
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+        gtk_container_add (GTK_CONTAINER (applet->priv->dock), frame);
+        gtk_widget_show (frame);
+
+        box = gtk_vbox_new (FALSE, 6);
+        gtk_container_set_border_width (GTK_CONTAINER (box), 6);
+        gtk_container_add (GTK_CONTAINER (frame), box);
+
+        applet->priv->bar = gvc_channel_bar_new ();
+        gtk_box_pack_start (GTK_BOX (box), applet->priv->bar, TRUE, FALSE, 0);
+        g_signal_connect (applet->priv->bar,
+                          "notify::is-muted",
+                          G_CALLBACK (on_bar_is_muted_notify),
+                          applet);
+
+        applet->priv->control = gvc_mixer_control_new ();
+        g_signal_connect (applet->priv->control,
+                          "ready",
+                          G_CALLBACK (on_control_ready),
+                          applet);
+
+        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (applet->priv->bar)));
+        g_signal_connect (adj,
+                          "value-changed",
+                          G_CALLBACK (on_adjustment_value_changed),
+                          applet);
+}
+
+static void
+gvc_applet_finalize (GObject *object)
+{
+        GvcApplet *applet;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_APPLET (object));
+
+        applet = GVC_APPLET (object);
+
+        g_return_if_fail (applet->priv != NULL);
+        G_OBJECT_CLASS (gvc_applet_parent_class)->finalize (object);
+}
+
+GvcApplet *
+gvc_applet_new (void)
+{
+        GObject *applet;
+
+        applet = g_object_new (GVC_TYPE_APPLET, NULL);
+
+        return GVC_APPLET (applet);
+}

Added: trunk/gnome-volume-control/src/gvc-applet.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-applet.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_APPLET_H
+#define __GVC_APPLET_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_APPLET         (gvc_applet_get_type ())
+#define GVC_APPLET(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_APPLET, GvcApplet))
+#define GVC_APPLET_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_APPLET, GvcAppletClass))
+#define GVC_IS_APPLET(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_APPLET))
+#define GVC_IS_APPLET_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_APPLET))
+#define GVC_APPLET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_APPLET, GvcAppletClass))
+
+typedef struct GvcAppletPrivate GvcAppletPrivate;
+
+typedef struct
+{
+        GObject            parent;
+        GvcAppletPrivate *priv;
+} GvcApplet;
+
+typedef struct
+{
+        GObjectClass   parent_class;
+} GvcAppletClass;
+
+GType               gvc_applet_get_type            (void);
+
+GvcApplet *         gvc_applet_new                 (void);
+void                gvc_applet_start               (GvcApplet     *applet);
+
+G_END_DECLS
+
+#endif /* __GVC_APPLET_H */

Added: trunk/gnome-volume-control/src/gvc-channel-bar.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-channel-bar.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,448 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gvc-channel-bar.h"
+
+#define SCALE_SIZE 128
+
+#define GVC_CHANNEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarPrivate))
+
+struct GvcChannelBarPrivate
+{
+        GtkOrientation orientation;
+        GtkWidget     *image;
+        GtkWidget     *label;
+        GtkWidget     *scale;
+        GtkWidget     *mute_button;
+        GtkAdjustment *adjustment;
+        gboolean       is_muted;
+        char          *name;
+        char          *icon_name;
+};
+
+enum
+{
+        PROP_0,
+        PROP_ORIENTATION,
+        PROP_IS_MUTED,
+        PROP_ADJUSTMENT,
+        PROP_NAME,
+        PROP_ICON_NAME,
+};
+
+static void     gvc_channel_bar_class_init (GvcChannelBarClass *klass);
+static void     gvc_channel_bar_init       (GvcChannelBar      *channel_bar);
+static void     gvc_channel_bar_finalize   (GObject            *object);
+
+G_DEFINE_TYPE (GvcChannelBar, gvc_channel_bar, GTK_TYPE_HBOX)
+
+static GtkWidget *
+_scale_box_new (GvcChannelBar *bar)
+{
+        GvcChannelBarPrivate *priv = bar->priv;
+        GtkWidget            *box;
+        GtkWidget            *alignment;
+
+        if (priv->orientation == GTK_ORIENTATION_VERTICAL) {
+                box = gtk_vbox_new (FALSE, 6);
+
+                priv->scale = gtk_vscale_new (priv->adjustment);
+
+                gtk_widget_set_size_request (priv->scale, -1, SCALE_SIZE);
+                gtk_range_set_inverted (GTK_RANGE (priv->scale), TRUE);
+
+                gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
+                gtk_box_pack_start (GTK_BOX (box), priv->label, FALSE, FALSE, 0);
+
+                gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0);
+
+                alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
+                gtk_box_pack_start (GTK_BOX (box), alignment, FALSE, FALSE, 0);
+                gtk_container_add (GTK_CONTAINER (alignment), priv->mute_button);
+
+        } else {
+                box = gtk_hbox_new (FALSE, 6);
+
+                priv->scale = gtk_hscale_new (priv->adjustment);
+
+                gtk_widget_set_size_request (priv->scale, SCALE_SIZE, -1);
+
+                gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
+                gtk_box_pack_start (GTK_BOX (box), priv->label, FALSE, FALSE, 0);
+
+                gtk_box_pack_start (GTK_BOX (box), priv->scale, TRUE, TRUE, 0);
+
+                alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
+                gtk_box_pack_start (GTK_BOX (box), alignment, FALSE, FALSE, 0);
+                gtk_container_add (GTK_CONTAINER (alignment), priv->mute_button);
+
+        }
+
+        gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE);
+
+        return box;
+}
+
+static void
+update_image (GvcChannelBar *bar)
+{
+        gtk_image_set_from_icon_name (GTK_IMAGE (bar->priv->image),
+                                      bar->priv->icon_name,
+                                      GTK_ICON_SIZE_DIALOG);
+
+        if (bar->priv->icon_name != NULL) {
+                gtk_widget_show (bar->priv->image);
+        } else {
+                gtk_widget_hide (bar->priv->image);
+        }
+}
+
+static void
+update_label (GvcChannelBar *bar)
+{
+        gtk_label_set_text (GTK_LABEL (bar->priv->label),
+                            bar->priv->name);
+
+        if (bar->priv->name != NULL) {
+                gtk_widget_show (bar->priv->label);
+        } else {
+                gtk_widget_hide (bar->priv->label);
+        }
+}
+
+void
+gvc_channel_bar_set_name (GvcChannelBar  *bar,
+                          const char     *name)
+{
+        g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
+
+        g_free (bar->priv->name);
+        bar->priv->name = g_strdup (name);
+        update_label (bar);
+        g_object_notify (G_OBJECT (bar), "name");
+}
+
+void
+gvc_channel_bar_set_icon_name (GvcChannelBar  *bar,
+                               const char     *name)
+{
+        g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
+
+        g_free (bar->priv->icon_name);
+        bar->priv->icon_name = g_strdup (name);
+        update_image (bar);
+        g_object_notify (G_OBJECT (bar), "icon-name");
+}
+
+void
+gvc_channel_bar_set_orientation (GvcChannelBar  *bar,
+                                 GtkOrientation  orientation)
+{
+        g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
+
+        if (orientation != bar->priv->orientation) {
+                bar->priv->orientation = orientation;
+
+                if (bar->priv->scale != NULL) {
+                        GtkWidget *box = bar->priv->scale->parent;
+                        GtkWidget *frame = box->parent;
+
+                        g_object_ref (bar->priv->mute_button);
+
+                        gtk_container_remove (GTK_CONTAINER (box), bar->priv->mute_button);
+                        gtk_container_remove (GTK_CONTAINER (box), bar->priv->scale);
+                        gtk_container_remove (GTK_CONTAINER (frame), box);
+
+                        box = _scale_box_new (bar);
+                        gtk_container_add (GTK_CONTAINER (frame), box);
+
+                        g_object_unref (bar->priv->mute_button);
+                }
+
+                g_object_notify (G_OBJECT (bar), "orientation");
+        }
+}
+
+static void
+gvc_channel_bar_set_adjustment (GvcChannelBar *bar,
+                                GtkAdjustment *adjustment)
+{
+        g_return_if_fail (GVC_CHANNEL_BAR (bar));
+        g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+        if (bar->priv->adjustment != NULL) {
+                g_object_unref (bar->priv->adjustment);
+        }
+        bar->priv->adjustment = g_object_ref_sink (adjustment);
+
+        if (bar->priv->scale != NULL) {
+                gtk_range_set_adjustment (GTK_RANGE (bar->priv->scale), adjustment);
+        }
+
+        g_object_notify (G_OBJECT (bar), "adjustment");
+}
+
+GtkAdjustment *
+gvc_channel_bar_get_adjustment (GvcChannelBar *bar)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), NULL);
+
+        return bar->priv->adjustment;
+}
+
+static void
+update_mute_button (GvcChannelBar *bar)
+{
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar->priv->mute_button),
+                                      bar->priv->is_muted);
+}
+
+void
+gvc_channel_bar_set_is_muted (GvcChannelBar *bar,
+                              gboolean       is_muted)
+{
+        g_return_if_fail (GVC_IS_CHANNEL_BAR (bar));
+
+        if (is_muted != bar->priv->is_muted) {
+                bar->priv->is_muted = is_muted;
+                g_object_notify (G_OBJECT (bar), "is-muted");
+                update_mute_button (bar);
+        }
+}
+
+gboolean
+gvc_channel_bar_get_is_muted  (GvcChannelBar *bar)
+{
+        g_return_val_if_fail (GVC_IS_CHANNEL_BAR (bar), FALSE);
+        return bar->priv->is_muted;
+}
+
+static void
+gvc_channel_bar_set_property (GObject       *object,
+                              guint          prop_id,
+                              const GValue  *value,
+                              GParamSpec    *pspec)
+{
+        GvcChannelBar *self = GVC_CHANNEL_BAR (object);
+
+        switch (prop_id) {
+        case PROP_ORIENTATION:
+                gvc_channel_bar_set_orientation (self, g_value_get_enum (value));
+                break;
+        case PROP_IS_MUTED:
+                gvc_channel_bar_set_is_muted (self, g_value_get_boolean (value));
+                break;
+        case PROP_NAME:
+                gvc_channel_bar_set_name (self, g_value_get_string (value));
+                break;
+        case PROP_ICON_NAME:
+                gvc_channel_bar_set_icon_name (self, g_value_get_string (value));
+                break;
+        case PROP_ADJUSTMENT:
+                gvc_channel_bar_set_adjustment (self, g_value_get_object (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gvc_channel_bar_get_property (GObject     *object,
+                              guint        prop_id,
+                              GValue      *value,
+                              GParamSpec  *pspec)
+{
+        GvcChannelBar *self = GVC_CHANNEL_BAR (object);
+        GvcChannelBarPrivate *priv = self->priv;
+
+        switch (prop_id) {
+        case PROP_ORIENTATION:
+                g_value_set_enum (value, priv->orientation);
+                break;
+        case PROP_IS_MUTED:
+                g_value_set_boolean (value, priv->is_muted);
+                break;
+        case PROP_NAME:
+                g_value_set_string (value, priv->name);
+                break;
+        case PROP_ICON_NAME:
+                g_value_set_string (value, priv->icon_name);
+                break;
+        case PROP_ADJUSTMENT:
+                g_value_set_object (value, gvc_channel_bar_get_adjustment (self));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static GObject *
+gvc_channel_bar_constructor (GType                  type,
+                             guint                  n_construct_properties,
+                             GObjectConstructParam *construct_params)
+{
+        GObject       *object;
+        GvcChannelBar *self;
+        GtkWidget     *frame;
+        GtkWidget     *box;
+
+        object = G_OBJECT_CLASS (gvc_channel_bar_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_CHANNEL_BAR (object);
+
+        /* frame */
+        frame = gtk_frame_new (NULL);
+        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+        gtk_container_add (GTK_CONTAINER (self), frame);
+
+        /* box with scale and +/- buttons */
+        box = _scale_box_new (self);
+        gtk_container_add (GTK_CONTAINER (frame), box);
+
+        gtk_widget_show_all (frame);
+
+        return object;
+}
+
+static void
+gvc_channel_bar_class_init (GvcChannelBarClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gvc_channel_bar_constructor;
+        object_class->finalize = gvc_channel_bar_finalize;
+        object_class->set_property = gvc_channel_bar_set_property;
+        object_class->get_property = gvc_channel_bar_get_property;
+
+        g_object_class_install_property (object_class,
+                                         PROP_ORIENTATION,
+                                         g_param_spec_enum ("orientation",
+                                                            "Orientation",
+                                                            "The orientation of the scale",
+                                                            GTK_TYPE_ORIENTATION,
+                                                            GTK_ORIENTATION_VERTICAL,
+                                                            G_PARAM_READWRITE));
+        g_object_class_install_property (object_class,
+                                         PROP_IS_MUTED,
+                                         g_param_spec_boolean ("is-muted",
+                                                               "is muted",
+                                                               "Whether stream is muted",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (object_class,
+                                         PROP_ADJUSTMENT,
+                                         g_param_spec_object ("adjustment",
+                                                              "Adjustment",
+                                                              "The GtkAdjustment that contains the current value of this scale button object",
+                                                              GTK_TYPE_ADJUSTMENT,
+                                                              G_PARAM_READWRITE));
+        g_object_class_install_property (object_class,
+                                         PROP_NAME,
+                                         g_param_spec_string ("name",
+                                                              "Name",
+                                                              "Name to display for this stream",
+                                                              NULL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (object_class,
+                                         PROP_ICON_NAME,
+                                         g_param_spec_string ("icon-name",
+                                                              "Icon Name",
+                                                              "Name of icon to display for this stream",
+                                                              NULL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+        g_type_class_add_private (klass, sizeof (GvcChannelBarPrivate));
+}
+
+static void
+on_mute_button_toggled (GtkToggleButton *button,
+                        GvcChannelBar   *bar)
+{
+        gboolean is_muted;
+        is_muted = gtk_toggle_button_get_active (button);
+        gvc_channel_bar_set_is_muted (bar, is_muted);
+}
+
+static void
+gvc_channel_bar_init (GvcChannelBar *bar)
+{
+        GtkWidget *image;
+
+        bar->priv = GVC_CHANNEL_BAR_GET_PRIVATE (bar);
+
+        bar->priv->orientation = GTK_ORIENTATION_VERTICAL;
+        bar->priv->adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
+                                                                    0.0,
+                                                                    65536.0,
+                                                                    65536.0/100.0,
+                                                                    65536.0/10.0,
+                                                                    0.0));
+        g_object_ref_sink (bar->priv->adjustment);
+
+        bar->priv->mute_button = gtk_toggle_button_new ();
+        image = gtk_image_new_from_icon_name ("audio-volume-muted", GTK_ICON_SIZE_MENU);
+        gtk_button_set_image (GTK_BUTTON (bar->priv->mute_button), image);
+        update_mute_button (bar);
+        g_signal_connect (bar->priv->mute_button,
+                          "toggled",
+                          G_CALLBACK (on_mute_button_toggled),
+                          bar);
+
+        bar->priv->image = gtk_image_new ();
+        gtk_widget_set_no_show_all (bar->priv->image, TRUE);
+
+        bar->priv->label = gtk_label_new (NULL);
+        gtk_widget_set_no_show_all (bar->priv->label, TRUE);
+}
+
+static void
+gvc_channel_bar_finalize (GObject *object)
+{
+        GvcChannelBar *channel_bar;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_CHANNEL_BAR (object));
+
+        channel_bar = GVC_CHANNEL_BAR (object);
+
+        g_return_if_fail (channel_bar->priv != NULL);
+        G_OBJECT_CLASS (gvc_channel_bar_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gvc_channel_bar_new (void)
+{
+        GObject *bar;
+        bar = g_object_new (GVC_TYPE_CHANNEL_BAR, NULL);
+        return GTK_WIDGET (bar);
+}

Added: trunk/gnome-volume-control/src/gvc-channel-bar.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-channel-bar.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_CHANNEL_BAR_H
+#define __GVC_CHANNEL_BAR_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_CHANNEL_BAR         (gvc_channel_bar_get_type ())
+#define GVC_CHANNEL_BAR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBar))
+#define GVC_CHANNEL_BAR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass))
+#define GVC_IS_CHANNEL_BAR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_BAR))
+#define GVC_IS_CHANNEL_BAR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_BAR))
+#define GVC_CHANNEL_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_BAR, GvcChannelBarClass))
+
+typedef struct GvcChannelBarPrivate GvcChannelBarPrivate;
+
+typedef struct
+{
+        GtkHBox               parent;
+        GvcChannelBarPrivate *priv;
+} GvcChannelBar;
+
+typedef struct
+{
+        GtkHBoxClass          parent_class;
+} GvcChannelBarClass;
+
+GType               gvc_channel_bar_get_type            (void);
+
+GtkWidget *         gvc_channel_bar_new                 (void);
+
+void                gvc_channel_bar_set_name            (GvcChannelBar *bar,
+                                                         const char    *name);
+void                gvc_channel_bar_set_icon_name       (GvcChannelBar *bar,
+                                                         const char    *icon_name);
+
+void                gvc_channel_bar_set_orientation     (GvcChannelBar *bar,
+                                                         GtkOrientation orientation);
+GtkOrientation      gvc_channel_bar_get_orientation     (GvcChannelBar *bar);
+
+GtkAdjustment *     gvc_channel_bar_get_adjustment      (GvcChannelBar *bar);
+
+gboolean            gvc_channel_bar_get_is_muted        (GvcChannelBar *bar);
+void                gvc_channel_bar_set_is_muted        (GvcChannelBar *bar,
+                                                         gboolean       is_muted);
+
+G_END_DECLS
+
+#endif /* __GVC_CHANNEL_BAR_H */

Added: trunk/gnome-volume-control/src/gvc-mixer-control.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-control.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,1070 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006-2008 Lennart Poettering
+ * Copyright (C) 2008 Sjoerd Simons <sjoerd luon net>
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/glib-mainloop.h>
+#include <pulse/ext-stream-restore.h>
+
+#include "gvc-mixer-control.h"
+#include "gvc-mixer-sink.h"
+#include "gvc-mixer-sink-input.h"
+
+#define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate))
+
+struct GvcMixerControlPrivate
+{
+        pa_glib_mainloop *pa_mainloop;
+        pa_mainloop_api  *pa_api;
+        pa_context       *pa_context;
+        int               n_outstanding;
+
+        char             *default_sink_name;
+        guint             default_sink_index;
+        char             *default_source_name;
+        guint             default_source_index;
+
+        GvcMixerStream   *event_sink_input;
+
+        GHashTable       *sinks; /* fixed outputs */
+        GHashTable       *sources; /* fixed inputs */
+        GHashTable       *sink_inputs; /* routable output streams */
+        GHashTable       *source_outputs; /* routable input streams */
+        GHashTable       *clients;
+};
+
+enum {
+        READY,
+        STREAM_ADDED,
+        STREAM_REMOVED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void     gvc_mixer_control_class_init (GvcMixerControlClass *klass);
+static void     gvc_mixer_control_init       (GvcMixerControl      *mixer_control);
+static void     gvc_mixer_control_finalize   (GObject              *object);
+
+G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT)
+
+GvcMixerStream *
+gvc_mixer_control_get_event_sink_input (GvcMixerControl *control)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+        return control->priv->event_sink_input;
+}
+
+GvcMixerStream *
+gvc_mixer_control_get_default_sink (GvcMixerControl *control)
+{
+        GvcMixerStream *stream;
+
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+        stream = g_hash_table_lookup (control->priv->sinks,
+                                      GUINT_TO_POINTER (control->priv->default_sink_index));
+        return stream;
+}
+
+static void
+listify_hash_values_hfunc (gpointer key,
+                           gpointer value,
+                           gpointer user_data)
+{
+        GSList **list = user_data;
+
+        *list = g_slist_prepend (*list, value);
+}
+
+static int
+gvc_stream_collate (GvcMixerStream *a,
+                    GvcMixerStream *b)
+{
+        const char *namea;
+        const char *nameb;
+
+        g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0);
+        g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0);
+
+        namea = gvc_mixer_stream_get_name (a);
+        nameb = gvc_mixer_stream_get_name (b);
+
+        return g_utf8_collate (namea, nameb);
+}
+
+GSList *
+gvc_mixer_control_get_sinks (GvcMixerControl *control)
+{
+        GSList *retval;
+
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+        retval = NULL;
+        g_hash_table_foreach (control->priv->sinks,
+                              listify_hash_values_hfunc,
+                              &retval);
+        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+GSList *
+gvc_mixer_control_get_sink_inputs (GvcMixerControl *control)
+{
+        GSList *retval;
+
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL);
+
+        retval = NULL;
+        g_hash_table_foreach (control->priv->sink_inputs,
+                              listify_hash_values_hfunc,
+                              &retval);
+        return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate);
+}
+
+static void
+dec_outstanding (GvcMixerControl *control)
+{
+        if (control->priv->n_outstanding <= 0) {
+                return;
+        }
+
+        if (--control->priv->n_outstanding <= 0) {
+                g_signal_emit (G_OBJECT (control), signals[READY], 0);
+        }
+}
+
+gboolean
+gvc_mixer_control_is_ready (GvcMixerControl *control)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+
+        return (control->priv->n_outstanding == 0);
+}
+
+static void
+update_server (GvcMixerControl      *control,
+               const pa_server_info *info)
+{
+        if (info->default_source_name != NULL) {
+                g_debug ("Default source: %s", info->default_source_name);
+                g_free (control->priv->default_source_name);
+                control->priv->default_source_name = g_strdup (info->default_source_name);
+                /* FIXME: iterate over existing sources and update default flag */
+        }
+        if (info->default_sink_name != NULL) {
+                g_debug ("Default sink: %s", info->default_sink_name);
+                g_free (control->priv->default_sink_name);
+                control->priv->default_sink_name = g_strdup (info->default_sink_name);
+                /* FIXME: iterate over existing sinks and update default flag */
+        }
+}
+
+static void
+update_sink (GvcMixerControl    *control,
+             const pa_sink_info *info)
+{
+        GvcMixerStream *stream;
+        gboolean        is_new;
+        gboolean        is_default;
+        pa_volume_t     avg_volume;
+#if 0
+        g_debug ("Updating sink: index=%u name='%s' description='%s'",
+                 info->index,
+                 info->name,
+                 info->description);
+#endif
+        is_new = FALSE;
+        is_default = FALSE;
+
+        stream = g_hash_table_lookup (control->priv->sinks,
+                                      GUINT_TO_POINTER (info->index));
+        if (stream == NULL) {
+                stream = gvc_mixer_sink_new (control->priv->pa_context,
+                                             info->index,
+                                             info->channel_map.channels);
+                g_hash_table_insert (control->priv->sinks,
+                                     GUINT_TO_POINTER (info->index),
+                                     stream);
+                is_new = TRUE;
+        }
+
+        if (control->priv->default_sink_name != NULL
+            && info->name != NULL
+            && strcmp (control->priv->default_sink_name, info->name) == 0) {
+                is_default = TRUE;
+        }
+        avg_volume = pa_cvolume_avg (&info->volume);
+
+        gvc_mixer_stream_set_name (stream, info->name);
+        gvc_mixer_stream_set_icon_name (stream, "audio-card");
+        gvc_mixer_stream_set_volume (stream, (guint)avg_volume);
+        gvc_mixer_stream_set_is_muted (stream, info->mute);
+        gvc_mixer_stream_set_is_default (stream, is_default);
+
+        //w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL;
+
+        if (is_new) {
+                g_signal_emit (G_OBJECT (control), signals[STREAM_ADDED], 0, info->index);
+        }
+
+        if (is_default) {
+                control->priv->default_sink_index = info->index;
+        }
+}
+
+static void
+update_source (GvcMixerControl      *control,
+               const pa_source_info *info)
+{
+        g_debug ("Updating source: index=%u name='%s' description='%s'",
+                 info->index,
+                 info->name,
+                 info->description);
+
+}
+
+static void
+set_icon_name_from_proplist (GvcMixerStream *stream,
+                             pa_proplist    *l,
+                             const char     *default_icon_name)
+{
+        const char *t;
+
+        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) {
+                goto finish;
+        }
+
+        if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) {
+                goto finish;
+        }
+
+        if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) {
+                goto finish;
+        }
+
+        if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) {
+
+                if (strcmp (t, "video") == 0 ||
+                    strcmp (t, "phone") == 0) {
+                        goto finish;
+                }
+
+                if (strcmp (t, "music") == 0) {
+                        t = "audio";
+                        goto finish;
+                }
+
+                if (strcmp (t, "game") == 0) {
+                        t = "applications-games";
+                        goto finish;
+                }
+
+                if (strcmp (t, "event") == 0) {
+                        t = "dialog-information";
+                        goto finish;
+                }
+        }
+
+        t = default_icon_name;
+
+ finish:
+        gvc_mixer_stream_set_icon_name (stream, t);
+}
+
+static void
+update_sink_input (GvcMixerControl          *control,
+                   const pa_sink_input_info *info)
+{
+        GvcMixerStream *stream;
+        gboolean        is_new;
+        gboolean        is_default;
+        pa_volume_t     avg_volume;
+
+        g_debug ("Updating sink input: index=%u name='%s'",
+                 info->index,
+                 info->name);
+
+        is_new = FALSE;
+        is_default = FALSE;
+
+        stream = g_hash_table_lookup (control->priv->sink_inputs,
+                                      GUINT_TO_POINTER (info->index));
+        if (stream == NULL) {
+                stream = gvc_mixer_sink_input_new (control->priv->pa_context,
+                                                   info->index,
+                                                   info->channel_map.channels);
+                g_hash_table_insert (control->priv->sink_inputs,
+                                     GUINT_TO_POINTER (info->index),
+                                     stream);
+                is_new = TRUE;
+        }
+
+        avg_volume = pa_cvolume_avg (&info->volume);
+
+        gvc_mixer_stream_set_name (stream, info->name);
+
+        set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia");
+        gvc_mixer_stream_set_volume (stream, (guint)avg_volume);
+        gvc_mixer_stream_set_is_muted (stream, info->mute);
+        gvc_mixer_stream_set_is_default (stream, is_default);
+
+        if (is_new) {
+                g_signal_emit (G_OBJECT (control), signals[STREAM_ADDED], 0, info->index);
+        }
+}
+
+static void
+update_source_output (GvcMixerControl             *control,
+                      const pa_source_output_info *info)
+{
+        g_debug ("Updating source output: index=%u name='%s'",
+                 info->index,
+                 info->name);
+}
+
+static void
+update_client (GvcMixerControl      *control,
+               const pa_client_info *info)
+{
+#if 0
+        g_debug ("Updating client: index=%u name='%s'",
+                 info->index,
+                 info->name);
+#endif
+}
+
+static void
+_pa_context_get_sink_info_cb (pa_context         *context,
+                              const pa_sink_info *i,
+                              int                 eol,
+                              void               *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+                        return;
+                }
+
+                g_warning ("Sink callback failure");
+                return;
+        }
+
+        if (eol > 0) {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_sink (control, i);
+}
+
+
+static void
+_pa_context_get_source_info_cb (pa_context           *context,
+                                const pa_source_info *i,
+                                int                   eol,
+                                void                 *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+                        return;
+                }
+
+                g_warning ("Source callback failure");
+                return;
+        }
+
+        if (eol > 0) {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_source (control, i);
+}
+
+static void
+_pa_context_get_sink_input_info_cb (pa_context               *context,
+                                    const pa_sink_input_info *i,
+                                    int                       eol,
+                                    void                     *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+                        return;
+                }
+
+                g_warning ("Sink input callback failure");
+                return;
+        }
+
+        if (eol > 0) {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_sink_input (control, i);
+}
+
+static void
+_pa_context_get_source_output_info_cb (pa_context                  *context,
+                                       const pa_source_output_info *i,
+                                       int                          eol,
+                                       void                        *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+                        return;
+                }
+
+                g_warning ("Source output callback failure");
+                return;
+        }
+
+        if (eol > 0)  {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_source_output (control, i);
+}
+
+static void
+_pa_context_get_client_info_cb (pa_context           *context,
+                                const pa_client_info *i,
+                                int                   eol,
+                                void                 *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                if (pa_context_errno (context) == PA_ERR_NOENTITY) {
+                        return;
+                }
+
+                g_warning ("Client callback failure");
+                return;
+        }
+
+        if (eol > 0) {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_client (control, i);
+}
+
+static void
+_pa_context_get_server_info_cb (pa_context           *context,
+                                const pa_server_info *i,
+                                void                 *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (i == NULL) {
+                g_warning ("Server info callback failure");
+                return;
+        }
+
+        update_server (control, i);
+        dec_outstanding (control);
+}
+
+static void
+remove_event_role_stream (GvcMixerControl *control)
+{
+        g_debug ("Removing event role");
+}
+
+static void
+update_event_role_stream (GvcMixerControl                  *control,
+                          const pa_ext_stream_restore_info *info)
+{
+        GvcMixerStream *stream;
+        gboolean        is_new;
+        pa_volume_t     avg_volume;
+
+        if (strcmp (info->name, "sink-input-by-media-role:event") != 0) {
+                return;
+        }
+
+        g_debug ("Updating event role: name='%s' device='%s'",
+                 info->name,
+                 info->device);
+
+        is_new = FALSE;
+
+        if (control->priv->event_sink_input == NULL) {
+                stream = gvc_mixer_sink_input_new (control->priv->pa_context,
+                                                   0,
+                                                   1);
+                control->priv->event_sink_input = stream;
+                is_new = TRUE;
+        } else {
+                stream = control->priv->event_sink_input;
+        }
+
+        avg_volume = pa_cvolume_avg (&info->volume);
+
+        gvc_mixer_stream_set_name (stream, _("System Sounds"));
+        //gvc_mixer_stream_set_name (stream, info->name);
+        gvc_mixer_stream_set_icon_name (stream, "multimedia-volume-control");
+        gvc_mixer_stream_set_volume (stream, (guint)avg_volume);
+        gvc_mixer_stream_set_is_muted (stream, info->mute);
+}
+
+static void
+_pa_ext_stream_restore_read_cb (pa_context                       *context,
+                                const pa_ext_stream_restore_info *i,
+                                int                               eol,
+                                void                             *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        if (eol < 0) {
+                g_debug ("Failed to initialized stream_restore extension: %s",
+                         pa_strerror (pa_context_errno (context)));
+                remove_event_role_stream (control);
+                return;
+        }
+
+        if (eol > 0) {
+                dec_outstanding (control);
+                return;
+        }
+
+        update_event_role_stream (control, i);
+}
+
+static void
+_pa_ext_stream_restore_subscribe_cb (pa_context *context,
+                                     void       *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+        pa_operation    *o;
+
+        o = pa_ext_stream_restore_read (context,
+                                        _pa_ext_stream_restore_read_cb,
+                                        control);
+        if (o == NULL) {
+                g_warning ("pa_ext_stream_restore_read() failed");
+                return;
+        }
+
+        pa_operation_unref (o);
+}
+
+static void
+req_update_server_info (GvcMixerControl *control,
+                        int              index)
+{
+        pa_operation *o;
+
+        o = pa_context_get_server_info (control->priv->pa_context,
+                                        _pa_context_get_server_info_cb,
+                                        control);
+        if (o == NULL) {
+                g_warning ("pa_context_get_server_info() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+req_update_client_info (GvcMixerControl *control,
+                        int              index)
+{
+        pa_operation *o;
+
+        if (index < 0) {
+                o = pa_context_get_client_info_list (control->priv->pa_context,
+                                                     _pa_context_get_client_info_cb,
+                                                     control);
+        } else {
+                o = pa_context_get_client_info (control->priv->pa_context,
+                                                index,
+                                                _pa_context_get_client_info_cb,
+                                                control);
+        }
+
+        if (o == NULL) {
+                g_warning ("pa_context_client_info_list() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+req_update_sink_info (GvcMixerControl *control,
+                      int              index)
+{
+        pa_operation *o;
+
+        if (index < 0) {
+                o = pa_context_get_sink_info_list (control->priv->pa_context,
+                                                   _pa_context_get_sink_info_cb,
+                                                   control);
+        } else {
+                o = pa_context_get_sink_info_by_index (control->priv->pa_context,
+                                                       index,
+                                                       _pa_context_get_sink_info_cb,
+                                                       control);
+        }
+
+        if (o == NULL) {
+                g_warning ("pa_context_get_sink_info_list() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+req_update_source_info (GvcMixerControl *control,
+                        int              index)
+{
+        pa_operation *o;
+
+        if (index < 0) {
+                o = pa_context_get_source_info_list (control->priv->pa_context,
+                                                     _pa_context_get_source_info_cb,
+                                                     control);
+        } else {
+                o = pa_context_get_source_info_by_index(control->priv->pa_context,
+                                                        index,
+                                                        _pa_context_get_source_info_cb,
+                                                        control);
+        }
+
+        if (o == NULL) {
+                g_warning ("pa_context_get_source_info_list() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+req_update_sink_input_info (GvcMixerControl *control,
+                            int              index)
+{
+        pa_operation *o;
+
+        if (index < 0) {
+                o = pa_context_get_sink_input_info_list (control->priv->pa_context,
+                                                         _pa_context_get_sink_input_info_cb,
+                                                         control);
+        } else {
+                o = pa_context_get_sink_input_info (control->priv->pa_context,
+                                                    index,
+                                                    _pa_context_get_sink_input_info_cb,
+                                                    control);
+        }
+
+        if (o == NULL) {
+                g_warning ("pa_context_get_sink_input_info_list() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+req_update_source_output_info (GvcMixerControl *control,
+                               int              index)
+{
+        pa_operation *o;
+
+        if (index < 0) {
+                o = pa_context_get_source_output_info_list (control->priv->pa_context,
+                                                            _pa_context_get_source_output_info_cb,
+                                                            control);
+        } else {
+                o = pa_context_get_source_output_info (control->priv->pa_context,
+                                                       index,
+                                                       _pa_context_get_source_output_info_cb,
+                                                       control);
+        }
+
+        if (o == NULL) {
+                g_warning ("pa_context_get_source_output_info_list() failed");
+                return;
+        }
+        pa_operation_unref (o);
+}
+
+static void
+remove_client (GvcMixerControl *control,
+               guint            index)
+{
+        g_debug ("Removing client: index=%u", index);
+        /* FIXME: */
+}
+
+static void
+remove_sink (GvcMixerControl *control,
+             guint            index)
+{
+        g_debug ("Removing sink: index=%u", index);
+        /* FIXME: */
+}
+
+static void
+remove_source (GvcMixerControl *control,
+               guint            index)
+{
+        g_debug ("Removing source: index=%u", index);
+        /* FIXME: */
+}
+
+static void
+remove_sink_input (GvcMixerControl *control,
+                   guint            index)
+{
+        g_debug ("Removing source: index=%u", index);
+        /* FIXME: */
+}
+
+static void
+remove_source_output (GvcMixerControl *control,
+                      guint            index)
+{
+        g_debug ("Removing source output: index=%u", index);
+        /* FIXME: */
+}
+
+static void
+_pa_context_subscribe_cb (pa_context                  *context,
+                          pa_subscription_event_type_t t,
+                          uint32_t                     index,
+                          void                        *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+        case PA_SUBSCRIPTION_EVENT_SINK:
+                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                        remove_sink (control, index);
+                } else {
+                        req_update_sink_info (control, index);
+                }
+                break;
+
+        case PA_SUBSCRIPTION_EVENT_SOURCE:
+                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                        remove_source (control, index);
+                } else {
+                        req_update_source_info (control, index);
+                }
+                break;
+
+        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                        remove_sink_input (control, index);
+                } else {
+                        req_update_sink_input_info (control, index);
+                }
+                break;
+
+        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                        remove_source_output (control, index);
+                } else {
+                        req_update_source_output_info (control, index);
+                }
+                break;
+
+        case PA_SUBSCRIPTION_EVENT_CLIENT:
+                if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                        remove_client (control, index);
+                } else {
+                        req_update_client_info (control, index);
+                }
+                break;
+
+        case PA_SUBSCRIPTION_EVENT_SERVER:
+                req_update_server_info (control, index);
+                break;
+        }
+}
+
+static void
+gvc_mixer_control_ready (GvcMixerControl *control)
+{
+        pa_operation *o;
+
+        pa_context_set_subscribe_callback (control->priv->pa_context,
+                                           _pa_context_subscribe_cb,
+                                           control);
+        o = pa_context_subscribe (control->priv->pa_context,
+                                  (pa_subscription_mask_t)
+                                  (PA_SUBSCRIPTION_MASK_SINK|
+                                   PA_SUBSCRIPTION_MASK_SOURCE|
+                                   PA_SUBSCRIPTION_MASK_SINK_INPUT|
+                                   PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
+                                   PA_SUBSCRIPTION_MASK_CLIENT|
+                                   PA_SUBSCRIPTION_MASK_SERVER),
+                                  NULL,
+                                  NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_context_subscribe() failed");
+                return;
+        }
+        pa_operation_unref (o);
+
+        req_update_server_info (control, -1);
+        req_update_client_info (control, -1);
+        req_update_sink_info (control, -1);
+        req_update_source_info (control, -1);
+        req_update_sink_input_info (control, -1);
+        req_update_source_output_info (control, -1);
+
+        control->priv->n_outstanding = 6;
+
+        /* This call is not always supported */
+        o = pa_ext_stream_restore_read (control->priv->pa_context,
+                                        _pa_ext_stream_restore_read_cb,
+                                        control);
+        if (o != NULL) {
+                pa_operation_unref (o);
+                control->priv->n_outstanding++;
+
+                pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context,
+                                                        _pa_ext_stream_restore_subscribe_cb,
+                                                        control);
+
+
+                o = pa_ext_stream_restore_subscribe (control->priv->pa_context,
+                                                     1,
+                                                     NULL,
+                                                     NULL);
+                if (o != NULL) {
+                        pa_operation_unref (o);
+                }
+
+        } else {
+                g_debug ("Failed to initialized stream_restore extension: %s",
+                         pa_strerror (pa_context_errno (control->priv->pa_context)));
+        }
+}
+
+static void
+_pa_context_state_cb (pa_context *context,
+                      void       *userdata)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (userdata);
+
+        switch (pa_context_get_state (context)) {
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+                break;
+
+        case PA_CONTEXT_READY:
+                gvc_mixer_control_ready (control);
+                break;
+
+        case PA_CONTEXT_FAILED:
+                g_warning ("Connection failed");
+                break;
+
+        case PA_CONTEXT_TERMINATED:
+        default:
+                /* FIXME: */
+                break;
+        }
+}
+
+gboolean
+gvc_mixer_control_open (GvcMixerControl *control)
+{
+        int res;
+
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+        g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
+        g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE);
+
+        pa_context_set_state_callback (control->priv->pa_context,
+                                       _pa_context_state_cb,
+                                       control);
+
+        res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) 0, NULL);
+        if (res < 0) {
+                g_warning ("Failed to connect context: %s",
+                           pa_strerror (pa_context_errno (control->priv->pa_context)));
+        }
+
+        return res;
+}
+
+gboolean
+gvc_mixer_control_close (GvcMixerControl *control)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE);
+        g_return_val_if_fail (control->priv->pa_context != NULL, FALSE);
+
+        pa_context_disconnect (control->priv->pa_context);
+        return TRUE;
+}
+
+static void
+gvc_mixer_control_dispose (GObject *object)
+{
+        GvcMixerControl *control = GVC_MIXER_CONTROL (object);
+
+        if (control->priv->pa_context != NULL) {
+                pa_context_unref (control->priv->pa_context);
+                control->priv->pa_context = NULL;
+        }
+
+        if (control->priv->pa_mainloop != NULL) {
+                pa_glib_mainloop_free (control->priv->pa_mainloop);
+                control->priv->pa_mainloop = NULL;
+        }
+
+        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object);
+}
+
+static GObject *
+gvc_mixer_control_constructor (GType                  type,
+                               guint                  n_construct_properties,
+                               GObjectConstructParam *construct_params)
+{
+        GObject         *object;
+        GvcMixerControl *self;
+        pa_proplist     *proplist;
+
+        object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_MIXER_CONTROL (object);
+
+        /* FIXME: read these from an object property */
+        proplist = pa_proplist_new ();
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_NAME,
+                          _("GNOME Volume Control"));
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_ID,
+                          "org.gnome.VolumeControl");
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_ICON_NAME,
+                          "multimedia-volume-control");
+        pa_proplist_sets (proplist,
+                          PA_PROP_APPLICATION_VERSION,
+                          PACKAGE_VERSION);
+
+        self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist);
+        g_assert (self->priv->pa_context);
+        pa_proplist_free (proplist);
+
+        return object;
+}
+
+static void
+gvc_mixer_control_class_init (GvcMixerControlClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gvc_mixer_control_constructor;
+        object_class->dispose = gvc_mixer_control_dispose;
+        object_class->finalize = gvc_mixer_control_finalize;
+
+        signals [READY] =
+                g_signal_new ("ready",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GvcMixerControlClass, ready),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [STREAM_ADDED] =
+                g_signal_new ("stream-added",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_added),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__UINT,
+                              G_TYPE_NONE, 1, G_TYPE_UINT);
+        signals [STREAM_REMOVED] =
+                g_signal_new ("stream-removed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__UINT,
+                              G_TYPE_NONE, 1, G_TYPE_UINT);
+
+        g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate));
+}
+
+static void
+gvc_mixer_control_init (GvcMixerControl *control)
+{
+        control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control);
+
+        control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ());
+        g_assert (control->priv->pa_mainloop);
+
+        control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop);
+        g_assert (control->priv->pa_api);
+
+        control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+        control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+        control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+        control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+        control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref);
+}
+
+static void
+gvc_mixer_control_finalize (GObject *object)
+{
+        GvcMixerControl *mixer_control;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_CONTROL (object));
+
+        mixer_control = GVC_MIXER_CONTROL (object);
+
+        g_return_if_fail (mixer_control->priv != NULL);
+        G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object);
+}
+
+GvcMixerControl *
+gvc_mixer_control_new (void)
+{
+        GObject *control;
+        control = g_object_new (GVC_TYPE_MIXER_CONTROL, NULL);
+        return GVC_MIXER_CONTROL (control);
+}

Added: trunk/gnome-volume-control/src/gvc-mixer-control.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-control.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_CONTROL_H
+#define __GVC_MIXER_CONTROL_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_CONTROL         (gvc_mixer_control_get_type ())
+#define GVC_MIXER_CONTROL(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl))
+#define GVC_MIXER_CONTROL_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
+#define GVC_IS_MIXER_CONTROL(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CONTROL))
+#define GVC_IS_MIXER_CONTROL_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CONTROL))
+#define GVC_MIXER_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass))
+
+typedef struct GvcMixerControlPrivate GvcMixerControlPrivate;
+
+typedef struct
+{
+        GObject                 parent;
+        GvcMixerControlPrivate *priv;
+} GvcMixerControl;
+
+typedef struct
+{
+        GObjectClass            parent_class;
+
+        void (*ready)          (GvcMixerControl *control);
+        void (*stream_added)   (GvcMixerControl *control,
+                                guint            index);
+        void (*stream_removed) (GvcMixerControl *control,
+                                guint            index);
+} GvcMixerControlClass;
+
+GType               gvc_mixer_control_get_type            (void);
+
+GvcMixerControl *   gvc_mixer_control_new                 (void);
+
+gboolean            gvc_mixer_control_open                (GvcMixerControl *control);
+gboolean            gvc_mixer_control_close               (GvcMixerControl *control);
+gboolean            gvc_mixer_control_is_ready            (GvcMixerControl *control);
+
+GSList *            gvc_mixer_control_get_sinks           (GvcMixerControl *control);
+GSList *            gvc_mixer_control_get_sink_inputs     (GvcMixerControl *control);
+
+GvcMixerStream *    gvc_mixer_control_get_default_sink    (GvcMixerControl *control);
+GvcMixerStream *    gvc_mixer_control_get_event_sink_input (GvcMixerControl *control);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_CONTROL_H */

Added: trunk/gnome-volume-control/src/gvc-mixer-dialog.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-dialog.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,394 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gvc-channel-bar.h"
+#include "gvc-mixer-control.h"
+#include "gvc-mixer-sink.h"
+#include "gvc-mixer-dialog.h"
+
+#define SCALE_SIZE 128
+
+#define GVC_MIXER_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogPrivate))
+
+struct GvcMixerDialogPrivate
+{
+        GvcMixerControl *mixer_control;
+        GHashTable      *bars;
+        GtkWidget       *streams_box;
+        GtkWidget       *output_streams_box;
+        GtkWidget       *application_streams_box;
+};
+
+enum
+{
+        PROP_0,
+        PROP_MIXER_CONTROL
+};
+
+static void     gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass);
+static void     gvc_mixer_dialog_init       (GvcMixerDialog      *mixer_dialog);
+static void     gvc_mixer_dialog_finalize   (GObject            *object);
+
+G_DEFINE_TYPE (GvcMixerDialog, gvc_mixer_dialog, GTK_TYPE_DIALOG)
+static void
+
+gvc_mixer_dialog_set_mixer_control (GvcMixerDialog  *dialog,
+                                    GvcMixerControl *control)
+{
+        g_return_if_fail (GVC_MIXER_DIALOG (dialog));
+        g_return_if_fail (GVC_IS_MIXER_CONTROL (control));
+
+        g_object_ref (control);
+
+        if (dialog->priv->mixer_control != NULL) {
+                g_object_unref (dialog->priv->mixer_control);
+        }
+
+        dialog->priv->mixer_control = control;
+
+        g_object_notify (G_OBJECT (dialog), "mixer-control");
+}
+
+static GvcMixerControl *
+gvc_mixer_dialog_get_mixer_control (GvcMixerDialog *dialog)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_DIALOG (dialog), NULL);
+
+        return dialog->priv->mixer_control;
+}
+
+static void
+gvc_mixer_dialog_set_property (GObject       *object,
+                               guint          prop_id,
+                               const GValue  *value,
+                               GParamSpec    *pspec)
+{
+        GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
+
+        switch (prop_id) {
+        case PROP_MIXER_CONTROL:
+                gvc_mixer_dialog_set_mixer_control (self, g_value_get_object (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gvc_mixer_dialog_get_property (GObject     *object,
+                               guint        prop_id,
+                               GValue      *value,
+                               GParamSpec  *pspec)
+{
+        GvcMixerDialog *self = GVC_MIXER_DIALOG (object);
+
+        switch (prop_id) {
+        case PROP_MIXER_CONTROL:
+                g_value_set_object (value, gvc_mixer_dialog_get_mixer_control (self));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+on_adjustment_value_changed (GtkAdjustment  *adjustment,
+                             GvcMixerDialog *dialog)
+{
+        gdouble         volume;
+        GvcMixerStream *stream;
+
+        stream = g_object_get_data (G_OBJECT (adjustment), "gvc-mixer-dialog-stream"),
+        volume = gtk_adjustment_get_value (adjustment);
+        if (stream != NULL) {
+                gvc_mixer_stream_change_volume (stream, (guint)volume);
+        }
+}
+
+static void
+on_bar_is_muted_notify (GObject        *object,
+                        GParamSpec     *pspec,
+                        GvcMixerDialog *dialog)
+{
+        gboolean        is_muted;
+        GvcMixerStream *stream;
+
+        stream = g_object_get_data (object, "gvc-mixer-dialog-stream");
+        is_muted = gvc_channel_bar_get_is_muted (GVC_CHANNEL_BAR (object));
+        if (stream != NULL) {
+                gvc_mixer_stream_change_is_muted (stream, is_muted);
+        }
+}
+
+static GtkWidget *
+lookup_bar_for_stream (GvcMixerDialog *dialog,
+                       GvcMixerStream *stream)
+{
+        GtkWidget *bar;
+
+        bar = g_hash_table_lookup (dialog->priv->bars, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)));
+
+        return bar;
+}
+
+static void
+on_stream_volume_notify (GObject        *object,
+                         GParamSpec     *pspec,
+                         GvcMixerDialog *dialog)
+{
+        GvcMixerStream *stream;
+        GtkWidget      *bar;
+        GtkAdjustment  *adj;
+
+        stream = GVC_MIXER_STREAM (object);
+
+        bar = lookup_bar_for_stream (dialog, stream);
+        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
+
+        g_signal_handlers_block_by_func (adj,
+                                         on_adjustment_value_changed,
+                                         dialog);
+
+        gtk_adjustment_set_value (adj,
+                                  gvc_mixer_stream_get_volume (stream));
+
+        g_signal_handlers_unblock_by_func (adj,
+                                           on_adjustment_value_changed,
+                                           dialog);
+}
+
+static void
+on_stream_is_muted_notify (GObject        *object,
+                           GParamSpec     *pspec,
+                           GvcMixerDialog *dialog)
+{
+        GvcMixerStream *stream;
+        GtkWidget      *bar;
+        gboolean        is_muted;
+
+        stream = GVC_MIXER_STREAM (object);
+        bar = lookup_bar_for_stream (dialog, stream);
+        is_muted = gvc_mixer_stream_get_is_muted (stream);
+        gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar),
+                                      is_muted);
+
+        if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
+                gtk_widget_set_sensitive (dialog->priv->application_streams_box,
+                                          !is_muted);
+        }
+
+}
+
+static void
+save_bar_for_stream (GvcMixerDialog *dialog,
+                     GvcMixerStream *stream,
+                     GtkWidget      *bar)
+{
+        g_hash_table_insert (dialog->priv->bars,
+                             GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)),
+                             bar);
+}
+
+static void
+add_stream (GvcMixerDialog *dialog,
+            GvcMixerStream *stream)
+{
+        GtkWidget     *bar;
+        GtkAdjustment *adj;
+        gboolean       is_muted;
+
+        bar = gvc_channel_bar_new ();
+
+        is_muted = gvc_mixer_stream_get_is_muted (stream);
+
+        if (stream == gvc_mixer_control_get_default_sink (dialog->priv->mixer_control)) {
+                gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
+                                          _("Speakers"));
+                gtk_widget_set_sensitive (dialog->priv->application_streams_box,
+                                          !is_muted);
+        } else {
+                gvc_channel_bar_set_name (GVC_CHANNEL_BAR (bar),
+                                          gvc_mixer_stream_get_name (stream));
+        }
+
+        gvc_channel_bar_set_icon_name (GVC_CHANNEL_BAR (bar),
+                                       gvc_mixer_stream_get_icon_name (stream));
+        g_object_set_data (G_OBJECT (bar), "gvc-mixer-dialog-stream", stream);
+
+        save_bar_for_stream (dialog, stream, bar);
+
+        if (GVC_IS_MIXER_SINK (stream)) {
+                gtk_box_pack_start (GTK_BOX (dialog->priv->output_streams_box), bar, TRUE, FALSE, 0);
+        } else {
+                gtk_box_pack_start (GTK_BOX (dialog->priv->application_streams_box), bar, TRUE, FALSE, 0);
+        }
+
+        gvc_channel_bar_set_is_muted (GVC_CHANNEL_BAR (bar), is_muted);
+
+        adj = GTK_ADJUSTMENT (gvc_channel_bar_get_adjustment (GVC_CHANNEL_BAR (bar)));
+
+        gtk_adjustment_set_value (adj,
+                                  gvc_mixer_stream_get_volume (stream));
+
+        g_object_set_data (G_OBJECT (adj), "gvc-mixer-dialog-stream", stream);
+        g_signal_connect (adj,
+                          "value-changed",
+                          G_CALLBACK (on_adjustment_value_changed),
+                          dialog);
+
+        g_signal_connect (bar,
+                          "notify::is-muted",
+                          G_CALLBACK (on_bar_is_muted_notify),
+                          dialog);
+        g_signal_connect (stream,
+                          "notify::is-muted",
+                          G_CALLBACK (on_stream_is_muted_notify),
+                          dialog);
+        g_signal_connect (stream,
+                          "notify::volume",
+                          G_CALLBACK (on_stream_volume_notify),
+                          dialog);
+        gtk_widget_show (bar);
+}
+
+static GObject *
+gvc_mixer_dialog_constructor (GType                  type,
+                              guint                  n_construct_properties,
+                              GObjectConstructParam *construct_params)
+{
+        GObject        *object;
+        GvcMixerDialog *self;
+        GtkWidget      *separator;
+        GtkWidget      *main_vbox;
+        GSList         *streams;
+        GSList         *l;
+        GvcMixerStream *stream;
+
+        object = G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_MIXER_DIALOG (object);
+
+        main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (self));
+        self->priv->streams_box = gtk_hbox_new (FALSE, 12);
+        gtk_container_add (GTK_CONTAINER (main_vbox), self->priv->streams_box);
+
+        self->priv->output_streams_box = gtk_hbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
+                            self->priv->output_streams_box,
+                            FALSE, FALSE, 6);
+
+        separator = gtk_vseparator_new ();
+        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
+                            separator,
+                            FALSE, FALSE, 6);
+
+        self->priv->application_streams_box = gtk_hbox_new (FALSE, 12);
+        gtk_box_pack_start (GTK_BOX (self->priv->streams_box),
+                            self->priv->application_streams_box,
+                            FALSE, FALSE, 6);
+
+        gtk_widget_show_all (self->priv->streams_box);
+
+        streams = gvc_mixer_control_get_sinks (self->priv->mixer_control);
+        for (l = streams; l != NULL; l = l->next) {
+                stream = l->data;
+                add_stream (self, stream);
+        }
+        g_slist_free (streams);
+
+        stream = gvc_mixer_control_get_event_sink_input (self->priv->mixer_control);
+        add_stream (self, stream);
+
+        streams = gvc_mixer_control_get_sink_inputs (self->priv->mixer_control);
+        for (l = streams; l != NULL; l = l->next) {
+                stream = l->data;
+                add_stream (self, stream);
+        }
+        g_slist_free (streams);
+
+        return object;
+}
+
+static void
+gvc_mixer_dialog_class_init (GvcMixerDialogClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->constructor = gvc_mixer_dialog_constructor;
+        object_class->finalize = gvc_mixer_dialog_finalize;
+        object_class->set_property = gvc_mixer_dialog_set_property;
+        object_class->get_property = gvc_mixer_dialog_get_property;
+
+        g_object_class_install_property (object_class,
+                                         PROP_MIXER_CONTROL,
+                                         g_param_spec_object ("mixer-control",
+                                                              "mixer control",
+                                                              "mixer control",
+                                                              GVC_TYPE_MIXER_CONTROL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+        g_type_class_add_private (klass, sizeof (GvcMixerDialogPrivate));
+}
+
+static void
+gvc_mixer_dialog_init (GvcMixerDialog *dialog)
+{
+        dialog->priv = GVC_MIXER_DIALOG_GET_PRIVATE (dialog);
+        dialog->priv->bars = g_hash_table_new (NULL, NULL);
+}
+
+static void
+gvc_mixer_dialog_finalize (GObject *object)
+{
+        GvcMixerDialog *mixer_dialog;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_DIALOG (object));
+
+        mixer_dialog = GVC_MIXER_DIALOG (object);
+
+        g_return_if_fail (mixer_dialog->priv != NULL);
+        G_OBJECT_CLASS (gvc_mixer_dialog_parent_class)->finalize (object);
+}
+
+GvcMixerDialog *
+gvc_mixer_dialog_new (GvcMixerControl *control)
+{
+        GObject *dialog;
+        dialog = g_object_new (GVC_TYPE_MIXER_DIALOG,
+                               "icon-name", "multimedia-volume-control",
+                               "title", _("Volume Control"),
+                               "has-separator", FALSE,
+                               "mixer-control", control,
+                               NULL);
+        return GVC_MIXER_DIALOG (dialog);
+}

Added: trunk/gnome-volume-control/src/gvc-mixer-dialog.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-dialog.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_DIALOG_H
+#define __GVC_MIXER_DIALOG_H
+
+#include <glib-object.h>
+#include "gvc-mixer-control.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_DIALOG         (gvc_mixer_dialog_get_type ())
+#define GVC_MIXER_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialog))
+#define GVC_MIXER_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass))
+#define GVC_IS_MIXER_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_DIALOG))
+#define GVC_IS_MIXER_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_DIALOG))
+#define GVC_MIXER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_DIALOG, GvcMixerDialogClass))
+
+typedef struct GvcMixerDialogPrivate GvcMixerDialogPrivate;
+
+typedef struct
+{
+        GtkDialog              parent;
+        GvcMixerDialogPrivate *priv;
+} GvcMixerDialog;
+
+typedef struct
+{
+        GtkDialogClass         parent_class;
+} GvcMixerDialogClass;
+
+GType               gvc_mixer_dialog_get_type            (void);
+
+GvcMixerDialog *    gvc_mixer_dialog_new                 (GvcMixerControl *control);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_DIALOG_H */

Added: trunk/gnome-volume-control/src/gvc-mixer-sink-input.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-sink-input.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,172 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-sink-input.h"
+
+#define GVC_MIXER_SINK_INPUT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputPrivate))
+
+struct GvcMixerSinkInputPrivate
+{
+        gpointer dummy;
+};
+
+static void     gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass);
+static void     gvc_mixer_sink_input_init       (GvcMixerSinkInput      *mixer_sink_input);
+static void     gvc_mixer_sink_input_finalize   (GObject            *object);
+
+G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_sink_input_change_volume (GvcMixerStream *stream,
+                                    guint           volume)
+{
+        pa_operation      *o;
+        guint              index;
+        guint              num_channels;
+        pa_context        *context;
+        pa_cvolume         cv;
+
+        index = gvc_mixer_stream_get_index (stream);
+        num_channels = gvc_mixer_stream_get_num_channels (stream);
+
+        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
+
+        context = gvc_mixer_stream_get_pa_context (stream);
+
+        o = pa_context_set_sink_input_volume (context,
+                                              index,
+                                              &cv,
+                                              NULL,
+                                              NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_context_set_sink_input_volume() failed");
+                return FALSE;
+        }
+
+        pa_operation_unref(o);
+
+        return TRUE;
+}
+
+static gboolean
+gvc_mixer_sink_input_change_is_muted (GvcMixerStream *stream,
+                                      gboolean        is_muted)
+{
+        pa_operation *o;
+        guint         index;
+        pa_context   *context;
+
+        index = gvc_mixer_stream_get_index (stream);
+        context = gvc_mixer_stream_get_pa_context (stream);
+
+        o = pa_context_set_sink_input_mute (context,
+                                            index,
+                                            is_muted,
+                                            NULL,
+                                            NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_context_set_sink_input_mute_by_index() failed");
+                return FALSE;
+        }
+
+        pa_operation_unref(o);
+
+        return TRUE;
+}
+
+static GObject *
+gvc_mixer_sink_input_constructor (GType                  type,
+                                  guint                  n_construct_properties,
+                                  GObjectConstructParam *construct_params)
+{
+        GObject       *object;
+        GvcMixerSinkInput *self;
+
+        object = G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_MIXER_SINK_INPUT (object);
+
+        return object;
+}
+
+static void
+gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass)
+{
+        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
+        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+        object_class->constructor = gvc_mixer_sink_input_constructor;
+        object_class->finalize = gvc_mixer_sink_input_finalize;
+
+        stream_class->change_volume = gvc_mixer_sink_input_change_volume;
+        stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted;
+
+        g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate));
+}
+
+static void
+gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input)
+{
+        sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input);
+
+}
+
+static void
+gvc_mixer_sink_input_finalize (GObject *object)
+{
+        GvcMixerSinkInput *mixer_sink_input;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object));
+
+        mixer_sink_input = GVC_MIXER_SINK_INPUT (object);
+
+        g_return_if_fail (mixer_sink_input->priv != NULL);
+        G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->finalize (object);
+}
+
+GvcMixerStream *
+gvc_mixer_sink_input_new (pa_context *context,
+                          guint       index,
+                          guint       num_channels)
+{
+        GObject *object;
+
+        object = g_object_new (GVC_TYPE_MIXER_SINK_INPUT,
+                               "pa-context", context,
+                               "index", index,
+                               "num-channels", num_channels,
+                               NULL);
+
+        return GVC_MIXER_STREAM (object);
+}

Added: trunk/gnome-volume-control/src/gvc-mixer-sink-input.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-sink-input.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SINK_INPUT_H
+#define __GVC_MIXER_SINK_INPUT_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SINK_INPUT         (gvc_mixer_sink_input_get_type ())
+#define GVC_MIXER_SINK_INPUT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInput))
+#define GVC_MIXER_SINK_INPUT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))
+#define GVC_IS_MIXER_SINK_INPUT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK_INPUT))
+#define GVC_IS_MIXER_SINK_INPUT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK_INPUT))
+#define GVC_MIXER_SINK_INPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass))
+
+typedef struct GvcMixerSinkInputPrivate GvcMixerSinkInputPrivate;
+
+typedef struct
+{
+        GvcMixerStream            parent;
+        GvcMixerSinkInputPrivate *priv;
+} GvcMixerSinkInput;
+
+typedef struct
+{
+        GvcMixerStreamClass parent_class;
+} GvcMixerSinkInputClass;
+
+GType               gvc_mixer_sink_input_get_type      (void);
+
+GvcMixerStream *    gvc_mixer_sink_input_new           (pa_context *context,
+                                                        guint       index,
+                                                        guint       num_channels);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SINK_INPUT_H */

Added: trunk/gnome-volume-control/src/gvc-mixer-sink.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-sink.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,172 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-sink.h"
+
+#define GVC_MIXER_SINK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkPrivate))
+
+struct GvcMixerSinkPrivate
+{
+        gpointer dummy;
+};
+
+static void     gvc_mixer_sink_class_init (GvcMixerSinkClass *klass);
+static void     gvc_mixer_sink_init       (GvcMixerSink      *mixer_sink);
+static void     gvc_mixer_sink_finalize   (GObject            *object);
+
+G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM)
+
+static gboolean
+gvc_mixer_sink_change_volume (GvcMixerStream *stream,
+                              guint           volume)
+{
+        pa_operation *o;
+        guint         index;
+        guint         num_channels;
+        pa_context   *context;
+        pa_cvolume    cv;
+
+        index = gvc_mixer_stream_get_index (stream);
+        num_channels = gvc_mixer_stream_get_num_channels (stream);
+
+        pa_cvolume_set (&cv, num_channels, (pa_volume_t)volume);
+
+        context = gvc_mixer_stream_get_pa_context (stream);
+
+        o = pa_context_set_sink_volume_by_index (context,
+                                                 index,
+                                                 &cv,
+                                                 NULL,
+                                                 NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_context_set_sink_volume_by_index() failed");
+                return FALSE;
+        }
+
+        pa_operation_unref(o);
+
+        return TRUE;
+}
+
+static gboolean
+gvc_mixer_sink_change_is_muted (GvcMixerStream *stream,
+                                gboolean        is_muted)
+{
+        pa_operation *o;
+        guint         index;
+        pa_context   *context;
+
+        index = gvc_mixer_stream_get_index (stream);
+        context = gvc_mixer_stream_get_pa_context (stream);
+
+        o = pa_context_set_sink_mute_by_index (context,
+                                               index,
+                                               is_muted,
+                                               NULL,
+                                               NULL);
+
+        if (o == NULL) {
+                g_warning ("pa_context_set_sink_mute_by_index() failed");
+                return FALSE;
+        }
+
+        pa_operation_unref(o);
+
+        return TRUE;
+}
+
+static GObject *
+gvc_mixer_sink_constructor (GType                  type,
+                            guint                  n_construct_properties,
+                            GObjectConstructParam *construct_params)
+{
+        GObject       *object;
+        GvcMixerSink *self;
+
+        object = G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_MIXER_SINK (object);
+
+        return object;
+}
+
+static void
+gvc_mixer_sink_class_init (GvcMixerSinkClass *klass)
+{
+        GObjectClass        *object_class = G_OBJECT_CLASS (klass);
+        GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass);
+
+        object_class->constructor = gvc_mixer_sink_constructor;
+        object_class->finalize = gvc_mixer_sink_finalize;
+
+        stream_class->change_volume = gvc_mixer_sink_change_volume;
+        stream_class->change_is_muted = gvc_mixer_sink_change_is_muted;
+
+        g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate));
+}
+
+static void
+gvc_mixer_sink_init (GvcMixerSink *sink)
+{
+        sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink);
+
+}
+
+static void
+gvc_mixer_sink_finalize (GObject *object)
+{
+        GvcMixerSink *mixer_sink;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_SINK (object));
+
+        mixer_sink = GVC_MIXER_SINK (object);
+
+        g_return_if_fail (mixer_sink->priv != NULL);
+        G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->finalize (object);
+}
+
+GvcMixerStream *
+gvc_mixer_sink_new (pa_context *context,
+                    guint       index,
+                    guint       num_channels)
+{
+        GObject *object;
+
+        object = g_object_new (GVC_TYPE_MIXER_SINK,
+                               "pa-context", context,
+                               "index", index,
+                               "num-channels", num_channels,
+                               NULL);
+
+        return GVC_MIXER_STREAM (object);
+}

Added: trunk/gnome-volume-control/src/gvc-mixer-sink.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-sink.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_SINK_H
+#define __GVC_MIXER_SINK_H
+
+#include <glib-object.h>
+#include "gvc-mixer-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_SINK         (gvc_mixer_sink_get_type ())
+#define GVC_MIXER_SINK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK, GvcMixerSink))
+#define GVC_MIXER_SINK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))
+#define GVC_IS_MIXER_SINK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK))
+#define GVC_IS_MIXER_SINK_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK))
+#define GVC_MIXER_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass))
+
+typedef struct GvcMixerSinkPrivate GvcMixerSinkPrivate;
+
+typedef struct
+{
+        GvcMixerStream       parent;
+        GvcMixerSinkPrivate *priv;
+} GvcMixerSink;
+
+typedef struct
+{
+        GvcMixerStreamClass parent_class;
+} GvcMixerSinkClass;
+
+GType               gvc_mixer_sink_get_type            (void);
+
+GvcMixerStream *    gvc_mixer_sink_new                 (pa_context *context,
+                                                        guint       index,
+                                                        guint       num_channels);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_SINK_H */

Added: trunk/gnome-volume-control/src/gvc-mixer-stream.c
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-stream.c	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,450 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 William Jon McCann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "gvc-mixer-stream.h"
+
+#define GVC_MIXER_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamPrivate))
+
+static guint32 stream_serial = 1;
+
+struct GvcMixerStreamPrivate
+{
+        pa_context    *pa_context;
+        guint          id;
+        guint          index;
+        guint          num_channels;
+        guint          volume;
+        char          *name;
+        char          *icon_name;
+        gboolean       is_muted;
+        gboolean       is_default;
+};
+
+enum
+{
+        PROP_0,
+        PROP_ID,
+        PROP_PA_CONTEXT,
+        PROP_NUM_CHANNELS,
+        PROP_INDEX,
+        PROP_NAME,
+        PROP_ICON_NAME,
+        PROP_VOLUME,
+        PROP_IS_MUTED,
+        PROP_IS_DEFAULT,
+};
+
+static void     gvc_mixer_stream_class_init (GvcMixerStreamClass *klass);
+static void     gvc_mixer_stream_init       (GvcMixerStream      *mixer_stream);
+static void     gvc_mixer_stream_finalize   (GObject            *object);
+
+G_DEFINE_ABSTRACT_TYPE (GvcMixerStream, gvc_mixer_stream, G_TYPE_OBJECT)
+
+static guint32
+get_next_stream_serial (void)
+{
+        guint32 serial;
+
+        serial = stream_serial++;
+
+        if ((gint32)stream_serial < 0) {
+                stream_serial = 1;
+        }
+
+        return serial;
+}
+
+pa_context *
+gvc_mixer_stream_get_pa_context (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+        return stream->priv->pa_context;
+}
+
+guint
+gvc_mixer_stream_get_index (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+        return stream->priv->index;
+}
+
+guint
+gvc_mixer_stream_get_id (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+        return stream->priv->id;
+}
+
+guint
+gvc_mixer_stream_get_num_channels (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+        return stream->priv->num_channels;
+}
+
+guint
+gvc_mixer_stream_get_volume (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0);
+        return stream->priv->volume;
+}
+
+gboolean
+gvc_mixer_stream_set_volume (GvcMixerStream *stream,
+                             pa_volume_t     volume)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        if (volume != stream->priv->volume) {
+                stream->priv->volume = volume;
+                g_object_notify (G_OBJECT (stream), "volume");
+        }
+
+        return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_get_is_muted  (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+        return stream->priv->is_muted;
+}
+
+gboolean
+gvc_mixer_stream_get_is_default  (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+        return stream->priv->is_default;
+}
+
+gboolean
+gvc_mixer_stream_set_is_muted  (GvcMixerStream *stream,
+                                gboolean        is_muted)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        if (is_muted != stream->priv->is_muted) {
+                stream->priv->is_muted = is_muted;
+                g_object_notify (G_OBJECT (stream), "is-muted");
+        }
+
+        return TRUE;
+}
+
+gboolean
+gvc_mixer_stream_set_is_default  (GvcMixerStream *stream,
+                                  gboolean        is_default)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        if (is_default != stream->priv->is_default) {
+                stream->priv->is_default = is_default;
+                g_object_notify (G_OBJECT (stream), "is-default");
+        }
+
+        return TRUE;
+}
+
+char *
+gvc_mixer_stream_get_name (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+        return stream->priv->name;
+}
+
+gboolean
+gvc_mixer_stream_set_name (GvcMixerStream *stream,
+                           const char     *name)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        g_free (stream->priv->name);
+        stream->priv->name = g_strdup (name);
+        g_object_notify (G_OBJECT (stream), "name");
+
+        return TRUE;
+}
+
+char *
+gvc_mixer_stream_get_icon_name (GvcMixerStream *stream)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL);
+        return stream->priv->icon_name;
+}
+
+gboolean
+gvc_mixer_stream_set_icon_name (GvcMixerStream *stream,
+                                const char     *icon_name)
+{
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+
+        g_free (stream->priv->icon_name);
+        stream->priv->icon_name = g_strdup (icon_name);
+        g_object_notify (G_OBJECT (stream), "icon-name");
+
+        return TRUE;
+}
+
+static void
+gvc_mixer_stream_set_property (GObject       *object,
+                               guint          prop_id,
+                               const GValue  *value,
+                               GParamSpec    *pspec)
+{
+        GvcMixerStream *self = GVC_MIXER_STREAM (object);
+
+        switch (prop_id) {
+        case PROP_PA_CONTEXT:
+                self->priv->pa_context = g_value_get_pointer (value);
+                break;
+        case PROP_INDEX:
+                self->priv->index = g_value_get_ulong (value);
+                break;
+        case PROP_ID:
+                self->priv->id = g_value_get_ulong (value);
+                break;
+        case PROP_NUM_CHANNELS:
+                self->priv->num_channels = g_value_get_ulong (value);
+                break;
+        case PROP_NAME:
+                gvc_mixer_stream_set_name (self, g_value_get_string (value));
+                break;
+        case PROP_ICON_NAME:
+                gvc_mixer_stream_set_icon_name (self, g_value_get_string (value));
+                break;
+        case PROP_VOLUME:
+                gvc_mixer_stream_set_volume (self, g_value_get_ulong (value));
+                break;
+        case PROP_IS_MUTED:
+                gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value));
+                break;
+        case PROP_IS_DEFAULT:
+                gvc_mixer_stream_set_is_default (self, g_value_get_boolean (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+gvc_mixer_stream_get_property (GObject     *object,
+                               guint        prop_id,
+                               GValue      *value,
+                               GParamSpec  *pspec)
+{
+        GvcMixerStream *self = GVC_MIXER_STREAM (object);
+
+        switch (prop_id) {
+        case PROP_PA_CONTEXT:
+                g_value_set_pointer (value, self->priv->pa_context);
+                break;
+        case PROP_INDEX:
+                g_value_set_ulong (value, self->priv->index);
+                break;
+        case PROP_ID:
+                g_value_set_ulong (value, self->priv->id);
+                break;
+        case PROP_NUM_CHANNELS:
+                g_value_set_ulong (value, self->priv->num_channels);
+                break;
+        case PROP_NAME:
+                g_value_set_string (value, self->priv->name);
+                break;
+        case PROP_ICON_NAME:
+                g_value_set_string (value, self->priv->icon_name);
+                break;
+        case PROP_VOLUME:
+                g_value_set_ulong (value, self->priv->volume);
+                break;
+        case PROP_IS_MUTED:
+                g_value_set_boolean (value, self->priv->is_muted);
+                break;
+        case PROP_IS_DEFAULT:
+                g_value_set_boolean (value, self->priv->is_default);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static GObject *
+gvc_mixer_stream_constructor (GType                  type,
+                              guint                  n_construct_properties,
+                              GObjectConstructParam *construct_params)
+{
+        GObject       *object;
+        GvcMixerStream *self;
+
+        object = G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+        self = GVC_MIXER_STREAM (object);
+
+        self->priv->id = get_next_stream_serial ();
+
+        return object;
+}
+
+static gboolean
+gvc_mixer_stream_real_change_volume (GvcMixerStream *stream,
+                                     guint           volume)
+{
+        return FALSE;
+}
+
+static gboolean
+gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream,
+                                       gboolean        is_muted)
+{
+        return FALSE;
+}
+
+gboolean
+gvc_mixer_stream_change_volume (GvcMixerStream *stream,
+                                guint           volume)
+{
+        gboolean ret;
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_volume (stream, volume);
+        return ret;
+}
+
+gboolean
+gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
+                                  gboolean        is_muted)
+{
+        gboolean ret;
+        g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE);
+        ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_is_muted (stream, is_muted);
+        return ret;
+}
+
+static void
+gvc_mixer_stream_class_init (GvcMixerStreamClass *klass)
+{
+        GObjectClass   *gobject_class = G_OBJECT_CLASS (klass);
+
+        gobject_class->constructor = gvc_mixer_stream_constructor;
+        gobject_class->finalize = gvc_mixer_stream_finalize;
+        gobject_class->set_property = gvc_mixer_stream_set_property;
+        gobject_class->get_property = gvc_mixer_stream_get_property;
+
+        klass->change_volume = gvc_mixer_stream_real_change_volume;
+        klass->change_is_muted = gvc_mixer_stream_real_change_is_muted;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_INDEX,
+                                         g_param_spec_ulong ("index",
+                                                             "Index",
+                                                             "The index for this stream",
+                                                             0, G_MAXULONG, 0,
+                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+        g_object_class_install_property (gobject_class,
+                                         PROP_ID,
+                                         g_param_spec_ulong ("id",
+                                                             "id",
+                                                             "The id for this stream",
+                                                             0, G_MAXULONG, 0,
+                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+        g_object_class_install_property (gobject_class,
+                                         PROP_NUM_CHANNELS,
+                                         g_param_spec_ulong ("num-channels",
+                                                             "num channels",
+                                                             "The number of channels for this stream",
+                                                             0, G_MAXULONG, 0,
+                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+        g_object_class_install_property (gobject_class,
+                                         PROP_PA_CONTEXT,
+                                         g_param_spec_pointer ("pa-context",
+                                                               "PulseAudio context",
+                                                               "The PulseAudio context for this stream",
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
+        g_object_class_install_property (gobject_class,
+                                         PROP_VOLUME,
+                                         g_param_spec_ulong ("volume",
+                                                             "Volume",
+                                                             "The volume for this stream",
+                                                             0, G_MAXULONG, 0,
+                                                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_NAME,
+                                         g_param_spec_string ("name",
+                                                              "Name",
+                                                              "Name to display for this stream",
+                                                              NULL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (gobject_class,
+                                         PROP_ICON_NAME,
+                                         g_param_spec_string ("icon-name",
+                                                              "Icon Name",
+                                                              "Name of icon to display for this stream",
+                                                              NULL,
+                                                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (gobject_class,
+                                         PROP_IS_MUTED,
+                                         g_param_spec_boolean ("is-muted",
+                                                               "is muted",
+                                                               "Whether stream is muted",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+        g_object_class_install_property (gobject_class,
+                                         PROP_IS_DEFAULT,
+                                         g_param_spec_boolean ("is-default",
+                                                               "is default",
+                                                               "Whether stream is the default",
+                                                               FALSE,
+                                                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
+
+        g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate));
+}
+
+static void
+gvc_mixer_stream_init (GvcMixerStream *stream)
+{
+        stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream);
+
+}
+
+static void
+gvc_mixer_stream_finalize (GObject *object)
+{
+        GvcMixerStream *mixer_stream;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GVC_IS_MIXER_STREAM (object));
+
+        mixer_stream = GVC_MIXER_STREAM (object);
+
+        g_return_if_fail (mixer_stream->priv != NULL);
+        G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object);
+}

Added: trunk/gnome-volume-control/src/gvc-mixer-stream.h
==============================================================================
--- (empty file)
+++ trunk/gnome-volume-control/src/gvc-mixer-stream.h	Tue Nov  4 01:04:11 2008
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GVC_MIXER_STREAM_H
+#define __GVC_MIXER_STREAM_H
+
+#include <glib-object.h>
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+#define GVC_TYPE_MIXER_STREAM         (gvc_mixer_stream_get_type ())
+#define GVC_MIXER_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStream))
+#define GVC_MIXER_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))
+#define GVC_IS_MIXER_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_STREAM))
+#define GVC_IS_MIXER_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_STREAM))
+#define GVC_MIXER_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass))
+
+typedef struct GvcMixerStreamPrivate GvcMixerStreamPrivate;
+
+typedef struct
+{
+        GObject                parent;
+        GvcMixerStreamPrivate *priv;
+} GvcMixerStream;
+
+typedef struct
+{
+        GObjectClass           parent_class;
+
+        /* vtable */
+        gboolean (*change_volume)   (GvcMixerStream *stream,
+                                     guint           volume);
+        gboolean (*change_is_muted) (GvcMixerStream *stream,
+                                     gboolean        is_muted);
+} GvcMixerStreamClass;
+
+GType               gvc_mixer_stream_get_type        (void);
+
+pa_context *        gvc_mixer_stream_get_pa_context  (GvcMixerStream *stream);
+guint               gvc_mixer_stream_get_index       (GvcMixerStream *stream);
+guint               gvc_mixer_stream_get_id          (GvcMixerStream *stream);
+guint               gvc_mixer_stream_get_num_channels (GvcMixerStream *stream);
+gboolean            gvc_mixer_stream_get_is_default  (GvcMixerStream *stream);
+
+guint               gvc_mixer_stream_get_volume      (GvcMixerStream *stream);
+gboolean            gvc_mixer_stream_change_volume   (GvcMixerStream *stream,
+                                                      guint           volume);
+
+gboolean            gvc_mixer_stream_get_is_muted    (GvcMixerStream *stream);
+gboolean            gvc_mixer_stream_change_is_muted (GvcMixerStream *stream,
+                                                      gboolean        is_muted);
+char *              gvc_mixer_stream_get_name        (GvcMixerStream *stream);
+char *              gvc_mixer_stream_get_icon_name   (GvcMixerStream *stream);
+
+/* private */
+gboolean            gvc_mixer_stream_set_is_default  (GvcMixerStream *stream,
+                                                      gboolean        is_default);
+gboolean            gvc_mixer_stream_set_volume      (GvcMixerStream *stream,
+                                                      guint           volume);
+gboolean            gvc_mixer_stream_set_is_muted    (GvcMixerStream *stream,
+                                                      gboolean        is_muted);
+gboolean            gvc_mixer_stream_set_name        (GvcMixerStream *stream,
+                                                      const char     *name);
+gboolean            gvc_mixer_stream_set_icon_name   (GvcMixerStream *stream,
+                                                      const char     *name);
+
+G_END_DECLS
+
+#endif /* __GVC_MIXER_STREAM_H */

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Tue Nov  4 01:04:11 2008
@@ -16,6 +16,11 @@
 gnome-cd/gnome-cd.schemas.in.in
 gnome-cd/gst-cdrom.c
 gnome-cd/preferences.c
+gnome-volume-control/src/applet-main.c
+gnome-volume-control/src/dialog-main.c
+gnome-volume-control/src/gvc-applet.c
+gnome-volume-control/src/gvc-mixer-control.c
+gnome-volume-control/src/gvc-mixer-dialog.c
 grecord/gnome-sound-recorder.desktop.in.in
 grecord/gnome-sound-recorder.schemas.in.in
 grecord/src/gnome-recorder.c



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]