aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am84
-rw-r--r--src/common-defs.h4
-rw-r--r--src/dbus-shared-names.h47
-rw-r--r--src/indicator-sound.c209
-rw-r--r--src/sound-service-dbus.c141
-rw-r--r--src/sound-service-dbus.h70
-rw-r--r--src/sound-service.c255
-rw-r--r--src/sound-service.h71
-rw-r--r--src/sound-service.list1
-rw-r--r--src/sound-service.xml15
10 files changed, 897 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..bd9b2ad
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,84 @@
+libexec_PROGRAMS = indicator-sound-service
+
+###################
+# Indicator Stuff
+###################
+
+soundmenulibdir = $(INDICATORDIR)
+soundmenulib_LTLIBRARIES = libsoundmenu.la
+libsoundmenu_la_SOURCES = \
+ common-defs.h \
+ indicator-sound.c \
+ dbus-shared-names.h \
+ sound-service-client.h \
+ sound-service-marshal.c \
+ sound-service-marshal.h
+
+libsoundmenu_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror
+libsoundmenu_la_LIBADD = $(APPLET_LIBS)
+libsoundmenu_la_LDFLAGS = -module -avoid-version
+
+checkxml: $(srcdir)/sound-service.xml
+ @xmllint -valid -noout $<
+ @echo $< checks out ok
+
+sound-service-client.h: $(srcdir)/sound-service.xml
+ dbus-binding-tool \
+ --prefix=_sound_service_client \
+ --mode=glib-client \
+ --output=sound-service-client.h \
+ $(srcdir)/sound-service.xml
+
+sound-service-server.h: $(srcdir)/sound-service.xml
+ dbus-binding-tool \
+ --prefix=_sound_service_server \
+ --mode=glib-server \
+ --output=sound-service-server.h \
+ $(srcdir)/sound-service.xml
+
+sound-service-marshal.h: $(srcdir)/sound-service.list
+ glib-genmarshal --header \
+ --prefix=_sound_service_marshal $(srcdir)/sound-service.list \
+ > sound-service-marshal.h
+
+sound-service-marshal.c: $(srcdir)/sound-service.list
+ glib-genmarshal --body \
+ --prefix=_sound_service_marshal $(srcdir)/sound-service.list \
+ > sound-service-marshal.c
+
+
+#################
+# Session Stuff
+#################
+indicator_sound_service_SOURCES = \
+ common-defs.h \
+ sound-service.h \
+ sound-service.c \
+ sound-service-dbus.h \
+ sound-service-dbus.c \
+ sound-service-server.h \
+ sound-service-marshal.c \
+ sound-service-marshal.h
+indicator_sound_service_CFLAGS = $(SOUNDSERVICE_CFLAGS) $(GCONF_CFLAGS) -DLIBEXECDIR=\"$(libexecdir)\" -Wall -Werror
+indicator_sound_service_LDADD = $(SOUNDSERVICE_LIBS) $(GCONF_LIBS)
+
+###############
+# Other Stuff
+###############
+BUILT_SOURCES = \
+ sound-service-client.h \
+ sound-service-server.h \
+ sound-service-marshal.h \
+ sound-service-marshal.c
+
+EXTRA_DIST = \
+ sound-service.xml \
+ sound-service.list
+
+CLEANFILES = \
+ $(BUILT_SOURCES)
+
+
+
+
+
diff --git a/src/common-defs.h b/src/common-defs.h
new file mode 100644
index 0000000..3003bbb
--- /dev/null
+++ b/src/common-defs.h
@@ -0,0 +1,4 @@
+/* constants used for signals on the dbus. This file is shared between client and server implementation */
+
+#define SIGNAL_SINK_INPUT_WHILE_MUTED "SinkInputWhileMuted"
+
diff --git a/src/dbus-shared-names.h b/src/dbus-shared-names.h
new file mode 100644
index 0000000..124692a
--- /dev/null
+++ b/src/dbus-shared-names.h
@@ -0,0 +1,47 @@
+/*
+A small wrapper utility to load indicators and put them as menu items
+into the gnome-panel using it's applet interface.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __DBUS_SHARED_NAMES_H__
+#define __DBUS_SHARED_NAMES_H__ 1
+
+#define INDICATOR_STATUS_DBUS_NAME "org.ayatana.indicator.status"
+#define INDICATOR_STATUS_DBUS_OBJECT "/org/ayatana/indicator/status/menu"
+#define INDICATOR_STATUS_SERVICE_DBUS_OBJECT "/org/ayatana/indicator/status/service"
+#define INDICATOR_STATUS_SERVICE_DBUS_INTERFACE "org.ayatana.indicator.status.service"
+
+#define INDICATOR_USERS_DBUS_NAME "org.ayatana.indicator.users"
+#define INDICATOR_USERS_DBUS_OBJECT "/org/ayatana/indicator/users/menu"
+#define INDICATOR_USERS_SERVICE_DBUS_OBJECT "/org/gnome/DisplayManager/UserManager"
+#define INDICATOR_USERS_SERVICE_DBUS_INTERFACE "org.gnome.DisplayManager.UserManager"
+
+#define INDICATOR_SESSION_DBUS_NAME "org.ayatana.indicator.session"
+#define INDICATOR_SESSION_DBUS_OBJECT "/org/ayatana/indicator/session/menu"
+#define INDICATOR_SESSION_DBUS_VERSION 0
+
+#define INDICATOR_SOUND_DBUS_NAME "org.ayatana.indicator.sound"
+#define INDICATOR_SOUND_DBUS_OBJECT "/org/ayatana/indicator/sound/menu"
+#define INDICATOR_SOUND_SERVICE_DBUS_OBJECT "/org/ayatana/indicator/sound/service"
+#define INDICATOR_SOUND_SERVICE_DBUS_INTERFACE "org.ayatana.indicator.sound.service"
+#define INDICATOR_SOUND_DBUS_VERSION 0
+
+#endif /* __DBUS_SHARED_NAMES_H__ */
diff --git a/src/indicator-sound.c b/src/indicator-sound.c
new file mode 100644
index 0000000..64a6688
--- /dev/null
+++ b/src/indicator-sound.c
@@ -0,0 +1,209 @@
+/*
+A small wrapper utility to load indicators and put them as menu items
+into the gnome-panel using it's applet interface.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curra@canonical.com>
+ Ted Gould <ted@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <libdbusmenu-gtk/menu.h>
+/*#include <idoscalemenuitem.h>*/
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libindicator/indicator.h>
+#include <libindicator/indicator-object.h>
+#include <libindicator/indicator-service-manager.h>
+
+
+#include "dbus-shared-names.h"
+#include "sound-service-client.h"
+#include "common-defs.h"
+#include "sound-service-marshal.h"
+
+
+#define INDICATOR_SOUND_TYPE (indicator_sound_get_type ())
+#define INDICATOR_SOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_SOUND_TYPE, IndicatorSound))
+#define INDICATOR_SOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_SOUND_TYPE, IndicatorSoundClass))
+#define IS_INDICATOR_SOUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_SOUND_TYPE))
+#define IS_INDICATOR_SOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_SOUND_TYPE))
+#define INDICATOR_SOUND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_SOUND_TYPE, IndicatorSoundClass))
+
+typedef struct _IndicatorSound IndicatorSound;
+typedef struct _IndicatorSoundClass IndicatorSoundClass;
+
+struct _IndicatorSoundClass {
+ IndicatorObjectClass parent_class;
+};
+
+struct _IndicatorSound {
+ IndicatorObject parent;
+ IndicatorServiceManager * service;
+};
+
+GType indicator_sound_get_type (void);
+
+
+/* Indicator stuff */
+INDICATOR_SET_VERSION
+INDICATOR_SET_TYPE(INDICATOR_SOUND_TYPE)
+
+/* Prototypes */
+static GtkLabel * get_label (IndicatorObject * io);
+static GtkImage * get_icon (IndicatorObject * io);
+static GtkMenu * get_menu (IndicatorObject * io);
+//static GtkWidget *volume_item;
+static DBusGProxy * sound_dbus_proxy = NULL;
+
+
+static void indicator_sound_class_init (IndicatorSoundClass *klass);
+static void indicator_sound_init (IndicatorSound *self);
+static void indicator_sound_dispose (GObject *object);
+static void indicator_sound_finalize (GObject *object);
+
+G_DEFINE_TYPE (IndicatorSound, indicator_sound, INDICATOR_OBJECT_TYPE);
+
+static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata);
+static void catch_signal(DBusGProxy * proxy, gint sink_index, gboolean value, gpointer userdata);
+
+static void
+indicator_sound_class_init (IndicatorSoundClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = indicator_sound_dispose;
+ object_class->finalize = indicator_sound_finalize;
+
+ IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
+ io_class->get_label = get_label;
+ io_class->get_image = get_icon;
+ io_class->get_menu = get_menu;
+
+ dbus_g_object_register_marshaller (_sound_service_marshal_VOID__INT_BOOLEAN,
+ G_TYPE_NONE,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INVALID);
+
+ return;
+}
+
+static void indicator_sound_init (IndicatorSound *self)
+{
+ /* Set good defaults */
+ self->service = NULL;
+
+ /* Now let's fire these guys up. */
+ self->service = indicator_service_manager_new_version(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_VERSION);
+
+ g_signal_connect(G_OBJECT(self->service), INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_changed), self);
+
+
+ return;
+}
+
+static void
+connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata)
+{
+ if (connected) {
+ if (sound_dbus_proxy == NULL) {
+ GError * error = NULL;
+
+ DBusGConnection * sbus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+
+ sound_dbus_proxy = dbus_g_proxy_new_for_name_owner(sbus,
+ INDICATOR_SOUND_DBUS_NAME,
+ INDICATOR_SOUND_SERVICE_DBUS_OBJECT,
+ INDICATOR_SOUND_SERVICE_DBUS_INTERFACE,
+ &error);
+
+ if (error != NULL) {
+ g_warning("Unable to get status proxy: %s", error->message);
+ g_error_free(error);
+ }
+ g_debug("about to connect to the signals");
+ dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_INPUT_WHILE_MUTED, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_INPUT_WHILE_MUTED, G_CALLBACK(catch_signal), NULL, NULL);
+ }
+
+ } else {
+ //TODO : will need to handle this scenario
+ }
+
+ return;
+}
+
+static void catch_signal (DBusGProxy * proxy, gint sink_index, gboolean value, gpointer userdata)
+{
+ g_debug("signal caught - i don't believe it !");
+}
+
+
+static void
+indicator_sound_dispose (GObject *object)
+{
+ IndicatorSound * self = INDICATOR_SOUND(object);
+
+ if (self->service != NULL) {
+ g_object_unref(G_OBJECT(self->service));
+ self->service = NULL;
+ }
+
+
+ G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object);
+ return;
+}
+
+static void
+indicator_sound_finalize (GObject *object)
+{
+
+ G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object);
+ return;
+}
+
+static GtkLabel *
+get_label (IndicatorObject * io)
+{
+ return NULL;
+}
+
+static GtkImage *
+get_icon (IndicatorObject * io)
+{
+ GtkImage * status_image = GTK_IMAGE(gtk_image_new_from_icon_name("audio-volume-high", GTK_ICON_SIZE_MENU));
+ gtk_widget_show(GTK_WIDGET(status_image));
+ return status_image;
+}
+
+/* Indicator based function to get the menu for the whole
+ applet. This starts up asking for the parts of the menu
+ from the various services. */
+static GtkMenu *
+get_menu (IndicatorObject * io)
+{
+ //volume_item = ido_scale_menu_item_new_with_range ("Volume", 0, 100, 1);
+ //gtk_menu_shell_append (GTK_MENU_SHELL (menu), volume_item);
+ return GTK_MENU(dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT));
+}
+
+
diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c
new file mode 100644
index 0000000..920664e
--- /dev/null
+++ b/src/sound-service-dbus.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2010 Canonical Ltd.
+ *
+ * Authors:
+ * Conor Curran <conor.curran@canonical.com>
+ * Cody Russell <crussell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dbus/dbus-glib.h>
+#include "dbus-shared-names.h"
+#include "sound-service-dbus.h"
+#include "sound-service-client.h"
+#include "sound-service-server.h"
+#include "common-defs.h"
+#include "sound-service-marshal.h"
+
+
+typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate;
+
+struct _SoundServiceDbusPrivate
+{
+ DBusGConnection *system_bus;
+ DBusGConnection *connection;
+};
+
+/* Signals */
+enum {
+ SINK_INPUT_WHILE_MUTED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define SOUND_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusPrivate))
+
+
+static void sound_service_dbus_class_init (SoundServiceDbusClass *klass);
+static void sound_service_dbus_init (SoundServiceDbus *self);
+static void sound_service_dbus_dispose (GObject *object);
+static void sound_service_dbus_finalize (GObject *object);
+
+
+/* GObject Boilerplate */
+G_DEFINE_TYPE (SoundServiceDbus, sound_service_dbus, G_TYPE_OBJECT);
+
+static void
+sound_service_dbus_class_init (SoundServiceDbusClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof(SoundServiceDbusPrivate));
+
+ object_class->dispose = sound_service_dbus_dispose;
+ object_class->finalize = sound_service_dbus_finalize;
+
+ g_assert(klass != NULL);
+ dbus_g_object_type_install_info(SOUND_SERVICE_DBUS_TYPE,
+ &dbus_glib__sound_service_server_object_info);
+
+ signals[SINK_INPUT_WHILE_MUTED] = g_signal_new(SIGNAL_SINK_INPUT_WHILE_MUTED,
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _sound_service_marshal_VOID__INT_BOOLEAN,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BOOLEAN);
+}
+
+
+/**
+Utility methods to emit signals from the service into the ether.
+**/
+void sound_service_dbus_sink_input_while_muted(SoundServiceDbus* obj, gint sink_index, gboolean value){
+/* g_assert((num < LAST_SIGNAL) && (num >= 0));*/
+ g_debug("Emitting signal: SINK_INPUT_WHILE_MUTED, with sink_index %i and value %i", sink_index, value);
+ g_signal_emit(obj,
+ signals[SINK_INPUT_WHILE_MUTED],
+ 0,
+ sink_index,
+ value);
+}
+
+
+
+
+static void
+sound_service_dbus_init (SoundServiceDbus *self)
+{
+ GError *error = NULL;
+ SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self);
+
+ priv->system_bus = NULL;
+ priv->connection = NULL;
+
+ /* Get the system bus */
+ priv->system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ /* Put the object on DBus */
+ priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (error != NULL) {
+ g_error("Unable to connect to the session bus when creating application indicator: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ dbus_g_connection_register_g_object(priv->connection,
+ "/org/ayatana/indicator/sound/service",
+ G_OBJECT(self));
+
+ return;
+}
+
+static void
+sound_service_dbus_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (sound_service_dbus_parent_class)->dispose (object);
+ return;
+}
+
+static void
+sound_service_dbus_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (sound_service_dbus_parent_class)->finalize (object);
+ return;
+}
+
diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h
new file mode 100644
index 0000000..83f9c77
--- /dev/null
+++ b/src/sound-service-dbus.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 Canonical Ltd.
+ *
+ * Authors:
+ * Conor Curran <conor.curran@canonical.com>
+ * Cody Russell <crussell@canonical.com>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SOUND_SERVICE_DBUS_H__
+#define __SOUND_SERVICE_DBUS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define SOUND_SERVICE_DBUS_TYPE (sound_service_dbus_get_type ())
+#define SOUND_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbus))
+#define SOUND_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusClass))
+#define IS_SOUND_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUND_SERVICE_DBUS_TYPE))
+#define IS_SOUND_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SOUND_SERVICE_DBUS_TYPE))
+#define SOUND_SERVICE_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusClass))
+
+
+typedef struct _SoundServiceDbus SoundServiceDbus;
+typedef struct _SoundServiceDbusClass SoundServiceDbusClass;
+typedef struct _SoundData SoundData;
+
+struct _SoundData
+{
+ gchar *client_name;
+ gint64 sink_index;
+ gboolean *muted;
+
+ SoundServiceDbus *service;
+};
+
+struct _SoundServiceDbus {
+ GObject parent;
+};
+
+struct _SoundServiceDbusClass {
+ GObjectClass parent_class;
+ /* Signals -> outward messages*/
+ void (* sink_input_while_muted) (SoundServiceDbus *self, gint sink_index, gboolean is_muted, gpointer sound_data);
+};
+
+GType sound_service_dbus_get_type (void) G_GNUC_CONST;
+
+// Utility methods to get the messages across the into the static function space of sound-service-dbus
+void sound_service_dbus_sink_input_while_muted (SoundServiceDbus* obj, gint sink_index, gboolean value);
+
+//void sound_service_dbus_sink_volume_changed (SoundServiceDbus* obj, gint volume);
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/sound-service.c b/src/sound-service.c
new file mode 100644
index 0000000..dc43d77
--- /dev/null
+++ b/src/sound-service.c
@@ -0,0 +1,255 @@
+/*
+This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel.
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+ Ted Gould <ted@canonical.com>
+ Christoph Korn <c_korn@gmx.de>
+ Cody Russell <crussell@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "sound-service-dbus.h"
+#include "sound-service.h"
+#include "common-defs.h"
+
+/**********************************************************************************************************************/
+// Pulse-Audio asychronous call-backs
+/**********************************************************************************************************************/
+static void context_get_sink_info_by_index_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
+ if (eol > 0) {
+ return;
+ }
+ else{
+ g_debug("\n SINK INFO Name : %s \n", sink->name);
+ g_debug("\n SINK INFO Muted : %d \n", sink->mute);
+ if (sink->mute == 1){
+ g_debug("HERE is one for the DBUS - sink input while sink is muted");
+ sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, TRUE);
+ }
+ else{
+ g_debug("Sink input while the device is unmuted - not interested");
+ sound_service_dbus_sink_input_while_muted(dbus_interface, sink->index, FALSE);
+ }
+ }
+}
+
+static void context_success_callback(pa_context *c, int success, void *userdata){
+ g_debug("Context Success Callback - result = %i", success);
+}
+
+static void retrieve_complete_sink_list(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
+ if(eol > 0){
+ // TODO apparently never returns 0 sinks - Tested and it appears this assumption/prediction is correct.
+ // i would imagine different behaviour on different machines - watch this space!
+ // Some fuzzy reasoning might be needed.
+ if(sink_list->len == 1){
+ pa_sink_info* only_sink = (pa_sink_info*)g_ptr_array_index(sink_list, 0);
+ //TODO: sink is not null but its module is the null-module-sink!
+ // For now taking the easy route string compare on the name and the active port
+ // needs more testing
+ int value = g_strcasecmp(only_sink->name, " auto_null ");
+ g_debug("comparison outcome with auto_null is %i", value);
+ sink_available = (value != 0 && only_sink->active_port != NULL);
+ g_debug("Available sink is named %s", only_sink->name);
+ g_debug("does Available sink have an active port: %i", only_sink->active_port != NULL);
+ g_debug("sink_available = %i", sink_available);
+ return;
+ }
+ sink_available = TRUE;
+ return;
+ }
+ g_ptr_array_add(sink_list, (gpointer)sink);
+}
+
+
+static void set_global_mute_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata){
+ if(eol > 0){
+ g_debug("No more sinks to mute ! \n Everything should now be muted/unmuted ?" );
+ return;
+ }
+ // Otherwise mute/unmute it!
+ pa_context_set_sink_mute_by_index(pulse_context, sink->index, all_muted == TRUE ? 1 : 0, context_success_callback, NULL);
+}
+
+static void context_get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata){
+ if (eol > 0) {
+ return;
+ }
+ else{
+ if (info == NULL)
+ {
+ // TODO: watch this carefully - PA async api should not be doing this . . .
+ g_debug("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");
+ return;
+ }
+ g_debug("\n SINK INPUT INFO CALLBACK about to start asking questions...\n");
+ g_debug("\n SINK INPUT INFO Name : %s \n", info->name);
+ g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink);
+ pa_operation_unref(pa_context_get_sink_info_by_index(c, info->sink, context_get_sink_info_by_index_callback, userdata));
+ }
+}
+
+static void subscribed_events_callback(pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata){
+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ g_debug("Event sink for %i", index);
+ break;
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ // This will be triggered when the sink receives input from a new stream
+ // If a playback client is paused and then resumed this will NOT trigger this event.
+ g_debug("Subscribed_events_callback - type = sink input and index = %i", index);
+ g_debug("Sink input info query just about to happen");
+ pa_operation_unref(pa_context_get_sink_input_info(c, index, context_get_sink_input_info_callback, userdata));
+ g_debug("Sink input info query just happened");
+ break;
+ }
+}
+
+static void context_state_callback(pa_context *c, void *userdata) {
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_UNCONNECTED:
+ g_debug("unconnected");
+ break;
+ case PA_CONTEXT_CONNECTING:
+ g_debug("connecting");
+ break;
+ case PA_CONTEXT_AUTHORIZING:
+ g_debug("authorizing");
+ break;
+ case PA_CONTEXT_SETTING_NAME:
+ g_debug("context setting name");
+ break;
+ case PA_CONTEXT_FAILED:
+ g_debug("FAILED to retrieve context");
+ break;
+ case PA_CONTEXT_TERMINATED:
+ g_debug("context terminated");
+ break;
+ case PA_CONTEXT_READY:
+ g_debug("PA daemon is ready");
+ pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata);
+ pa_operation_unref(pa_context_get_sink_info_list(c, retrieve_complete_sink_list, NULL));
+ pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL));
+ //pa_operation_unref(pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL));
+ break;
+ }
+}
+
+/**********************************************************************************************************************/
+// Init functions (GTK and DBUS)
+/**********************************************************************************************************************/
+/**
+Pass to the g_idle_add method - returning False will ensure that this method is never called again as it is removed as an event source.
+**/
+static gboolean idle_routine (gpointer data)
+{
+ return FALSE;
+}
+
+/**
+Build the DBus menu items. For now Mute all/Unmute is the only available option
+**/
+static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
+{
+ mute_all_menuitem = dbusmenu_menuitem_new();
+
+ dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute"));
+ g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute), NULL);
+ //TODO: If no valid sinks are found grey out the item(s)
+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_SENSITIVE, sink_available);
+ dbusmenu_menuitem_child_append(root, mute_all_menuitem);
+}
+
+static void set_global_mute()
+{
+ all_muted = !all_muted;
+ g_debug("Mute is now = %i", all_muted);
+ pa_operation_unref(pa_context_get_sink_info_list(pulse_context, set_global_mute_callback, NULL));
+ dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(all_muted == FALSE ? "Mute All" : "Unmute"));
+}
+
+
+/* When the service interface starts to shutdown, we
+ should follow it. -
+*/
+void
+service_shutdown (IndicatorService *service, gpointer user_data)
+{
+
+ if (mainloop != NULL) {
+
+/* g_debug("Service shutdown");*/
+/* if (pulse_context){*/
+/* pa_context_unref(pulse_context);*/
+/* }*/
+/* g_ptr_array_free(sink_list, TRUE);*/
+/* pa_glib_mainloop_free(pa_main_loop);*/
+/* g_main_loop_quit(mainloop);*/
+ }
+ return;
+}
+
+
+/* Main, is well, main. It brings everything up and throws
+ us into the mainloop of no return. Some refactoring needed.*/
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+
+ IndicatorService * service = indicator_service_new_version(INDICATOR_SOUND_DBUS_NAME,
+ INDICATOR_SOUND_DBUS_VERSION);
+ g_signal_connect(G_OBJECT(service),
+ INDICATOR_SERVICE_SIGNAL_SHUTDOWN,
+ G_CALLBACK(service_shutdown), NULL);
+
+ root_menuitem = dbusmenu_menuitem_new();
+ g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
+
+ g_idle_add(idle_routine, root_menuitem);
+
+ sink_list = g_ptr_array_new();
+ dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL);
+
+ DbusmenuServer * server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT);
+ dbusmenu_server_set_root(server, root_menuitem);
+
+ pa_main_loop = pa_glib_mainloop_new(g_main_context_default());
+ g_assert(pa_main_loop);
+ pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound");
+ g_assert(pulse_context);
+
+
+ // Establish event callback registration
+ pa_context_set_state_callback(pulse_context, context_state_callback, NULL);
+ pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
+
+ rebuild_sound_menu (root_menuitem, dbus_interface);
+
+ // Run the loop
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ return 0;
+}
+
+
+
+
diff --git a/src/sound-service.h b/src/sound-service.h
new file mode 100644
index 0000000..0687ad0
--- /dev/null
+++ b/src/sound-service.h
@@ -0,0 +1,71 @@
+#ifndef __INCLUDE_SOUND_SERVICE_H__
+#define __INCLUDE_SOUND_SERVICE_H__
+
+/*
+This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel.
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@canonical.com>
+ Ted Gould <ted@canonical.com>
+ Christoph Korn <c_korn@gmx.de>
+ Cody Russell <crussell@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
+by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <libdbusmenu-glib/server.h>
+#include <libdbusmenu-glib/menuitem.h>
+#include <libdbusmenu-glib/client.h>
+
+#include <libindicator/indicator-service.h>
+
+#include <pulse/pulseaudio.h>
+#include <pulse/glib-mainloop.h>
+#include <pulse/error.h>
+#include <pulse/gccmacro.h>
+
+#include "dbus-shared-names.h"
+
+// GTK + DBUS
+static GMainLoop * mainloop = NULL;
+static DbusmenuMenuitem * root_menuitem = NULL;
+static DbusmenuMenuitem * mute_all_menuitem = NULL;
+static SoundServiceDbus * dbus_interface = NULL;
+
+// PULSEAUDIO
+static pa_context *pulse_context = NULL;
+static pa_glib_mainloop *pa_main_loop = NULL;
+static GPtrArray* sink_list = NULL;
+static gboolean sink_available = TRUE;
+
+static void context_state_callback(pa_context *c, void *userdata);
+static gboolean idle_routine (gpointer data);
+static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service);
+
+static gboolean all_muted = FALSE;
+static void set_global_mute();
+
+// ENTRY AND EXIT POINTS
+void service_shutdown(IndicatorService * service, gpointer user_data);
+int main (int argc, char ** argv);
+
+#endif
+
diff --git a/src/sound-service.list b/src/sound-service.list
new file mode 100644
index 0000000..749780f
--- /dev/null
+++ b/src/sound-service.list
@@ -0,0 +1 @@
+VOID:INT,BOOLEAN
diff --git a/src/sound-service.xml b/src/sound-service.xml
new file mode 100644
index 0000000..a0f6f57
--- /dev/null
+++ b/src/sound-service.xml
@@ -0,0 +1,15 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/ayatana/indicator/sound">
+ <interface name="org.ayatana.indicator.sound">
+<!-- <method name = "sink_volume">-->
+<!-- <arg type='i' name='index' direction="in"/>-->
+<!-- <arg type='i' name='volume' direction="out"/>-->
+<!-- </method>-->
+<!-- Triggered when a sink is muted but the input has been sent to that sink -->
+ <signal name="SinkInputWhileMuted">
+ <arg name="cur_value" type="i" direction="out"/>
+ <arg name="cur_value" type="b" direction="out"/>
+ </signal>
+ </interface>
+</node>
+