gnome-media r4019 - in trunk: . gnome-volume-control gnome-volume-control/src po
- From: mccann svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-media r4019 - in trunk: . gnome-volume-control gnome-volume-control/src po
- Date: Tue, 4 Nov 2008 01:04:12 +0000 (UTC)
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]