aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am9
-rw-r--r--src/active-sink.c293
-rw-r--r--src/active-sink.h72
-rw-r--r--src/mute-menu-item.c12
-rw-r--r--src/pulse-manager.c589
-rw-r--r--src/pulseaudio-mgr.c404
-rw-r--r--src/pulseaudio-mgr.h (renamed from src/pulse-manager.h)28
-rw-r--r--src/slider-menu-item.c46
-rw-r--r--src/slider-menu-item.h4
-rw-r--r--src/sound-service-dbus.c179
-rw-r--r--src/sound-service-dbus.h10
-rw-r--r--src/sound-service.c3
12 files changed, 841 insertions, 808 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ed64aeb..63d6d5a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -72,7 +72,6 @@ music_bridge_VALAFLAGS = \
--pkg gdk-pixbuf-2.0 \
--pkg libxml-2.0
-
$(MAINTAINER_VALAFLAGS)
music_bridge_APIFILES = \
@@ -89,8 +88,10 @@ indicator_sound_service_SOURCES = \
common-defs.h \
sound-service.h \
sound-service.c \
- pulse-manager.h \
- pulse-manager.c \
+ pulseaudio-mgr.h \
+ pulseaudio-mgr.c \
+ active-sink.c \
+ active-sink.h \
sound-service-dbus.h \
sound-service-dbus.c \
slider-menu-item.h \
@@ -108,7 +109,7 @@ indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_
# Service xml compilation
#########################
DBUS_SPECS = \
- sound-service.xml
+ sound-service.xml
gen-%.xml.h: %.xml
@echo "Building $@ from $<"
diff --git a/src/active-sink.c b/src/active-sink.c
new file mode 100644
index 0000000..b7954be
--- /dev/null
+++ b/src/active-sink.c
@@ -0,0 +1,293 @@
+/*
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@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 <libdbusmenu-glib/menuitem.h>
+
+#include "active-sink.h"
+#include "slider-menu-item.h"
+#include "mute-menu-item.h"
+
+#include "pulseaudio-mgr.h"
+
+typedef struct _ActiveSinkPrivate ActiveSinkPrivate;
+
+struct _ActiveSinkPrivate
+{
+ SliderMenuItem* volume_slider_menuitem;
+ MuteMenuItem* mute_menuitem;
+ SoundState current_sound_state;
+ SoundServiceDbus* service;
+ gint index;
+ gchar* name;
+ pa_cvolume volume;
+ pa_channel_map channel_map;
+ pa_volume_t base_volume;
+};
+
+#define ACTIVE_SINK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ACTIVE_SINK_TYPE, ActiveSinkPrivate))
+
+/* Prototypes */
+static void active_sink_class_init (ActiveSinkClass *klass);
+static void active_sink_init (ActiveSink *self);
+static void active_sink_dispose (GObject *object);
+static void active_sink_finalize (GObject *object);
+
+static SoundState active_sink_get_state_from_volume (ActiveSink* self);
+static pa_cvolume active_sink_construct_mono_volume (const pa_cvolume* vol);
+static void active_sink_volume_update (ActiveSink* self, gdouble percent);
+static void active_sink_mute_update (ActiveSink* self, gboolean muted);
+
+
+G_DEFINE_TYPE (ActiveSink, active_sink, G_TYPE_OBJECT);
+
+static void
+active_sink_class_init (ActiveSinkClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ActiveSinkPrivate));
+
+ gobject_class->dispose = active_sink_dispose;
+ gobject_class->finalize = active_sink_finalize;
+}
+
+static void
+active_sink_init (ActiveSink *self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ priv->mute_menuitem = NULL;
+ priv->volume_slider_menuitem = NULL;
+ priv->current_sound_state = UNAVAILABLE;
+ priv->index = -1;
+ priv->name = NULL;
+ priv->service = NULL;
+
+ // Init our menu items.
+ priv->mute_menuitem = g_object_new (MUTE_MENU_ITEM_TYPE, NULL);
+ priv->volume_slider_menuitem = slider_menu_item_new (self);
+ mute_menu_item_enable (priv->mute_menuitem, FALSE);
+ slider_menu_item_enable (priv->volume_slider_menuitem, FALSE);
+}
+
+static void
+active_sink_dispose (GObject *object)
+{
+ G_OBJECT_CLASS (active_sink_parent_class)->dispose (object);
+}
+
+static void
+active_sink_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (active_sink_parent_class)->finalize (object);
+}
+
+void
+active_sink_populate (ActiveSink* sink,
+ const pa_sink_info* update)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE(sink);
+
+ priv->name = g_strdup (update->name);
+ priv->index = update->index;
+ active_sink_mute_update (sink, update->mute);
+ priv->volume = active_sink_construct_mono_volume (&update->volume);
+ priv->base_volume = update->base_volume;
+ priv->channel_map = update->channel_map;
+
+ pa_volume_t vol = pa_cvolume_max (&update->volume);
+ gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
+
+ active_sink_volume_update (sink, volume_percent);
+ active_sink_mute_update (sink, update->mute);
+ mute_menu_item_enable (priv->mute_menuitem, TRUE);
+ slider_menu_item_enable (priv->volume_slider_menuitem, TRUE);
+
+ g_debug ("Active sink has been populated - volume %f", volume_percent);
+}
+
+void
+active_sink_update (ActiveSink* sink,
+ const pa_sink_info* update)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (sink);
+ active_sink_mute_update (sink, update->mute);
+ priv->volume = active_sink_construct_mono_volume (&update->volume);
+ priv->base_volume = update->base_volume;
+ priv->channel_map = update->channel_map;
+
+ pa_volume_t vol = pa_cvolume_max (&update->volume);
+ gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
+
+ active_sink_volume_update (sink, volume_percent);
+ active_sink_mute_update (sink, update->mute);
+}
+
+// To the UI
+static void
+active_sink_volume_update (ActiveSink* self, gdouble percent)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ slider_menu_item_update (priv->volume_slider_menuitem, percent);
+ SoundState state = active_sink_get_state_from_volume (self);
+ if (priv->current_sound_state != state){
+ priv->current_sound_state = state;
+ sound_service_dbus_update_sound_state (priv->service,
+ priv->current_sound_state);
+ }
+}
+
+// From the UI
+void
+active_sink_update_volume (ActiveSink* self, gdouble percent)
+{
+ pa_cvolume new_volume;
+ pa_cvolume_init(&new_volume);
+ new_volume.channels = 1;
+ pa_volume_t new_volume_value = (pa_volume_t) ((percent * PA_VOLUME_NORM) / 100);
+ pa_cvolume_set(&new_volume, 1, new_volume_value);
+
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+
+ pa_cvolume_set(&priv->volume, priv->channel_map.channels, new_volume_value);
+ pm_update_volume (priv->index, new_volume);
+}
+
+
+static void
+active_sink_mute_update (ActiveSink* self, gboolean muted)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ mute_menu_item_update (priv->mute_menuitem, muted);
+ SoundState state = active_sink_get_state_from_volume (self);
+
+ if (muted == TRUE){
+ state = MUTED;
+ }
+ if (priv->current_sound_state != state){
+ priv->current_sound_state = state;
+ sound_service_dbus_update_sound_state (priv->service, state);
+ }
+}
+
+void
+active_sink_ensure_sink_is_unmuted (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ if (mute_menu_item_is_muted (priv->mute_menuitem)){
+ pm_update_mute (FALSE);
+ }
+}
+
+
+static SoundState
+active_sink_get_state_from_volume (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ GVariant* v = dbusmenu_menuitem_property_get_variant (DBUSMENU_MENUITEM(priv->volume_slider_menuitem),
+ DBUSMENU_VOLUME_MENUITEM_LEVEL);
+ gdouble volume_percent = g_variant_get_double (v);
+
+ SoundState state = LOW_LEVEL;
+
+ if (volume_percent < 30.0 && volume_percent > 0) {
+ state = LOW_LEVEL;
+ }
+ else if (volume_percent < 70.0 && volume_percent >= 30.0) {
+ state = MEDIUM_LEVEL;
+ }
+ else if (volume_percent >= 70.0) {
+ state = HIGH_LEVEL;
+ }
+ else if (volume_percent == 0.0) {
+ state = ZERO_LEVEL;
+ }
+ return state;
+}
+
+static pa_cvolume
+active_sink_construct_mono_volume (const pa_cvolume* vol)
+{
+ pa_cvolume new_volume;
+ pa_cvolume_init(&new_volume);
+ new_volume.channels = 1;
+ pa_volume_t max_vol = pa_cvolume_max(vol);
+ pa_cvolume_set(&new_volume, 1, max_vol);
+ return new_volume;
+}
+
+void
+active_sink_determine_blocking_state (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ if (mute_menu_item_is_muted (priv->mute_menuitem)){
+ /**
+ We don't want to set the current state to blocking
+ as this is a fire and forget event.
+ */
+ sound_service_dbus_update_sound_state (priv->service,
+ BLOCKED);
+ }
+}
+
+gint
+active_sink_get_index (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ return priv->index;
+}
+
+gboolean
+active_sink_is_populated (ActiveSink* sink)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (sink);
+ return (priv->index != -1);
+}
+
+void
+active_sink_deactivate (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ priv->current_sound_state = UNAVAILABLE;
+ sound_service_dbus_update_sound_state (priv->service,
+ priv->current_sound_state);
+ mute_menu_item_enable (priv->mute_menuitem, FALSE);
+ slider_menu_item_enable (priv->volume_slider_menuitem, FALSE);
+ priv->index = -1;
+ g_free(priv->name);
+ priv->name = NULL;
+}
+
+SoundState
+active_sink_get_state (ActiveSink* self)
+{
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (self);
+ return priv->current_sound_state;
+}
+
+ActiveSink*
+active_sink_new (SoundServiceDbus* service)
+{
+ ActiveSink* sink = g_object_new (ACTIVE_SINK_TYPE, NULL);
+ ActiveSinkPrivate* priv = ACTIVE_SINK_GET_PRIVATE (sink);
+ priv->service = service;
+ sound_service_dbus_build_sound_menu (service,
+ mute_menu_item_get_button (priv->mute_menuitem),
+ DBUSMENU_MENUITEM (priv->volume_slider_menuitem));
+ pm_establish_pulse_connection (sink);
+ return sink;
+}
diff --git a/src/active-sink.h b/src/active-sink.h
new file mode 100644
index 0000000..ab05ebc
--- /dev/null
+++ b/src/active-sink.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 Canonical Ltd.
+ *
+ * Authors:
+ * Conor Curran <conor.curran@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 __ACTIVE_SINK_H__
+#define __ACTIVE_SINK_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "common-defs.h"
+#include "sound-service-dbus.h"
+
+#include <pulse/pulseaudio.h>
+
+G_BEGIN_DECLS
+
+#define ACTIVE_SINK_TYPE (active_sink_get_type ())
+#define ACTIVE_SINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ACTIVE_SINK_TYPE, ActiveSink))
+#define ACTIVE_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), ACTIVE_SINK_TYPE, ActiveSinkClass))
+#define IS_ACTIVE_SINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ACTIVE_SINK_TYPE))
+#define IS_ACTIVE_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ACTIVE_SINK_TYPE))
+#define ACTIVE_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ACTIVE_SINK_TYPE, ActiveSinkClass))
+
+typedef struct _ActiveSink ActiveSink;
+typedef struct _ActiveSinkClass ActiveSinkClass;
+
+struct _ActiveSink {
+ GObject parent;
+};
+
+struct _ActiveSinkClass {
+ GObjectClass parent_class;
+};
+
+GType active_sink_get_type (void) G_GNUC_CONST;
+
+void active_sink_populate (ActiveSink* sink, const pa_sink_info* update);
+void active_sink_update (ActiveSink* sink, const pa_sink_info* update);
+
+gboolean active_sink_is_populated (ActiveSink* sink);
+void active_sink_determine_blocking_state (ActiveSink* self);
+
+gint active_sink_get_index (ActiveSink* self);
+SoundState active_sink_get_state (ActiveSink* self);
+
+void active_sink_deactivate (ActiveSink* self);
+
+void active_sink_update_mute (ActiveSink* self, gboolean mute_update);
+void active_sink_update_volume (ActiveSink* self, gdouble percent);
+void active_sink_ensure_sink_is_unmuted (ActiveSink* self);
+
+ActiveSink* active_sink_new (SoundServiceDbus* service);
+
+G_END_DECLS
+
+#endif
diff --git a/src/mute-menu-item.c b/src/mute-menu-item.c
index f7f3824..8409b9f 100644
--- a/src/mute-menu-item.c
+++ b/src/mute-menu-item.c
@@ -24,7 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "common-defs.h"
#include "mute-menu-item.h"
-#include "pulse-manager.h"
+#include "pulseaudio-mgr.h"
typedef struct _MuteMenuItemPrivate MuteMenuItemPrivate;
@@ -62,6 +62,9 @@ mute_menu_item_init (MuteMenuItem *self)
g_debug("Building new Mute Menu Item");
MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE(self);
priv->button = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set_bool (priv->button,
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ TRUE);
g_signal_connect (G_OBJECT (priv->button),
DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
@@ -90,10 +93,8 @@ mute_menu_item_set_global_mute_from_ui (gpointer user_data)
DbusmenuMenuitem* button = DBUSMENU_MENUITEM (user_data);
gboolean current_value = dbusmenu_menuitem_property_get_bool (button,
DBUSMENU_MUTE_MENUITEM_VALUE);
-
gboolean new_value = !current_value;
- // pa manager api - to be refactored
- toggle_global_mute (new_value);
+ pm_update_mute (new_value);
}
void
@@ -113,6 +114,9 @@ void
mute_menu_item_enable (MuteMenuItem* item, gboolean active)
{
MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item);
+ dbusmenu_menuitem_property_set_bool (priv->button,
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ TRUE);
dbusmenu_menuitem_property_set_bool (priv->button,
DBUSMENU_MENUITEM_PROP_ENABLED,
diff --git a/src/pulse-manager.c b/src/pulse-manager.c
deleted file mode 100644
index 457992b..0000000
--- a/src/pulse-manager.c
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
-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.curran@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 <pulse/glib-mainloop.h>
-#include <pulse/error.h>
-#include <pulse/gccmacro.h>
-
-#include "pulse-manager.h"
-
-#define RECONNECT_DELAY 5
-
-static GHashTable *sink_hash = NULL;
-static SoundServiceDbus *dbus_service = NULL;
-static gint DEFAULT_SINK_INDEX = -1;
-static gboolean pa_server_available = FALSE;
-static gint reconnect_idle_id = 0;
-static pa_context *pulse_context = NULL;
-static pa_glib_mainloop *pa_main_loop = NULL;
-
-static void context_state_callback(pa_context *c, void *userdata);
-static gboolean reconnect_to_pulse();
-static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink_info, int eol, void *userdata);
-static void context_success_callback(pa_context *c, int success, void *userdata);
-static void pulse_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata);
-static void pulse_server_info_callback(pa_context *c, const pa_server_info *info, void *userdata);
-static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, void *userdata);
-static void destroy_sink_info(void *value);
-static gboolean determine_sink_availability();
-
-static gboolean has_volume_changed(const pa_sink_info* new_sink, sink_info* cached_sink);
-static pa_cvolume construct_mono_volume(const pa_cvolume* vol);
-
-/**
-Future Refactoring notes
- - rewrite in vala.
- - make sure all state is kept in the service for volume icon switching.
-**/
-
-/**
-Entry point
-**/
-void establish_pulse_activities(SoundServiceDbus *service)
-{
- dbus_service = service;
- 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),
- "com.canonical.indicators.sound");
- g_assert(pulse_context);
-
- sink_hash = g_hash_table_new_full(g_direct_hash,
- g_direct_equal,
- NULL,
- destroy_sink_info);
-
- // Establish event callback registration
- pa_context_set_state_callback (pulse_context, context_state_callback, NULL);
- sound_service_dbus_update_pa_state (dbus_service, FALSE, FALSE, 0);
- pa_context_connect (pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
-}
-
-/**
-get_context()
-Needed for testing - bah!
-**/
-pa_context* get_context()
-{
- return pulse_context;
-}
-
-static gboolean
-reconnect_to_pulse()
-{
- g_debug("Attempt to reconnect to pulse");
- // reset
- if (pulse_context != NULL) {
- pa_context_unref(pulse_context);
- pulse_context = NULL;
- }
-
- if (sink_hash != NULL) {
- g_hash_table_destroy(sink_hash);
- sink_hash = NULL;
- }
- pulse_context = pa_context_new( pa_glib_mainloop_get_api( pa_main_loop ),
- "com.canonical.indicators.sound" );
- g_assert(pulse_context);
- sink_hash = g_hash_table_new_full( g_direct_hash, g_direct_equal,
- NULL,
- destroy_sink_info );
- // Establish event callback registration
- pa_context_set_state_callback (pulse_context, context_state_callback, NULL);
- int result = pa_context_connect (pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
-
- if (result < 0) {
- g_warning ("Failed to connect context: %s",
- pa_strerror (pa_context_errno (pulse_context)));
- }
- // we always want to cancel any continious callbacks with the existing timeout
- // if the connection failed the new context created above will catch any updates
- // to do with the state of pulse and thus take care of business.
- reconnect_idle_id = 0;
- return FALSE;
-}
-
-/**
-close_pulse_activites()
-Gracefully close our connection with the Pulse async library.
-**/
-void close_pulse_activites()
-{
- if (pulse_context != NULL) {
- /* g_debug("freeing the pulse context");*/
- pa_context_unref(pulse_context);
- pulse_context = NULL;
- }
- g_hash_table_destroy(sink_hash);
- pa_glib_mainloop_free(pa_main_loop);
- pa_main_loop = NULL;
- /* g_debug("I just closed communication with Pulse");*/
-}
-
-/**
-destroy_sink_info()
-item destructor method for the sink_info hash
-**/
-static void destroy_sink_info(void *value)
-{
- sink_info *sink = (sink_info*)value;
- g_free(sink->name);
- g_free(sink);
-}
-
-/*
-Controllers & Utilities
-*/
-static gboolean determine_sink_availability()
-{
- // Firstly check to see if we have any sinks
- // if not get the hell out of here !
- if (g_hash_table_size(sink_hash) < 1) {
- g_debug("Sink_available returning false because sinks_hash is empty !!!");
- DEFAULT_SINK_INDEX = -1;
- return FALSE;
- }
- // Secondly, make sure the default sink index is set
- // If the default sink index has not been set
- // (via the server or has been reset because default sink has been removed),
- // it will attempt to set it to the value of the first
- // index in the array of keys from the sink_hash.
- GList* keys = g_hash_table_get_keys(sink_hash);
- GList* key = g_list_first(keys);
-
- DEFAULT_SINK_INDEX = (DEFAULT_SINK_INDEX < 0) ? GPOINTER_TO_INT(key->data) : DEFAULT_SINK_INDEX;
-
- // Thirdly ensure the default sink index does not have the name "auto_null"
- sink_info* s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX));
- // Up until now the most robust method to test this is to manually remove the available sink device
- // kernel module and then reload (rmmod & modprobe).
- // TODO: Edge case of dynamic loading and unloading of sinks should be handled also.
- /* g_debug("About to test for to see if the available sink is null - s->name = %s", s->name);*/
- gboolean available = g_ascii_strncasecmp("auto_null", s->name, 9) != 0;
- /* g_debug("PA_Manager -> determine_sink_availability: %i", available);*/
- return available;
-}
-
-gboolean default_sink_is_muted()
-{
- if (DEFAULT_SINK_INDEX < 0)
- return FALSE;
- if (g_hash_table_size(sink_hash) < 1)
- return FALSE;
- sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX));
- return s->mute;
-}
-
-static void check_sink_input_while_muted_event(gint sink_index)
-{
- /* g_debug("SINKINPUTWHILEMUTED SIGNAL EVENT TO BE SENT FROM PA MANAGER - check trace for value");*/
-
- if (default_sink_is_muted(sink_index) == TRUE) {
- sound_service_dbus_update_sound_state(dbus_service, BLOCKED);
- }
-// Why do you need to send a false for a blocked event, it times out after 5 secs anyway
-//} else {
- // sound_service_dbus_sink_input_while_muted(dbus_service, FALSE);
- //}
-}
-
-static gdouble get_default_sink_volume()
-{
- if (DEFAULT_SINK_INDEX < 0)
- return 0;
- sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX));
- pa_volume_t vol = pa_cvolume_avg(&s->volume);
- gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
- /* g_debug("software volume = %f", volume_percent);*/
- return volume_percent;
-}
-
-static void mute_each_sink(gpointer key, gpointer value, gpointer user_data)
-{
- sink_info *info = (sink_info*)value;
- pa_operation_unref(pa_context_set_sink_mute_by_index(pulse_context, info->index, GPOINTER_TO_INT(user_data), context_success_callback, NULL));
- if (GPOINTER_TO_INT(user_data) == 1) {
- sound_service_dbus_update_sink_mute(dbus_service, TRUE);
- } else {
- sound_service_dbus_update_volume(dbus_service, get_default_sink_volume());
- }
-}
-
-void toggle_global_mute(gboolean mute_value)
-{
- g_hash_table_foreach(sink_hash, mute_each_sink, GINT_TO_POINTER(mute_value));
- /* g_debug("in the pulse manager: toggle global mute value %i", mute_value);*/
-}
-
-
-/*
-Refine the resolution of the slider or binary scale it to achieve a more subtle volume control.
-Use the base volume stored in the sink struct to calculate actual linear volumes.
-*/
-void set_sink_volume(gdouble percent)
-{
- if (pa_server_available == FALSE)
- return;
- /* g_debug("in the pulse manager:set_sink_volume with percent %f", percent);*/
-
- if (DEFAULT_SINK_INDEX < 0) {
- g_warning("We have no default sink !!! - returning after not attempting to set any volume of any sink");
- return;
- }
-
- sink_info *cached_sink = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX));
-
- pa_cvolume new_volume;
- pa_cvolume_init(&new_volume);
- new_volume.channels = 1;
- pa_volume_t new_volume_value = (pa_volume_t) ((percent * PA_VOLUME_NORM) / 100);
- pa_cvolume_set(&new_volume, 1, new_volume_value);
- pa_cvolume_set(&cached_sink->volume, cached_sink->channel_map.channels, new_volume_value);
- pa_operation_unref(pa_context_set_sink_volume_by_index(pulse_context, DEFAULT_SINK_INDEX, &new_volume, NULL, NULL));
-}
-
-
-static pa_cvolume construct_mono_volume(const pa_cvolume* vol)
-{
- pa_cvolume new_volume;
- pa_cvolume_init(&new_volume);
- new_volume.channels = 1;
- pa_volume_t max_vol = pa_cvolume_max(vol);
- pa_cvolume_set(&new_volume, 1, max_vol);
- return new_volume;
-}
-
-/**********************************************************************************************************************/
-// Pulse-Audio asychronous call-backs
-/**********************************************************************************************************************/
-
-static void gather_pulse_information(pa_context *c, void *userdata)
-{
- pa_operation *operation;
- if (!(operation = pa_context_get_server_info(c, pulse_server_info_callback, userdata))) {
- g_warning("pa_context_get_server_info failed");
- if (!(operation = pa_context_get_sink_info_list(c, pulse_sink_info_callback, NULL))) {
- g_warning("pa_context_get_sink_info_list() failed - cannot fetch server or sink info - leaving . . .");
- return;
- }
- }
- pa_operation_unref(operation);
- return;
-}
-
-
-static void context_success_callback(pa_context *c, int success, void *userdata)
-{
- /* g_debug("Context Success Callback - result = %i", success);*/
-}
-
-/**
-On Service startup this callback will be called multiple times resulting our sinks_hash container to be filled with the
-available sinks.
-For now this callback assumes it only used at startup. It may be necessary to use if sinks become available after startup.
-Major candidate for refactoring.
-**/
-static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata)
-{
- if (eol > 0) {
-
- gboolean device_available = determine_sink_availability();
- if (device_available == TRUE) {
- sound_service_dbus_update_pa_state( dbus_service,
- device_available,
- default_sink_is_muted(),
- get_default_sink_volume() );
- } else {
- //Update the indicator to show PA either is not ready or has no available sink
- g_warning("Cannot find a suitable default sink ...");
- sound_service_dbus_update_pa_state( dbus_service,
- device_available,
- default_sink_is_muted(),
- get_default_sink_volume() );
- }
- } else {
- /* g_debug("About to add an item to our hash");*/
- sink_info *value;
- value = g_new0(sink_info, 1);
- value->index = sink->index;
- value->name = g_strdup(sink->name);
- value->mute = !!sink->mute;
- value->volume = construct_mono_volume(&sink->volume);
- value->base_volume = sink->base_volume;
- value->channel_map = sink->channel_map;
- g_hash_table_insert(sink_hash, GINT_TO_POINTER(sink->index), value);
- /* g_debug("After adding an item to our hash");*/
- }
-}
-
-static void pulse_default_sink_info_callback(pa_context *c, const pa_sink_info *info, int eol, void *userdata)
-{
- if (eol > 0) {
- return;
- } else {
- DEFAULT_SINK_INDEX = info->index;
- /* g_debug("Just set the default sink index to %i", DEFAULT_SINK_INDEX); */
- GList *keys = g_hash_table_get_keys(sink_hash);
- gint position = g_list_index(keys, GINT_TO_POINTER(info->index));
- // Only update sink-list if the index is not in our already fetched list.
- if (position < 0) {
- pa_operation_unref(pa_context_get_sink_info_list(c, pulse_sink_info_callback, NULL));
- } else {
- sound_service_dbus_update_pa_state(dbus_service,
- determine_sink_availability(),
- default_sink_is_muted(),
- get_default_sink_volume());
- }
- }
-}
-
-static void pulse_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata)
-{
- if (eol > 0) {
- if (pa_context_errno(c) == PA_ERR_NOENTITY)
- return;
- /* g_warning("Sink INPUT info callback failure");*/
- return;
- } else {
- if (info == NULL) {
- // TODO: watch this carefully - PA async api should not be doing this . . .
- /* g_warning("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");*/
- return;
- }
- /* g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink);*/
- check_sink_input_while_muted_event(info->sink);
- }
-}
-
-static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, void *userdata)
-{
- if (eol > 0) {
- if (pa_context_errno(c) == PA_ERR_NOENTITY)
- return;
- /* g_warning("Sink INPUT info callback failure");*/
- return;
- }
- gint position = -1;
- GList *keys = g_hash_table_get_keys(sink_hash);
-
- if (info == NULL)
- return;
-
- position = g_list_index(keys, GINT_TO_POINTER(info->index));
-
- if (position >= 0) { // => index is within the keys of the hash.
- sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(info->index));
- s->name = g_strdup(info->name);
- gboolean mute_changed = s->mute != !!info->mute;
- s->mute = !!info->mute;
- gboolean volume_changed = has_volume_changed(info, s);
-
- /* g_debug("new balance : %i", (int)(pa_cvolume_get_balance(&info->volume, &info->channel_map) * 100));*/
- /* g_debug("cached balance : %i", (int)(pa_cvolume_get_balance(&s->volume, &s->channel_map) * 100));*/
- /* g_debug("update_sink_info: new_volume input : %f", (gdouble)(pa_cvolume_max(&info->volume)));*/
- /* g_debug("update sink info: cached volume is at: %f", (gdouble)(pa_cvolume_max(&s->volume)));*/
- /* g_debug("update sink info : volume changed = %i", volume_changed);*/
- /* g_debug("update sink info : compatibility = %i", pa_cvolume_compatible_with_channel_map(&info->volume, &s->channel_map));*/
-
- s->volume = construct_mono_volume(&info->volume);
- s->channel_map = info->channel_map;
-
- if (DEFAULT_SINK_INDEX == s->index) {
- //update the UI
- if (volume_changed == TRUE && s->mute == FALSE) {
- pa_volume_t vol = pa_cvolume_max(&s->volume);
- gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
- /* g_debug("Updating volume from PA manager with volume = %f", volume_percent);*/
- sound_service_dbus_update_volume(dbus_service, volume_percent);
- }
-
- if (mute_changed == TRUE) {
- /* g_debug("Updating Mute from PA manager with mute = %i", s->mute);*/
- sound_service_dbus_update_sink_mute(dbus_service, s->mute);
- if (s->mute == FALSE) {
- pa_volume_t vol = pa_cvolume_max(&s->volume);
- gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
- /* g_debug("Updating volume from PA manager with volume = %f", volume_percent);*/
- sound_service_dbus_update_volume(dbus_service, volume_percent);
- }
- }
- }
- } else {
- sink_info *value;
- value = g_new0(sink_info, 1);
- value->index = info->index;
- value->name = g_strdup(info->name);
- value->mute = !!info->mute;
- value->volume = construct_mono_volume(&info->volume);
- value->channel_map = info->channel_map;
- value->base_volume = info->base_volume;
- g_hash_table_insert(sink_hash, GINT_TO_POINTER(value->index), value);
- /* g_debug("pulse-manager:update_sink_info -> After adding a new sink to our hash");*/
- sound_service_dbus_update_sound_state(dbus_service, AVAILABLE);
- }
-}
-
-
-static gboolean has_volume_changed(const pa_sink_info* new_sink, sink_info* cached_sink)
-{
- if (pa_cvolume_compatible_with_channel_map(&new_sink->volume, &cached_sink->channel_map) == FALSE)
- return FALSE;
-
- pa_cvolume new_vol = construct_mono_volume(&new_sink->volume);
-
- if (pa_cvolume_equal(&new_vol, &(cached_sink->volume)) == TRUE) {
- /* g_debug("has_volume_changed: volumes appear to be equal? no change triggered!"); */
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-static void pulse_server_info_callback(pa_context *c,
- const pa_server_info *info,
- void *userdata)
-{
- /* g_debug("server info callback");*/
- pa_operation *operation;
- if (info == NULL) {
- g_warning("No server - get the hell out of here");
- sound_service_dbus_update_pa_state(dbus_service, FALSE, TRUE, 0);
- pa_server_available = FALSE;
- return;
- }
- pa_server_available = TRUE;
- if (info->default_sink_name != NULL) {
- if (!(operation = pa_context_get_sink_info_by_name(c,
- info->default_sink_name,
- pulse_default_sink_info_callback,
- userdata))) {
- g_warning("pa_context_get_sink_info_by_name() failed");
- } else {
- pa_operation_unref(operation);
- return;
- }
- }
- if (!(operation = pa_context_get_sink_info_list(c, pulse_sink_info_callback, NULL))) {
- g_warning("pa_context_get_sink_info_list() failed");
- return;
- }
- pa_operation_unref(operation);
-}
-
-
-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:
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
- if (index == DEFAULT_SINK_INDEX)
- sound_service_dbus_update_sound_state(dbus_service, UNAVAILABLE);
-
- /* g_debug("Subscribed_events_callback - removing sink of index %i from our sink hash - keep the cache tidy !", index);*/
- g_hash_table_remove(sink_hash, GINT_TO_POINTER(index));
-
- if (index == DEFAULT_SINK_INDEX) {
- /* g_debug("subscribed_events_callback - PA_SUBSCRIPTION_EVENT_SINK REMOVAL: default sink %i has been removed.", DEFAULT_SINK_INDEX); */
- DEFAULT_SINK_INDEX = -1;
- determine_sink_availability();
- }
- /* g_debug("subscribed_events_callback - Now what is our default sink : %i", DEFAULT_SINK_INDEX); */
- } else {
- /* g_debug("subscribed_events_callback - PA_SUBSCRIPTION_EVENT_SINK: a generic sink event - will trigger an update"); */
- pa_operation_unref(pa_context_get_sink_info_by_index(c, index, update_sink_info, userdata));
- }
- break;
- case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
- /* g_debug("subscribed_events_callback - PA_SUBSCRIPTION_EVENT_SINK_INPUT event triggered!!");*/
- if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
- //handle the sink input remove event - not relevant for current design
- } else {
- pa_operation_unref(pa_context_get_sink_input_info(c, index, pulse_sink_input_info_callback, userdata));
- }
- break;
- case PA_SUBSCRIPTION_EVENT_SERVER:
- g_debug("subscribed_events_callback - PA_SUBSCRIPTION_EVENT_SERVER change of some description ???");
- pa_operation *o;
- if (!(o = pa_context_get_server_info(c, pulse_server_info_callback, userdata))) {
- g_warning("subscribed_events_callback - pa_context_get_server_info() failed");
- return;
- }
- pa_operation_unref(o);
- 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 - waiting for the server to become available");
- 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_warning("PA_CONTEXT_FAILED - Is PulseAudio Daemon running ?");
- pa_server_available = FALSE;
- sound_service_dbus_update_pa_state( dbus_service,
- pa_server_available,
- default_sink_is_muted(),
- get_default_sink_volume() );
-
- if (reconnect_idle_id == 0){
- reconnect_idle_id = g_timeout_add_seconds (RECONNECT_DELAY,
- reconnect_to_pulse,
- NULL);
- }
- break;
- case PA_CONTEXT_TERMINATED:
- /* g_debug("context terminated");*/
- break;
- case PA_CONTEXT_READY:
- g_debug("PA_CONTEXT_READY");
- pa_operation *o;
-
- pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata);
-
- if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
- (PA_SUBSCRIPTION_MASK_SINK|
- PA_SUBSCRIPTION_MASK_SINK_INPUT|
- PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) {
- g_warning("pa_context_subscribe() failed");
- return;
- }
- pa_operation_unref(o);
-
- gather_pulse_information(c, userdata);
-
- break;
- }
-}
-
diff --git a/src/pulseaudio-mgr.c b/src/pulseaudio-mgr.c
new file mode 100644
index 0000000..1a2b3e0
--- /dev/null
+++ b/src/pulseaudio-mgr.c
@@ -0,0 +1,404 @@
+/*
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ Conor Curran <conor.curran@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/>.
+*/
+
+/**Notes
+ *
+ * Approach now is to set up the communication channels then query the server
+ * fetch its default sink. If this fails then fetch the list of sinks and take
+ * the first one which is not the auto-null sink.
+ * TODO: need to handle the situation where one chink in this linear chain breaks
+ * i.e. start off the process again and count the attempts (note different to
+ reconnect attempts)
+ */
+#include <pulse/gccmacro.h>
+#include <pulse/glib-mainloop.h>
+#include <pulse/error.h>
+
+#include "pulseaudio-mgr.h"
+
+#define RECONNECT_DELAY 5
+
+
+static void pm_context_state_callback(pa_context *c, void *userdata);
+static void pm_subscribed_events_callback (pa_context *c,
+ enum pa_subscription_event_type t,
+ uint32_t index,
+ void* userdata);
+static void pm_server_info_callback (pa_context *c,
+ const pa_server_info *info,
+ void *userdata);
+static void pm_default_sink_info_callback (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata);
+static void pm_sink_info_callback (pa_context *c,
+ const pa_sink_info *sink,
+ int eol,
+ void *userdata);
+static void pm_sink_input_info_callback (pa_context *c,
+ const pa_sink_input_info *info,
+ int eol,
+ void *userdata);
+static void pm_update_active_sink (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata);
+static void pm_toggle_mute_for_every_sink_callback (pa_context *c,
+ const pa_sink_info *sink,
+ int eol,
+ void* userdata);
+
+static gboolean reconnect_to_pulse (gpointer user_data);
+
+static gint connection_attempts = 0;
+static gint reconnect_idle_id = 0;
+static pa_context *pulse_context = NULL;
+static pa_glib_mainloop *pa_main_loop = NULL;
+
+/**
+ Entry Point
+ **/
+void
+pm_establish_pulse_connection (ActiveSink* active_sink)
+{
+ pa_main_loop = pa_glib_mainloop_new (g_main_context_default ());
+ g_assert (pa_main_loop);
+ reconnect_to_pulse ((gpointer)active_sink);
+}
+
+/**
+close_pulse_activites()
+Gracefully close our connection with the Pulse async library.
+**/
+void close_pulse_activites()
+{
+ if (pulse_context != NULL) {
+ pa_context_unref(pulse_context);
+ pulse_context = NULL;
+ }
+ pa_glib_mainloop_free(pa_main_loop);
+ pa_main_loop = NULL;
+}
+
+/**
+reconnect_to_pulse (gpointer user_data)
+Method which connects to the pulse server and is used to track reconnects.
+ */
+static gboolean
+reconnect_to_pulse (gpointer user_data)
+{
+ g_debug("Attempt to reconnect to pulse");
+ // reset
+ connection_attempts += 1;
+ if (pulse_context != NULL) {
+ pa_context_unref(pulse_context);
+ pulse_context = NULL;
+ }
+
+ pulse_context = pa_context_new( pa_glib_mainloop_get_api( pa_main_loop ),
+ "com.canonical.indicators.sound" );
+ g_assert(pulse_context);
+ pa_context_set_state_callback (pulse_context,
+ pm_context_state_callback,
+ user_data);
+ int result = pa_context_connect (pulse_context,
+ NULL,
+ PA_CONTEXT_NOFAIL,
+ user_data);
+
+ if (result < 0) {
+ g_warning ("Failed to connect context: %s",
+ pa_strerror (pa_context_errno (pulse_context)));
+ }
+
+ reconnect_idle_id = 0;
+ if (connection_attempts > 5){
+ return FALSE;
+ }
+ else{
+ return TRUE;
+ }
+}
+
+void
+pm_update_volume (gint sink_index, pa_cvolume new_volume)
+{
+ pa_operation_unref (pa_context_set_sink_volume_by_index (pulse_context,
+ sink_index,
+ &new_volume,
+ NULL,
+ NULL) );
+}
+
+void
+pm_update_mute (gboolean update)
+{
+ pa_operation_unref (pa_context_get_sink_info_list (pulse_context,
+ pm_toggle_mute_for_every_sink_callback,
+ GINT_TO_POINTER (update)));
+}
+
+/**********************************************************************************************************************/
+// Pulse-Audio asychronous call-backs
+/**********************************************************************************************************************/
+
+
+static void
+pm_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:
+ if (IS_ACTIVE_SINK (userdata) == FALSE){
+ g_warning ("subscribed events callback - our userdata is not what we think it should be");
+ return;
+ }
+ ActiveSink* sink = ACTIVE_SINK (userdata);
+ // We don't care about any other sink other than the active one.
+ if (index != active_sink_get_index (sink))
+ return;
+
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+ active_sink_deactivate (ACTIVE_SINK (userdata));
+
+ }
+ else{
+ pa_operation_unref (pa_context_get_sink_info_by_index (c,
+ index,
+ pm_update_active_sink,
+ userdata) );
+ }
+ break;
+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+ // We don't care about sink input removals.
+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_REMOVE) {
+ pa_operation_unref (pa_context_get_sink_input_info (c,
+ index,
+ pm_sink_input_info_callback, userdata));
+ }
+ break;
+ case PA_SUBSCRIPTION_EVENT_SERVER:
+ g_debug("PA_SUBSCRIPTION_EVENT_SERVER event triggered.");
+ pa_operation *o;
+ if (!(o = pa_context_get_server_info (c, pm_server_info_callback, userdata))) {
+ g_warning("subscribed_events_callback - pa_context_get_server_info() failed");
+ return;
+ }
+ pa_operation_unref(o);
+ break;
+ }
+}
+
+
+static void
+pm_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 - waiting for the server to become available");
+ break;
+ case PA_CONTEXT_AUTHORIZING:
+ break;
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ case PA_CONTEXT_FAILED:
+ g_warning("PA_CONTEXT_FAILED - Is PulseAudio Daemon running ?");
+ active_sink_deactivate (ACTIVE_SINK (userdata));
+ if (reconnect_idle_id == 0){
+ reconnect_idle_id = g_timeout_add_seconds (RECONNECT_DELAY,
+ reconnect_to_pulse,
+ userdata);
+ }
+ break;
+ case PA_CONTEXT_TERMINATED:
+ break;
+ case PA_CONTEXT_READY:
+ connection_attempts = 0;
+ g_debug("PA_CONTEXT_READY");
+ pa_operation *o;
+
+ pa_context_set_subscribe_callback(c, pm_subscribed_events_callback, userdata);
+
+ if (!(o = pa_context_subscribe (c, (pa_subscription_mask_t)
+ (PA_SUBSCRIPTION_MASK_SINK|
+ PA_SUBSCRIPTION_MASK_SINK_INPUT|
+ PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) {
+ g_warning("pa_context_subscribe() failed");
+
+ }
+
+ if (!(o = pa_context_get_server_info (c, pm_server_info_callback, userdata))) {
+ g_warning("Initial - pa_context_get_server_info() failed");
+ }
+ pa_operation_unref(o);
+
+ break;
+ }
+}
+
+/**
+ After startup we go straight for the server info to see if it has details of
+ the default sink. If so it makes things much easier.
+ **/
+static void
+pm_server_info_callback (pa_context *c,
+ const pa_server_info *info,
+ void *userdata)
+{
+ pa_operation *operation;
+ g_debug ("server info callback");
+
+ if (info == NULL) {
+ g_warning("No PA server - get the hell out of here");
+ active_sink_deactivate (ACTIVE_SINK (userdata));
+ return;
+ }
+ if (info->default_sink_name != NULL) {
+ g_debug ("default sink name from the server ain't null'");
+ if (!(operation = pa_context_get_sink_info_by_name (c,
+ info->default_sink_name,
+ pm_default_sink_info_callback,
+ userdata) )) {
+ }
+ else{
+ pa_operation_unref(operation);
+ return;
+ }
+ }
+ else if (!(operation = pa_context_get_sink_info_list(c,
+ pm_sink_info_callback,
+ NULL))) {
+ g_warning("pa_context_get_sink_info_list() failed");
+ return;
+ }
+ pa_operation_unref(operation);
+}
+
+// If the server doesn't have a default sink to give us
+// we should attempt to pick up the first of the list of sinks which doesn't have
+// the name 'auto_null' (that was all really I was doing before)
+static void
+pm_sink_info_callback (pa_context *c,
+ const pa_sink_info *sink,
+ int eol,
+ void* userdata)
+{
+ if (eol > 0) {
+ return;
+ }
+ else {
+ if (IS_ACTIVE_SINK (userdata) == FALSE){
+ g_warning ("sink info callback - our user data is not what we think it should be");
+ return;
+ }
+ ActiveSink* a_sink = ACTIVE_SINK (userdata);
+ if (active_sink_is_populated (a_sink) == FALSE &&
+ g_ascii_strncasecmp("auto_null", sink->name, 9) != 0){
+ active_sink_populate (a_sink, sink);
+ }
+ }
+}
+
+static void
+pm_default_sink_info_callback (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata)
+{
+ if (eol > 0) {
+ return;
+ }
+ else {
+ if (IS_ACTIVE_SINK (userdata) == FALSE){
+ g_warning ("Default sink info callback - our user data is not what we think it should be");
+ return;
+ }
+ g_debug ("server has handed us a default sink");
+ active_sink_populate (ACTIVE_SINK (userdata), info);
+ }
+}
+
+static void
+pm_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_warning("\n Sink input info callback : SINK INPUT INFO IS NULL BUT EOL was not POSITIVE!!!");
+ return;
+ }
+ if (IS_ACTIVE_SINK (userdata) == FALSE){
+ g_warning ("sink input info callback - our user data is not what we think it should be");
+ return;
+ }
+
+ ActiveSink* a_sink = ACTIVE_SINK (userdata);
+ if (active_sink_get_index (a_sink) == info->sink){
+ active_sink_determine_blocking_state (a_sink);
+ }
+ }
+}
+
+static void
+pm_update_active_sink (pa_context *c,
+ const pa_sink_info *info,
+ int eol,
+ void *userdata)
+{
+ if (eol > 0) {
+ return;
+ }
+ else{
+ if (IS_ACTIVE_SINK (userdata) == FALSE){
+ g_warning ("update_active_sink - our user data is not what we think it should be");
+ return;
+ }
+ active_sink_update (ACTIVE_SINK(userdata), info);
+ }
+}
+
+static void
+pm_toggle_mute_for_every_sink_callback (pa_context *c,
+ const pa_sink_info *sink,
+ int eol,
+ void* userdata)
+{
+ if (eol > 0) {
+ return;
+ }
+ else {
+ pa_operation_unref (pa_context_set_sink_mute_by_index (c,
+ sink->index,
+ GPOINTER_TO_INT(userdata),
+ NULL,
+ NULL));
+ }
+}
+
diff --git a/src/pulse-manager.h b/src/pulseaudio-mgr.h
index 5895aeb..c0ab9c0 100644
--- a/src/pulse-manager.h
+++ b/src/pulseaudio-mgr.h
@@ -1,7 +1,5 @@
-#ifndef __INCLUDE_PULSE_MANAGER_H__
-#define __INCLUDE_PULSE_MANAGER_H__
/*
-Copyright 2010 Canonical Ltd.
+Copyright 2011 Canonical Ltd.
Authors:
Conor Curran <conor.curran@canonical.com>
@@ -19,27 +17,15 @@ 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 "active-sink.h"
+
+void pm_establish_pulse_connection (ActiveSink* active_sink);
+void close_pulse_activites();
+void pm_update_volume (gint sink_index, pa_cvolume new_volume);
+void pm_update_mute (gboolean update);
-#include <pulse/pulseaudio.h>
-#include <glib.h>
-#include "sound-service-dbus.h"
-typedef struct {
- gchar* name;
- gint index;
- pa_cvolume volume;
- pa_channel_map channel_map;
- gboolean mute;
- pa_volume_t base_volume;
-} sink_info;
-pa_context* get_context(void);
-void establish_pulse_activities(SoundServiceDbus *service);
-void set_sink_volume(gdouble percent);
-void toggle_global_mute(gboolean mute_value);
-void close_pulse_activites();
-gboolean default_sink_is_muted();
-#endif
diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c
index a20bb00..b89f5ca 100644
--- a/src/slider-menu-item.c
+++ b/src/slider-menu-item.c
@@ -22,13 +22,12 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <glib/gi18n.h>
#include "slider-menu-item.h"
-#include "pulse-manager.h"
#include "common-defs.h"
-
typedef struct _SliderMenuItemPrivate SliderMenuItemPrivate;
struct _SliderMenuItemPrivate {
+ ActiveSink* a_sink;
};
#define SLIDER_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SLIDER_MENU_ITEM_TYPE, SliderMenuItemPrivate))
@@ -43,7 +42,8 @@ static void handle_event (DbusmenuMenuitem * mi, const gchar * name,
G_DEFINE_TYPE (SliderMenuItem, slider_menu_item, DBUSMENU_TYPE_MENUITEM);
-static void slider_menu_item_class_init (SliderMenuItemClass *klass)
+static void
+slider_menu_item_class_init (SliderMenuItemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -57,13 +57,18 @@ static void slider_menu_item_class_init (SliderMenuItemClass *klass)
return;
}
-static void slider_menu_item_init (SliderMenuItem *self)
+static void
+slider_menu_item_init (SliderMenuItem *self)
{
g_debug("Building new Slider Menu Item");
+ dbusmenu_menuitem_property_set( DBUSMENU_MENUITEM(self),
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ DBUSMENU_VOLUME_MENUITEM_TYPE );
return;
}
-static void slider_menu_item_dispose (GObject *object)
+static void
+slider_menu_item_dispose (GObject *object)
{
G_OBJECT_CLASS (slider_menu_item_parent_class)->dispose (object);
return;
@@ -89,27 +94,26 @@ handle_event (DbusmenuMenuitem * mi,
gboolean volume_input = g_variant_get_double(input);
if (value != NULL){
- set_sink_volume(volume_input);
- // TODO -when the ACTIVESINK instance exists this will be handled nicely
- // PA MANAGER will be refactored first.
- if (default_sink_is_muted () == TRUE){
- toggle_global_mute (FALSE);
- }
+ if (IS_SLIDER_MENU_ITEM (mi)) {
+ SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (SLIDER_MENU_ITEM (mi));
+ active_sink_update_volume (priv->a_sink, volume_input);
+ active_sink_ensure_sink_is_unmuted (priv->a_sink);
+ }
}
}
-void slider_menu_item_update (SliderMenuItem* item,
+void
+slider_menu_item_update (SliderMenuItem* item,
gdouble update)
{
- // TODO
- // Check if that variant below will leak !!!
GVariant* new_volume = g_variant_new_double(update);
dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(item),
DBUSMENU_VOLUME_MENUITEM_LEVEL,
new_volume);
}
-void slider_menu_item_enable (SliderMenuItem* item,
+void
+slider_menu_item_enable (SliderMenuItem* item,
gboolean active)
{
dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item),
@@ -117,15 +121,11 @@ void slider_menu_item_enable (SliderMenuItem* item,
active );
}
-SliderMenuItem* slider_menu_item_new (gboolean sinks_available,
- gdouble start_volume)
+SliderMenuItem*
+slider_menu_item_new (ActiveSink* sink)
{
SliderMenuItem *self = g_object_new(SLIDER_MENU_ITEM_TYPE, NULL);
- dbusmenu_menuitem_property_set( DBUSMENU_MENUITEM(self),
- DBUSMENU_MENUITEM_PROP_TYPE,
- DBUSMENU_VOLUME_MENUITEM_TYPE );
- slider_menu_item_update (self, start_volume);
- slider_menu_item_enable (self, sinks_available);
-
+ SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self);
+ priv->a_sink = sink;
return self;
} \ No newline at end of file
diff --git a/src/slider-menu-item.h b/src/slider-menu-item.h
index 51336ae..f094c71 100644
--- a/src/slider-menu-item.h
+++ b/src/slider-menu-item.h
@@ -23,6 +23,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <glib-object.h>
#include <libdbusmenu-glib/menuitem.h>
+#include "active-sink.h"
G_BEGIN_DECLS
@@ -49,8 +50,7 @@ GType slider_menu_item_get_type (void);
void slider_menu_item_update(SliderMenuItem* item, gdouble update);
void slider_menu_item_enable(SliderMenuItem* item, gboolean active);
-SliderMenuItem* slider_menu_item_new (gboolean sinks_available,
- gdouble current_vol);
+SliderMenuItem* slider_menu_item_new (ActiveSink* sink);
G_END_DECLS
diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c
index 637bee4..58367f4 100644
--- a/src/sound-service-dbus.c
+++ b/src/sound-service-dbus.c
@@ -29,13 +29,9 @@
#include <libdbusmenu-glib/client.h>
#include "sound-service-dbus.h"
-
+#include "active-sink.h"
#include "gen-sound-service.xml.h"
#include "dbus-shared-names.h"
-#include "pulse-manager.h"
-#include "slider-menu-item.h"
-#include "mute-menu-item.h"
-#include "pulse-manager.h"
// DBUS methods
static void bus_method_call (GDBusConnection * connection,
@@ -57,16 +53,14 @@ static GDBusInterfaceVTable interface_table = {
typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate;
struct _SoundServiceDbusPrivate {
- GDBusConnection* connection;
- DbusmenuMenuitem* root_menuitem;
- SliderMenuItem* volume_slider_menuitem;
- MuteMenuItem* mute_menuitem;
- SoundState current_sound_state;
+ GDBusConnection* connection;
+ DbusmenuMenuitem* root_menuitem;
+ ActiveSink* active_sink;
};
static GDBusNodeInfo * node_info = NULL;
static GDBusInterfaceInfo * interface_info = NULL;
-static gboolean b_startup = TRUE;
+
#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);
@@ -74,17 +68,8 @@ static void sound_service_dbus_init (SoundServiceDbus *self);
static void sound_service_dbus_dispose (GObject *object);
static void sound_service_dbus_finalize (GObject *object);
-static void sound_service_dbus_build_sound_menu ( SoundServiceDbus* root,
- gboolean mute_update,
- gboolean availability,
- gdouble volume );
static void show_sound_settings_dialog (DbusmenuMenuitem *mi,
gpointer user_data);
-static SoundState sound_service_dbus_get_state_from_volume (SoundServiceDbus* self);
-static void sound_service_dbus_determine_state (SoundServiceDbus* self,
- gboolean availability,
- gboolean mute,
- gdouble volume);
static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self,
gchar* player_name,
gboolean blacklist);
@@ -133,8 +118,6 @@ sound_service_dbus_init (SoundServiceDbus *self)
priv->connection = NULL;
- priv->current_sound_state = UNAVAILABLE;
-
/* Fetch the session bus */
priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
@@ -167,32 +150,26 @@ sound_service_dbus_create_root_item (SoundServiceDbus* self)
DbusmenuServer *server = dbusmenu_server_new(INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH);
dbusmenu_server_set_root (server, priv->root_menuitem);
g_object_unref (priv->root_menuitem);
- establish_pulse_activities (self);
+ priv->active_sink = active_sink_new (self);
return priv->root_menuitem;
}
-static void
+void
sound_service_dbus_build_sound_menu ( SoundServiceDbus* self,
- gboolean mute_update,
- gboolean availability,
- gdouble volume )
+ DbusmenuMenuitem* mute_item,
+ DbusmenuMenuitem* slider_item)
{
SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self);
// Mute button
- priv->mute_menuitem = mute_menu_item_new ( mute_update, availability);
- dbusmenu_menuitem_child_append (priv->root_menuitem,
- mute_menu_item_get_button (priv->mute_menuitem));
-
- // Slider
- priv->volume_slider_menuitem = slider_menu_item_new ( availability, volume );
- dbusmenu_menuitem_child_append (priv->root_menuitem, DBUSMENU_MENUITEM ( priv->volume_slider_menuitem ));
+ dbusmenu_menuitem_child_append (priv->root_menuitem, mute_item);
+ g_debug ("just about to add the slider %i", DBUSMENU_IS_MENUITEM(slider_item));
+ dbusmenu_menuitem_child_append (priv->root_menuitem, slider_item);
// Separator
-
DbusmenuMenuitem* separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set( separator,
+ dbusmenu_menuitem_property_set (separator,
DBUSMENU_MENUITEM_PROP_TYPE,
DBUSMENU_CLIENT_TYPES_SEPARATOR);
dbusmenu_menuitem_child_append(priv->root_menuitem, separator);
@@ -207,9 +184,7 @@ sound_service_dbus_build_sound_menu ( SoundServiceDbus* self,
dbusmenu_menuitem_child_append(priv->root_menuitem, settings_mi);
g_object_unref (settings_mi);
g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(show_sound_settings_dialog), NULL);
-
- sound_service_dbus_determine_state (self, availability, mute_update, volume);
+ G_CALLBACK(show_sound_settings_dialog), NULL);
}
/**
@@ -229,41 +204,11 @@ show_sound_settings_dialog (DbusmenuMenuitem *mi,
}
}
-void
-sound_service_dbus_update_pa_state ( SoundServiceDbus* self,
- gboolean availability,
- gboolean mute_update,
- gdouble volume )
-{
- g_debug("update pa state with availability of %i, mute value of %i and a volume percent is %f", availability, mute_update, volume);
- SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self);
-
- if (b_startup == TRUE) {
- sound_service_dbus_build_sound_menu ( self,
- mute_update,
- availability,
- volume );
- b_startup = FALSE;
- return;
- }
-
- mute_menu_item_update ( priv->mute_menuitem,
- mute_update );
- slider_menu_item_update ( priv->volume_slider_menuitem,
- volume );
-
- mute_menu_item_enable ( priv->mute_menuitem, availability);
- slider_menu_item_enable ( priv->volume_slider_menuitem,
- availability );
- sound_service_dbus_determine_state (self, availability, mute_update, volume);
-
-}
-
-
static void
sound_service_dbus_dispose (GObject *object)
{
G_OBJECT_CLASS (sound_service_dbus_parent_class)->dispose (object);
+ //TODO dispose of the active sink instance !
return;
}
@@ -274,101 +219,19 @@ sound_service_dbus_finalize (GObject *object)
return;
}
-// UNTIL PA-MANAGER IS REFACTORED AND THE ACTIVESINK CLASS IS CREATED LEAVE
-// THE UI ELEMENTS SEPARATELY HANDLED LIKE THIS.
-void
-sound_service_dbus_update_volume (SoundServiceDbus* self,
- gdouble volume)
-{
- SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self);
- slider_menu_item_update (priv->volume_slider_menuitem, volume);
- sound_service_dbus_update_sound_state (self,
- sound_service_dbus_get_state_from_volume (self));
-}
-
-void
-sound_service_dbus_update_sink_mute (SoundServiceDbus* self,
- gboolean mute_update)
-{
- SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self);
- mute_menu_item_update (priv->mute_menuitem, mute_update);
- SoundState state = sound_service_dbus_get_state_from_volume (self);
- if (mute_update == TRUE){
- state = MUTED;
- }
- sound_service_dbus_update_sound_state (self, state);
-}
-
-/*------- State calculators ------------------*/
-static SoundState
-sound_service_dbus_get_state_from_volume (SoundServiceDbus* self)
-{
- SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self);
- GVariant* v = dbusmenu_menuitem_property_get_variant (DBUSMENU_MENUITEM(priv->volume_slider_menuitem),
- DBUSMENU_VOLUME_MENUITEM_LEVEL);
- gdouble volume_percent = g_variant_get_double (v);
-
- SoundState state = LOW_LEVEL;
-
- if (volume_percent < 30.0 && volume_percent > 0) {
- state = LOW_LEVEL;
- }
- else if (volume_percent < 70.0 && volume_percent >= 30.0) {
- state = MEDIUM_LEVEL;
- }
- else if (volume_percent >= 70.0) {
- state = HIGH_LEVEL;
- }
- else if (volume_percent == 0.0) {
- state = ZERO_LEVEL;
- }
- return state;
-}
-
-static void
-sound_service_dbus_determine_state (SoundServiceDbus* self,
- gboolean availability,
- gboolean mute,
- gdouble volume)
-{
- SoundState update;
- if (availability == FALSE) {
- update = UNAVAILABLE;
- }
- else if (mute == TRUE) {
- update = MUTED;
- }
- else{
- update = sound_service_dbus_get_state_from_volume (self);
- }
- sound_service_dbus_update_sound_state (self, update);
-}
-
// EMIT STATE SIGNAL
-
-// TODO: this will be a bit messy until the pa_manager is sorted.
-// And we figure out all of the edge cases.
void
sound_service_dbus_update_sound_state (SoundServiceDbus* self,
SoundState new_state)
{
SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self);
- SoundState update = new_state;
- // Ensure that after it has become available update the state with the current volume level
- if (new_state == AVAILABLE &&
- mute_menu_item_is_muted (priv->mute_menuitem) == FALSE){
- update = sound_service_dbus_get_state_from_volume (self);
- }
- if (update != BLOCKED){
- priv->current_sound_state = update;
- }
- GVariant* v_output = g_variant_new("(i)", (int)update);
+ GVariant* v_output = g_variant_new("(i)", (int)new_state);
GError * error = NULL;
- g_debug ("emitting signal with value %i", (int)update);
+ g_debug ("emitting state signal with value %i", (int)new_state);
g_dbus_connection_emit_signal( priv->connection,
NULL,
INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH,
@@ -384,7 +247,6 @@ sound_service_dbus_update_sound_state (SoundServiceDbus* self,
}
//HANDLE DBUS METHOD CALLS
-// TODO we will need to implement the black_list method.
static void
bus_method_call (GDBusConnection * connection,
const gchar * sender,
@@ -401,8 +263,8 @@ bus_method_call (GDBusConnection * connection,
SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (service);
if (g_strcmp0(method, "GetSoundState") == 0) {
- g_debug("Get state - %i", priv->current_sound_state );
- retval = g_variant_new ( "(i)", priv->current_sound_state);
+ g_debug("Get state - %i", active_sink_get_state (priv->active_sink));
+ retval = g_variant_new ( "(i)", active_sink_get_state (priv->active_sink));
}
else if (g_strcmp0(method, "BlacklistMediaPlayer") == 0) {
gboolean blacklist;
@@ -422,6 +284,9 @@ bus_method_call (GDBusConnection * connection,
g_dbus_method_invocation_return_value (invocation, retval);
}
+/**
+ TODO - Works nicely but refactor into at least two different methods
+**/
static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self,
gchar* player_name,
gboolean blacklist)
diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h
index fab3549..cdc4608 100644
--- a/src/sound-service-dbus.h
+++ b/src/sound-service-dbus.h
@@ -55,12 +55,10 @@ GType sound_service_dbus_get_type (void) G_GNUC_CONST;
DbusmenuMenuitem* sound_service_dbus_create_root_item (SoundServiceDbus* self);
void sound_service_dbus_update_sound_state (SoundServiceDbus* self, SoundState new_state);
-void sound_service_dbus_update_sink_mute(SoundServiceDbus* self, gboolean sink_mute);
-void sound_service_dbus_update_volume(SoundServiceDbus* self, gdouble volume);
-void sound_service_dbus_update_pa_state ( SoundServiceDbus* root,
- gboolean availability,
- gboolean mute_update,
- gdouble volume );
+void sound_service_dbus_build_sound_menu ( SoundServiceDbus* self,
+ DbusmenuMenuitem* mute_item,
+ DbusmenuMenuitem* slider_item);
+
G_END_DECLS
diff --git a/src/sound-service.c b/src/sound-service.c
index 2cb33d3..cfc0b7e 100644
--- a/src/sound-service.c
+++ b/src/sound-service.c
@@ -18,8 +18,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sound-service.h"
-
-#include "pulse-manager.h"
+#include "pulseaudio-mgr.h"
#include "sound-service-dbus.h"
#include "music-player-bridge.h"