aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am21
-rw-r--r--src/Makefile.in51
-rw-r--r--src/common-defs.h1
-rw-r--r--src/dbus-menu-manager.c241
-rw-r--r--src/dbus-menu-manager.h31
-rw-r--r--src/indicator-sound.c529
-rw-r--r--src/indicator-sound.h35
-rw-r--r--src/pulse-manager.c34
-rw-r--r--src/pulse-manager.h4
-rw-r--r--src/slider-menu-item.c1
-rw-r--r--src/sound-service-client.h38
-rw-r--r--src/sound-service-dbus.c51
-rw-r--r--src/sound-service-dbus.h5
-rw-r--r--src/sound-service-server.h7
-rw-r--r--src/sound-service.c175
-rw-r--r--src/sound-service.h11
-rw-r--r--src/sound-service.xml10
17 files changed, 808 insertions, 437 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 543ec58..73bb259 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,11 +8,10 @@ soundmenulibdir = $(INDICATORDIR)
soundmenulib_LTLIBRARIES = libsoundmenu.la
libsoundmenu_la_SOURCES = \
common-defs.h \
+ indicator-sound.h \
indicator-sound.c \
dbus-shared-names.h \
- sound-service-client.h \
- sound-service-marshal.c \
- sound-service-marshal.h
+ sound-service-client.h
libsoundmenu_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror
libsoundmenu_la_LIBADD = $(APPLET_LIBS)
@@ -29,16 +28,6 @@ sound-service-client.h: $(srcdir)/sound-service.xml
--output=sound-service-client.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
@@ -47,6 +36,8 @@ indicator_sound_service_SOURCES = \
common-defs.h \
sound-service.h \
sound-service.c \
+ dbus-menu-manager.c \
+ dbus-menu-manager.h \
pulse-manager.h \
pulse-manager.c \
sound-service-dbus.h \
@@ -71,9 +62,7 @@ sound-service-server.h: $(srcdir)/sound-service.xml
###############
BUILT_SOURCES = \
sound-service-client.h \
- sound-service-server.h \
- sound-service-marshal.h \
- sound-service-marshal.c
+ sound-service-server.h
EXTRA_DIST = \
sound-service.xml \
diff --git a/src/Makefile.in b/src/Makefile.in
index ada23f9..37b1e9c 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -75,8 +75,7 @@ am__installdirs = "$(DESTDIR)$(soundmenulibdir)" \
LTLIBRARIES = $(soundmenulib_LTLIBRARIES)
am__DEPENDENCIES_1 =
libsoundmenu_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
-am_libsoundmenu_la_OBJECTS = libsoundmenu_la-indicator-sound.lo \
- libsoundmenu_la-sound-service-marshal.lo
+am_libsoundmenu_la_OBJECTS = libsoundmenu_la-indicator-sound.lo
libsoundmenu_la_OBJECTS = $(am_libsoundmenu_la_OBJECTS)
AM_V_lt = $(am__v_lt_$(V))
am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
@@ -88,6 +87,7 @@ libsoundmenu_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
PROGRAMS = $(libexec_PROGRAMS)
am_indicator_sound_service_OBJECTS = \
indicator_sound_service-sound-service.$(OBJEXT) \
+ indicator_sound_service-dbus-menu-manager.$(OBJEXT) \
indicator_sound_service-pulse-manager.$(OBJEXT) \
indicator_sound_service-sound-service-dbus.$(OBJEXT) \
indicator_sound_service-sound-service-marshal.$(OBJEXT) \
@@ -292,11 +292,10 @@ soundmenulibdir = $(INDICATORDIR)
soundmenulib_LTLIBRARIES = libsoundmenu.la
libsoundmenu_la_SOURCES = \
common-defs.h \
+ indicator-sound.h \
indicator-sound.c \
dbus-shared-names.h \
- sound-service-client.h \
- sound-service-marshal.c \
- sound-service-marshal.h
+ sound-service-client.h
libsoundmenu_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror
libsoundmenu_la_LIBADD = $(APPLET_LIBS)
@@ -309,6 +308,8 @@ indicator_sound_service_SOURCES = \
common-defs.h \
sound-service.h \
sound-service.c \
+ dbus-menu-manager.c \
+ dbus-menu-manager.h \
pulse-manager.h \
pulse-manager.c \
sound-service-dbus.h \
@@ -327,9 +328,7 @@ indicator_sound_service_LDADD = $(SOUNDSERVICE_LIBS) $(GCONF_LIBS)
###############
BUILT_SOURCES = \
sound-service-client.h \
- sound-service-server.h \
- sound-service-marshal.h \
- sound-service-marshal.c
+ sound-service-server.h
EXTRA_DIST = \
sound-service.xml \
@@ -459,13 +458,13 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-dbus-menu-manager.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-pulse-manager.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-slider-menu-item.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-sound-service-dbus.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-sound-service-marshal.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/indicator_sound_service-sound-service.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsoundmenu_la-indicator-sound.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsoundmenu_la-sound-service-marshal.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -499,14 +498,6 @@ libsoundmenu_la-indicator-sound.lo: indicator-sound.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsoundmenu_la_CFLAGS) $(CFLAGS) -c -o libsoundmenu_la-indicator-sound.lo `test -f 'indicator-sound.c' || echo '$(srcdir)/'`indicator-sound.c
-libsoundmenu_la-sound-service-marshal.lo: sound-service-marshal.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsoundmenu_la_CFLAGS) $(CFLAGS) -MT libsoundmenu_la-sound-service-marshal.lo -MD -MP -MF $(DEPDIR)/libsoundmenu_la-sound-service-marshal.Tpo -c -o libsoundmenu_la-sound-service-marshal.lo `test -f 'sound-service-marshal.c' || echo '$(srcdir)/'`sound-service-marshal.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsoundmenu_la-sound-service-marshal.Tpo $(DEPDIR)/libsoundmenu_la-sound-service-marshal.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sound-service-marshal.c' object='libsoundmenu_la-sound-service-marshal.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsoundmenu_la_CFLAGS) $(CFLAGS) -c -o libsoundmenu_la-sound-service-marshal.lo `test -f 'sound-service-marshal.c' || echo '$(srcdir)/'`sound-service-marshal.c
-
indicator_sound_service-sound-service.o: sound-service.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -MT indicator_sound_service-sound-service.o -MD -MP -MF $(DEPDIR)/indicator_sound_service-sound-service.Tpo -c -o indicator_sound_service-sound-service.o `test -f 'sound-service.c' || echo '$(srcdir)/'`sound-service.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/indicator_sound_service-sound-service.Tpo $(DEPDIR)/indicator_sound_service-sound-service.Po
@@ -523,6 +514,22 @@ indicator_sound_service-sound-service.obj: sound-service.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -c -o indicator_sound_service-sound-service.obj `if test -f 'sound-service.c'; then $(CYGPATH_W) 'sound-service.c'; else $(CYGPATH_W) '$(srcdir)/sound-service.c'; fi`
+indicator_sound_service-dbus-menu-manager.o: dbus-menu-manager.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -MT indicator_sound_service-dbus-menu-manager.o -MD -MP -MF $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Tpo -c -o indicator_sound_service-dbus-menu-manager.o `test -f 'dbus-menu-manager.c' || echo '$(srcdir)/'`dbus-menu-manager.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Tpo $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dbus-menu-manager.c' object='indicator_sound_service-dbus-menu-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -c -o indicator_sound_service-dbus-menu-manager.o `test -f 'dbus-menu-manager.c' || echo '$(srcdir)/'`dbus-menu-manager.c
+
+indicator_sound_service-dbus-menu-manager.obj: dbus-menu-manager.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -MT indicator_sound_service-dbus-menu-manager.obj -MD -MP -MF $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Tpo -c -o indicator_sound_service-dbus-menu-manager.obj `if test -f 'dbus-menu-manager.c'; then $(CYGPATH_W) 'dbus-menu-manager.c'; else $(CYGPATH_W) '$(srcdir)/dbus-menu-manager.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Tpo $(DEPDIR)/indicator_sound_service-dbus-menu-manager.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dbus-menu-manager.c' object='indicator_sound_service-dbus-menu-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -c -o indicator_sound_service-dbus-menu-manager.obj `if test -f 'dbus-menu-manager.c'; then $(CYGPATH_W) 'dbus-menu-manager.c'; else $(CYGPATH_W) '$(srcdir)/dbus-menu-manager.c'; fi`
+
indicator_sound_service-pulse-manager.o: pulse-manager.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(indicator_sound_service_CFLAGS) $(CFLAGS) -MT indicator_sound_service-pulse-manager.o -MD -MP -MF $(DEPDIR)/indicator_sound_service-pulse-manager.Tpo -c -o indicator_sound_service-pulse-manager.o `test -f 'pulse-manager.c' || echo '$(srcdir)/'`pulse-manager.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/indicator_sound_service-pulse-manager.Tpo $(DEPDIR)/indicator_sound_service-pulse-manager.Po
@@ -813,16 +820,6 @@ sound-service-client.h: $(srcdir)/sound-service.xml
--output=sound-service-client.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
-
sound-service-server.h: $(srcdir)/sound-service.xml
dbus-binding-tool \
--prefix=_sound_service_server \
diff --git a/src/common-defs.h b/src/common-defs.h
index 942e269..9be1da5 100644
--- a/src/common-defs.h
+++ b/src/common-defs.h
@@ -22,6 +22,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define SIGNAL_SINK_INPUT_WHILE_MUTED "SinkInputWhileMuted"
#define SIGNAL_SINK_VOLUME_UPDATE "SinkVolumeUpdate"
#define SIGNAL_SINK_MUTE_UPDATE "SinkMuteUpdate"
+#define SIGNAL_SINK_AVAILABLE_UPDATE "SinkAvailableUpdate"
// DBUS items
#define DBUSMENU_SLIDER_MENUITEM_TYPE "x-canonical-ido-slider-item"
diff --git a/src/dbus-menu-manager.c b/src/dbus-menu-manager.c
new file mode 100644
index 0000000..64f7108
--- /dev/null
+++ b/src/dbus-menu-manager.c
@@ -0,0 +1,241 @@
+/*
+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>
+
+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 <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 "dbus-menu-manager.h"
+#include "sound-service-dbus.h"
+#include "pulse-manager.h"
+#include "slider-menu-item.h"
+
+#include "dbus-shared-names.h"
+
+// DBUS items
+static DbusmenuMenuitem *root_menuitem = NULL;
+static DbusmenuMenuitem *mute_all_menuitem = NULL;
+static SliderMenuItem *volume_slider_menuitem = NULL;
+static SoundServiceDbus *dbus_interface = NULL;
+
+// PULSEAUDIO
+static gboolean b_sink_available = FALSE;
+static gboolean b_all_muted = FALSE;
+static gboolean b_pulse_ready = FALSE;
+static gboolean b_startup = TRUE;
+static gdouble volume_percent = 0.0;
+
+static void set_global_mute_from_ui();
+static gboolean idle_routine (gpointer data);
+static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service);
+static void refresh_menu();
+
+/*-------------------------------------------------------------------------*/
+// Public Methods
+/*-------------------------------------------------------------------------*/
+
+/**
+setup:
+**/
+void dbus_menu_manager_setup()
+{
+ root_menuitem = dbusmenu_menuitem_new();
+ g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
+
+ g_idle_add(idle_routine, root_menuitem);
+
+ 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);
+ establish_pulse_activities(dbus_interface);
+}
+
+/**
+teardown:
+**/
+void dbus_menu_manager_teardown()
+{
+ //TODO tidy up dbus_interface and items!
+}
+
+/**
+update_pa_state:
+**/
+void dbus_menu_manager_update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble percent)
+{
+ b_sink_available = sink_available;
+ b_all_muted = sink_muted;
+ b_pulse_ready = pa_state;
+ volume_percent = percent;
+ g_debug("update pa state with state %i, availability of %i, mute value of %i and a volume percent is %f", pa_state, sink_available, sink_muted, volume_percent);
+ // Only rebuild the menu on start up...
+ if(b_startup == TRUE){
+ rebuild_sound_menu(root_menuitem, dbus_interface);
+ b_startup = FALSE;
+ }
+ else{
+ refresh_menu();
+ }
+ // Emit the signals after the menus are setup/torn down
+ // preserve ordering !
+ sound_service_dbus_update_sink_availability(dbus_interface, sink_available);
+ sound_service_dbus_update_sink_volume(dbus_interface, percent);
+ sound_service_dbus_update_sink_mute(dbus_interface, sink_muted);
+ dbus_menu_manager_update_mute_ui(b_all_muted);
+}
+
+/**
+update_mute_ui:
+'public' method allowing the pa manager to update the mute menu item.
+**/
+void dbus_menu_manager_update_mute_ui(gboolean incoming_mute_value)
+{
+ b_all_muted = incoming_mute_value;
+ dbusmenu_menuitem_property_set(mute_all_menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL,
+ _(b_all_muted == FALSE ? "Mute All" : "Unmute"));
+}
+
+
+/*-------------------------------------------------------------------------*/
+// Private Methods
+/*-------------------------------------------------------------------------*/
+
+static void refresh_menu()
+{
+ g_debug("in the refresh menu method");
+ if(b_sink_available == FALSE || b_pulse_ready == FALSE)
+ {
+
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_ENABLED,
+ FALSE);
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ FALSE);
+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
+ DBUSMENU_MENUITEM_PROP_ENABLED,
+ FALSE);
+
+ }
+ else if(b_sink_available == TRUE && b_pulse_ready == TRUE){
+
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_ENABLED,
+ TRUE);
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ TRUE);
+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
+ DBUSMENU_MENUITEM_PROP_ENABLED,
+ TRUE);
+ }
+/* if(b_all_muted == TRUE){*/
+/* dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), */
+/* DBUSMENU_MENUITEM_PROP_ENABLED,*/
+/* FALSE); */
+/* }*/
+}
+
+
+
+/**
+idle_routine:
+Something for glip mainloop to do when idle
+**/
+static gboolean idle_routine (gpointer data)
+{
+ return FALSE;
+}
+
+
+
+/**
+show_sound_settings_dialog:
+Bring up the gnome volume preferences dialog
+**/
+static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data)
+{
+ GError * error = NULL;
+ if (!g_spawn_command_line_async("gnome-volume-control", &error))
+ {
+ g_warning("Unable to show dialog: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+/**
+rebuild_sound_menu:
+Build the DBus menu items, mute/unmute, slider, separator and sound preferences 'link'
+**/
+static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
+{
+ // Mute button
+ mute_all_menuitem = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute"));
+ g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute_from_ui), NULL);
+ dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_ENABLED, b_sink_available);
+
+ // Slider
+ volume_slider_menuitem = slider_menu_item_new(b_sink_available, volume_percent);
+ dbusmenu_menuitem_child_append(root, mute_all_menuitem);
+ dbusmenu_menuitem_child_append(root, DBUSMENU_MENUITEM(volume_slider_menuitem));
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_ENABLED,
+ b_sink_available && !b_all_muted);
+ g_debug("!!!!!!**in the rebuild sound menu - slider active = %i", b_sink_available && !b_all_muted);
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ b_sink_available);
+ // Separator
+ DbusmenuMenuitem *separator = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_append(root, separator);
+
+ // Sound preferences dialog
+ DbusmenuMenuitem *settings_mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set(settings_mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Sound Preferences..."));
+//_("Sound Preferences..."));
+ dbusmenu_menuitem_child_append(root, settings_mi);
+ g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(show_sound_settings_dialog), NULL);
+}
+
+/**
+set_global_mute_from_ui:
+Callback for the dbusmenuitem button
+**/
+static void set_global_mute_from_ui()
+{
+ b_all_muted = !b_all_muted;
+ toggle_global_mute(b_all_muted);
+ dbusmenu_menuitem_property_set(mute_all_menuitem,
+ DBUSMENU_MENUITEM_PROP_LABEL,
+ _(b_all_muted == FALSE ? "Mute All" : "Unmute"));
+}
+
+
diff --git a/src/dbus-menu-manager.h b/src/dbus-menu-manager.h
new file mode 100644
index 0000000..5f49e5f
--- /dev/null
+++ b/src/dbus-menu-manager.h
@@ -0,0 +1,31 @@
+#ifndef __INCLUDE_DBUS_MENU_MANAGER_H__
+#define __INCLUDE_DBUS_MENU_MANAGER_H__
+
+/*
+This handles the management of the dbusmeneu items.
+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/>.
+*/
+
+void dbus_menu_manager_setup();
+void dbus_menu_manager_teardown();
+void dbus_menu_manager_update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble current_vol);
+// TODO update pa_state should incorporate the method below !
+void dbus_menu_manager_update_mute_ui(gboolean incoming_mute_value);
+
+#endif
+
diff --git a/src/indicator-sound.c b/src/indicator-sound.c
index 1301916..aecbe7a 100644
--- a/src/indicator-sound.c
+++ b/src/indicator-sound.c
@@ -5,7 +5,7 @@ into the gnome-panel using it's applet interface.
Copyright 2010 Canonical Ltd.
Authors:
- Conor Curran <conor.curra@canonical.com>
+ Conor Curran <conor.curran@canonical.com>
Ted Gould <ted@canonical.com>
This program is free software: you can redistribute it and/or modify it
@@ -34,8 +34,9 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <libindicator/indicator.h>
#include <libindicator/indicator-object.h>
#include <libindicator/indicator-service-manager.h>
+#include <libindicator/indicator-image-helper.h>
-
+#include "indicator-sound.h"
#include "dbus-shared-names.h"
#include "sound-service-client.h"
#include "common-defs.h"
@@ -78,14 +79,16 @@ G_DEFINE_TYPE (IndicatorSound, indicator_sound, INDICATOR_OBJECT_TYPE);
static GtkLabel * get_label (IndicatorObject * io);
static GtkImage * get_icon (IndicatorObject * io);
static GtkMenu * get_menu (IndicatorObject * io);
+
//Slider related
static GtkWidget *volume_slider = NULL;
static gboolean new_slider_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client);
-static void slider_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkWidget *widget);
-// Alternative callback mechanism, may use this again once ido is updated.
-/*static gboolean user_change_value_event_cb(GtkRange *range, GtkScrollType scroll_type, gdouble input_value, gpointer user_data);*/
+/*static void slider_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkWidget *widget);*/
static gboolean value_changed_event_cb(GtkRange *range, gpointer user_data);
static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data);
+/*static void slider_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data);*/
+static void slider_grabbed(GtkWidget *widget, gpointer user_data);
+static void slider_released(GtkWidget *widget, gpointer user_data);
// DBUS communication
static DBusGProxy *sound_dbus_proxy = NULL;
@@ -93,28 +96,41 @@ static void connection_changed (IndicatorServiceManager * sm, gboolean connected
static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gboolean value, gpointer userdata);
static void catch_signal_sink_volume_update(DBusGProxy * proxy, gdouble volume_percent, gpointer userdata);
static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value, gpointer userdata);
+static void catch_signal_sink_availability_update(DBusGProxy *proxy, gboolean available_value, gpointer userdata);
static void fetch_volume_percent_from_dbus();
static void fetch_mute_value_from_dbus();
+static void fetch_sink_availability_from_dbus();
/****Volume States 'members' ***/
-static void prepare_state_machine();
-static void determine_state_from_volume(gdouble volume_percent);
static void update_state(const gint state);
+
static const gint STATE_MUTED = 0;
static const gint STATE_ZERO = 1;
static const gint STATE_LOW = 2;
static const gint STATE_MEDIUM = 3;
static const gint STATE_HIGH = 4;
static const gint STATE_MUTED_WHILE_INPUT = 5;
-static const gint STATE_SINKS_NONE = 5;
+static const gint STATE_SINKS_NONE = 6;
+
static GHashTable *volume_states = NULL;
static GtkImage *speaker_image = NULL;
-static GtkWidget* primary_image = NULL;
static gint current_state = 0;
static gint previous_state = 0;
+
static gdouble initial_volume_percent = 0;
static gboolean initial_mute = FALSE;
-
+static gboolean device_available = TRUE;
+static gboolean slider_in_direct_use = FALSE;
+
+#define DESIGN_TEAM_SIZE design_team_size
+static GtkIconSize design_team_size;
+static gint animation_id;
+static GList * blocked_animation_list = NULL;
+static GList * blocked_iter = NULL;
+static void prepare_blocked_animation();
+static gboolean fade_back_to_mute_image();
+
+// Construction
static void
indicator_sound_class_init (IndicatorSoundClass *klass)
{
@@ -127,46 +143,123 @@ indicator_sound_class_init (IndicatorSoundClass *klass)
io_class->get_label = get_label;
io_class->get_image = get_icon;
io_class->get_menu = get_menu;
+
+ design_team_size = gtk_icon_size_register("design-team-size", 22, 22);
- 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);
prepare_state_machine();
+ prepare_blocked_animation();
+ animation_id = 0;
return;
}
+static void
+indicator_sound_dispose (GObject *object)
+{
+ IndicatorSound * self = INDICATOR_SOUND(object);
-/*
-Prepare states Array.
-*/
-static void prepare_state_machine()
+ if (self->service != NULL) {
+ g_object_unref(G_OBJECT(self->service));
+ self->service = NULL;
+ }
+ g_hash_table_destroy(volume_states);
+ g_list_foreach (blocked_animation_list, (GFunc)g_object_unref, NULL);
+ g_list_free(blocked_animation_list);
+ G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object);
+ return;
+}
+
+static void
+indicator_sound_finalize (GObject *object)
{
- // TODO we need three more images
- volume_states = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED), g_strdup("audio-volume-muted-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_ZERO), g_strdup("audio-volume-zero-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_LOW), g_strdup("audio-volume-low-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MEDIUM), g_strdup("audio-volume-medium-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_HIGH), g_strdup("audio-volume-high-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT), g_strdup("audio-volume-muted-blocking-panel"));
- g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_SINKS_NONE), g_strdup("audio-output-none-panel"));
+ G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object);
+ return;
+}
+
+static GtkLabel *
+get_label (IndicatorObject * io)
+{
+ return NULL;
+}
+
+static GtkImage *
+get_icon (IndicatorObject * io)
+{
+ gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
+ //g_debug("At start-up attempting to set the image to %s", current_name);
+ speaker_image = indicator_image_helper(current_name);
+ gtk_widget_show(GTK_WIDGET(speaker_image));
+ return speaker_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)
+{
+ DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT);
+ DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu);
+ dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item);
+
+ // register Key-press listening on the menu widget as the slider does not allow this.
+ g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL);
+ return GTK_MENU(menu);
+}
+
+
+/**
+new_slider_item:
+Create a new dBusMenu Slider item.
+**/
+static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+ g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
+ g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
+
+ volume_slider = ido_scale_menu_item_new_with_range ("Volume", initial_volume_percent, 0, 100, 0.5);
+ g_object_set(volume_slider, "reverse-scroll-events", TRUE, NULL);
+
+ GtkMenuItem *menu_volume_slider = GTK_MENU_ITEM(volume_slider);
+
+ dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_volume_slider, parent);
+
+ // register slider changes listening on the range
+ GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
+
+ g_signal_connect(slider, "value-changed", G_CALLBACK(value_changed_event_cb), newitem);
+ g_signal_connect(volume_slider, "slider-grabbed", G_CALLBACK(slider_grabbed), NULL);
+ g_signal_connect(volume_slider, "slider-released", G_CALLBACK(slider_released), NULL);
+/* g_signal_connect(slider, "size-allocate", G_CALLBACK(slider_size_allocate), NULL);*/
+
+ // Set images on the ido
+ GtkWidget* primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)volume_slider);
+ GIcon * primary_gicon = g_themed_icon_new_with_default_fallbacks(g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_ZERO)));
+ gtk_image_set_from_gicon(GTK_IMAGE(primary_image), primary_gicon, GTK_ICON_SIZE_MENU);
+ g_object_unref(primary_gicon);
+
+ GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)volume_slider);
+ GIcon * secondary_gicon = g_themed_icon_new_with_default_fallbacks(g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_HIGH)));
+ gtk_image_set_from_gicon(GTK_IMAGE(secondary_image), secondary_gicon, GTK_ICON_SIZE_MENU);
+ g_object_unref(secondary_gicon);
+
+ gtk_widget_set_sensitive(volume_slider, !initial_mute);
+ gtk_widget_show_all(volume_slider);
+
+ return TRUE;
}
static void
connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata)
{
+ // TODO: This could be safer.
if (connected) {
if (sound_dbus_proxy == NULL) {
GError * error = NULL;
@@ -191,11 +284,14 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u
dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_VOLUME_UPDATE, G_CALLBACK(catch_signal_sink_volume_update), NULL, NULL);
dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_MUTE_UPDATE, G_TYPE_BOOLEAN, G_TYPE_INVALID);
dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_MUTE_UPDATE, G_CALLBACK(catch_signal_sink_mute_update), NULL, NULL);
+ dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_AVAILABLE_UPDATE, G_TYPE_BOOLEAN, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_AVAILABLE_UPDATE, G_CALLBACK(catch_signal_sink_availability_update), NULL, NULL);
// Ensure we are in a coherent state with the service at start up.
// Preserve ordering!
fetch_volume_percent_from_dbus();
fetch_mute_value_from_dbus();
+ fetch_sink_availability_from_dbus();
}
} else {
@@ -205,108 +301,86 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u
return;
}
-static void fetch_volume_percent_from_dbus()
+/*
+Prepare states Array.
+*/
+void prepare_state_machine()
{
- GError * error = NULL;
- gdouble *volume_percent_input;
- volume_percent_input = g_new0(gdouble, 1);
- org_ayatana_indicator_sound_get_sink_volume(sound_dbus_proxy, volume_percent_input, &error);
- if (error != NULL) {
- g_warning("Unable to fetch VOLUME at indicator start up: %s", error->message);
- g_error_free(error);
- g_free(volume_percent_input);
- return;
- }
- initial_volume_percent = *volume_percent_input;
- determine_state_from_volume(initial_volume_percent);
- g_free(volume_percent_input);
- g_debug("at the indicator start up and the volume percent returned from dbus method is %f", initial_volume_percent);
+ volume_states = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED), g_strdup("audio-volume-muted-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_ZERO), g_strdup("audio-volume-low-zero-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_LOW), g_strdup("audio-volume-low-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MEDIUM), g_strdup("audio-volume-medium-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_HIGH), g_strdup("audio-volume-high-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT), g_strdup("audio-volume-muted-blocking-panel"));
+ g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_SINKS_NONE), g_strdup("audio-output-none-panel"));
}
-static void fetch_mute_value_from_dbus()
+/*
+prepare_blocked_animation:
+Prepares the array of images to be used in the blocked animation.
+Only called at startup.
+*/
+static void prepare_blocked_animation()
{
- GError * error = NULL;
- gboolean *mute_input;
- mute_input = g_new0(gboolean, 1);
- org_ayatana_indicator_sound_get_sink_mute(sound_dbus_proxy, mute_input, &error);
- if (error != NULL) {
- g_warning("Unable to fetch MUTE at indicator start up: %s", error->message);
+ GError* error= NULL;
+ int i;
+
+ gchar* blocked_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT));
+ gchar* muted_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED));
+ GtkIconTheme* theme = gtk_icon_theme_get_default();
+ GdkPixbuf* mute_buf = gtk_icon_theme_load_icon(theme,
+ muted_name,
+ 22,
+ GTK_ICON_LOOKUP_GENERIC_FALLBACK,
+ &error);
+ if(error != NULL){
+ g_error("indicator-sound : prepare_blocked_animation - %s", error->message);
g_error_free(error);
- g_free(mute_input);
- return;
- }
- initial_mute = *mute_input;
- if (initial_mute == TRUE)
- update_state(STATE_MUTED);
- g_free(mute_input);
- g_debug("at the indicator start up and the MUTE returned from dbus method is %i", initial_mute);
-}
-
-static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gboolean block_value, gpointer userdata)
-{
- g_debug("signal caught - sink input while muted with value %i", block_value);
-}
-
-static void catch_signal_sink_volume_update(DBusGProxy *proxy, gdouble volume_percent, gpointer userdata)
-{
- GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
- GtkRange *range = (GtkRange*)slider;
-
- // DEBUG
- gdouble current_value = gtk_range_get_value(range);
- g_debug("SIGNAL- update sink volume - current_value : %f and new value : %f", current_value, volume_percent);
- gtk_range_set_value(range, volume_percent);
- determine_state_from_volume(volume_percent);
-}
-
-static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value, gpointer userdata)
-{
- //We can be sure the service won't send a mute signal unless it has changed !
- //UNMUTE's force a volume update therefore icon is updated appropriately => no need for unmute handling here.
- if(mute_value == TRUE)
- {
- update_state(STATE_MUTED);
- }
- g_debug("signal caught - sink mute update with mute value: %i", mute_value);
- gtk_widget_set_sensitive(volume_slider, !mute_value);
+ return;
+ }
+
+ GdkPixbuf* blocked_buf = gtk_icon_theme_load_icon(theme, blocked_name,
+ 22,
+ GTK_ICON_LOOKUP_GENERIC_FALLBACK,
+ &error);
+ if(error != NULL){
+ g_error("indicator-sound : prepare_blocked_animation - %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ // sample 22 snapshots - range : 0-256
+ for(i = 0; i < 23; i++)
+ {
+ gdk_pixbuf_composite(mute_buf, blocked_buf, 0, 0,
+ gdk_pixbuf_get_width(mute_buf),
+ gdk_pixbuf_get_height(mute_buf),
+ 0, 0, 1, 1, GDK_INTERP_BILINEAR, MIN(255, i * 11));
+ g_debug("creating blocking animation - alpha value = %i", MIN(255, i * 11));
+ blocked_animation_list = g_list_append(blocked_animation_list, gdk_pixbuf_copy(blocked_buf));
+ }
}
-static void
-indicator_sound_dispose (GObject *object)
+gint get_state()
{
- IndicatorSound * self = INDICATOR_SOUND(object);
-
- if (self->service != NULL) {
- g_object_unref(G_OBJECT(self->service));
- self->service = NULL;
- }
- g_hash_table_destroy(volume_states);
- G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object);
- return;
+ return current_state;
}
-static void
-indicator_sound_finalize (GObject *object)
+gchar* get_state_image_name(gint state)
{
- G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object);
- return;
+ return g_hash_table_lookup(volume_states, GINT_TO_POINTER(state));
}
-static GtkLabel *
-get_label (IndicatorObject * io)
+void prepare_for_tests(IndicatorObject *io)
{
- return NULL;
+ prepare_state_machine();
+ get_icon(io);
}
-static GtkImage *
-get_icon (IndicatorObject * io)
+void tidy_up_hash()
{
- gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
- g_debug("At start-up attempting to set the image to %s", current_name);
- speaker_image = GTK_IMAGE(gtk_image_new_from_icon_name(current_name, GTK_ICON_SIZE_MENU));
- gtk_widget_show(GTK_WIDGET(speaker_image));
- return speaker_image;
+ g_hash_table_destroy(volume_states);
}
static void update_state(const gint state)
@@ -319,22 +393,25 @@ static void update_state(const gint state)
current_state = state;
gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));
- gtk_image_set_from_icon_name(speaker_image, image_name, GTK_ICON_SIZE_MENU);
+ GtkImage * tempimage = indicator_image_helper(image_name);
+ gtk_image_set_from_pixbuf(speaker_image, gtk_image_get_pixbuf(tempimage));
+ g_object_ref_sink(tempimage);
}
-static void determine_state_from_volume(gdouble volume_percent)
+void determine_state_from_volume(gdouble volume_percent)
{
/* g_debug("determine_state_from_volume - previous_state = %i", previous_state);*/
-
+ if (device_available == FALSE)
+ return;
gint state = previous_state;
if (volume_percent < 30.0 && volume_percent > 0){
state = STATE_LOW;
}
- else if(volume_percent < 70.0 && volume_percent > 30.0){
+ else if(volume_percent < 70.0 && volume_percent >= 30.0){
state = STATE_MEDIUM;
}
- else if(volume_percent > 70.0){
+ else if(volume_percent >= 70.0){
state = STATE_HIGH;
}
else if(volume_percent == 0.0){
@@ -344,70 +421,137 @@ static void determine_state_from_volume(gdouble volume_percent)
}
-/* 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)
+static void fetch_sink_availability_from_dbus()
{
- DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT);
- DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu);
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item);
-
- // register Key-press listening on the menu widget as the slider does not allow this.
- g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL);
+ GError * error = NULL;
+ gboolean * available_input;
+ available_input = g_new0(gboolean, 1);
+ org_ayatana_indicator_sound_get_sink_availability(sound_dbus_proxy, available_input, &error);
+ if (error != NULL) {
+ g_warning("Unable to fetch AVAILABILITY at indicator start up: %s", error->message);
+ g_error_free(error);
+ g_free(available_input);
+ return;
+ }
+ device_available = *available_input;
+ if (device_available == FALSE)
+ update_state(STATE_SINKS_NONE);
+ g_free(available_input);
+ g_debug("IndicatorSound::fetch_sink_availability_from_dbus -> AVAILABILTY returned from dbus method is %i", device_available);
- return GTK_MENU(menu);
}
-/**
-new_slider_item:
-Create a new dBusMenu Slider item, register the
-**/
-static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+static void fetch_volume_percent_from_dbus()
{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
-
- volume_slider = ido_scale_menu_item_new_with_range ("Volume", initial_volume_percent, 0, 100, 0.5);
- g_object_set(volume_slider, "reverse-scroll-events", TRUE, NULL);
+ GError * error = NULL;
+ gdouble *volume_percent_input;
+ volume_percent_input = g_new0(gdouble, 1);
+ org_ayatana_indicator_sound_get_sink_volume(sound_dbus_proxy, volume_percent_input, &error);
+ if (error != NULL) {
+ g_warning("Unable to fetch VOLUME at indicator start up: %s", error->message);
+ g_error_free(error);
+ g_free(volume_percent_input);
+ return;
+ }
+ initial_volume_percent = *volume_percent_input;
+ determine_state_from_volume(initial_volume_percent);
+ g_free(volume_percent_input);
+ g_debug("at the indicator start up and the volume percent returned from dbus method is %f", initial_volume_percent);
+}
- GtkMenuItem *menu_volume_slider = GTK_MENU_ITEM(volume_slider);
+static void fetch_mute_value_from_dbus()
+{
+ GError * error = NULL;
+ gboolean *mute_input;
+ mute_input = g_new0(gboolean, 1);
+ org_ayatana_indicator_sound_get_sink_mute(sound_dbus_proxy, mute_input, &error);
+ if (error != NULL) {
+ g_warning("Unable to fetch MUTE at indicator start up: %s", error->message);
+ g_error_free(error);
+ g_free(mute_input);
+ return;
+ }
+ initial_mute = *mute_input;
+ if (initial_mute == TRUE)
+ update_state(STATE_MUTED);
+ g_free(mute_input);
+ g_debug("at the indicator start up and the MUTE returned from dbus method is %i", initial_mute);
+}
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_volume_slider, parent);
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(slider_prop_change_cb), volume_slider);
-
- // register slider changes listening on the range
- GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
- g_signal_connect(slider, "value-changed", G_CALLBACK(value_changed_event_cb), newitem);
- // alternative callback mechanism which i could use again at some point.
-/* g_signal_connect(slider, "change-value", G_CALLBACK(user_change_value_event_cb), newitem); */
-
- // Set images on the ido
- primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)volume_slider);
- gtk_image_set_from_icon_name(GTK_IMAGE(primary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_ZERO)), GTK_ICON_SIZE_MENU);
- GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)volume_slider);
- gtk_image_set_from_icon_name(GTK_IMAGE(secondary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_HIGH)), GTK_ICON_SIZE_MENU);
+static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gboolean block_value, gpointer userdata)
+{
+ g_debug("signal caught - sink input while muted with value %i", block_value);
+ if (block_value == 1 && animation_id == 0 ) {
+ // We can assume we are in the muted state !
+ gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT));
+ GtkImage * tempimage = indicator_image_helper(image_name);
+ gtk_image_set_from_pixbuf(speaker_image, gtk_image_get_pixbuf(tempimage));
+ g_object_ref_sink(tempimage);
+
+ blocked_iter = blocked_animation_list;
+ animation_id = g_timeout_add_seconds(1, fade_back_to_mute_image, NULL);
+ }
+}
+
+static gboolean fade_back_to_mute_image()
+{
+ if(blocked_iter != NULL)
+ {
+ g_debug("in animation 'loop'\n");
+ gtk_image_set_from_pixbuf(speaker_image, blocked_iter->data);
+ blocked_iter = blocked_iter->next;
+ return TRUE;
+ }
+ else{
+ animation_id = 0;
+ g_debug("exit from animation\n");
+ return FALSE;
+ }
+}
- gtk_widget_show_all(volume_slider);
+static void catch_signal_sink_volume_update(DBusGProxy *proxy, gdouble volume_percent, gpointer userdata)
+{
+ if (slider_in_direct_use != TRUE){
+ GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
+ GtkRange *range = (GtkRange*)slider;
+
+ // DEBUG
+ gdouble current_value = gtk_range_get_value(range);
+ g_debug("SIGNAL- update sink volume - current_value : %f and new value : %f", current_value, volume_percent);
+ gtk_range_set_value(range, volume_percent);
+ determine_state_from_volume(volume_percent);
+ }
+}
- return TRUE;
+static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value, gpointer userdata)
+{
+ //We can be sure the service won't send a mute signal unless it has changed !
+ //UNMUTE's force a volume update therefore icon is updated appropriately => no need for unmute handling here.
+ if(mute_value == TRUE && device_available != FALSE)
+ {
+ update_state(STATE_MUTED);
+ }
+ else{
+ if(animation_id != 0){
+ g_debug("about to remove the animation_id callback from the mainloop!!**");
+ g_source_remove(animation_id);
+ animation_id = 0;
+ }
+ }
+ g_debug("signal caught - sink mute update with mute value: %i", mute_value);
+ gtk_widget_set_sensitive(volume_slider, !mute_value);
}
-/**
-slider_prop_change_cb:
-Whenever we have a property change on a DbusmenuMenuitem this will be called.
-**/
-static void slider_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkWidget *widget)
+static void catch_signal_sink_availability_update(DBusGProxy *proxy, gboolean available_value, gpointer userdata)
{
- g_debug("slider_prop_change_cb - dodgy updater ");
- g_debug("about to set the slider to %f", g_value_get_double(value));
- GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider);
- GtkRange* range = (GtkRange*)slider;
- gtk_range_set_value(range, g_value_get_double(value));
- return;
+ device_available = available_value;
+ if (device_available == FALSE){
+ update_state(STATE_SINKS_NONE);
+ }
+ g_debug("signal caught - sink availability update with value: %i", available_value);
}
+
/**
value_changed_event_cb:
This callback will get triggered irregardless of whether its a user change or a programmatic change.
@@ -427,6 +571,35 @@ static gboolean value_changed_event_cb(GtkRange *range, gpointer user_data)
return FALSE;
}
+
+static void slider_grabbed (GtkWidget *widget, gpointer user_data)
+{
+ slider_in_direct_use = TRUE;
+ g_debug ("!!!!!! grabbed\n");
+}
+
+static void slider_released (GtkWidget *widget, gpointer user_data)
+{
+ slider_in_direct_use = FALSE;
+ g_debug ("!!!!!! released\n");
+}
+
+
+/**
+slider_size_allocate:
+Callback on the size-allocate event on the slider item.
+**/
+/*static void slider_size_allocate(GtkWidget *widget,*/
+/* GtkAllocation *allocation, */
+/* gpointer user_data)*/
+/*{*/
+/* g_print("Size allocate on slider (%dx%d)\n", allocation->width, allocation->height);*/
+/* if(allocation->width < 200){*/
+/* g_print("Attempting to resize the slider");*/
+/* gtk_widget_set_size_request(widget, 200, -1); */
+/* }*/
+/*}*/
+
/**
key_press_cb:
**/
@@ -490,22 +663,4 @@ static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer dat
return digested;
}
-/**
-This callback should only be called when the user actually drags the slider.
-Turned off for now in favour of the non descriminating value-changed call back.
-Once the grabbing listener is implemented on the slider may revert to using this.
-Its another tool for filtering unwanted volume change updates.
-**/
-/*static gboolean user_change_value_event_cb(GtkRange *range, GtkScrollType scroll_type, gdouble input_value, gpointer user_data)*/
-/*{*/
-/* DbusmenuMenuitem *item = (DbusmenuMenuitem*)user_data;*/
-/* gdouble clamped_input = CLAMP(input_value, 0, 100);*/
-/* GValue value = {0};*/
-/* g_debug("User input on SLIDER - = %f", clamped_input);*/
-/* g_value_init(&value, G_TYPE_DOUBLE);*/
-/* g_value_set_double(&value, clamped_input);*/
-/* dbusmenu_menuitem_handle_event (item, "slider_change", &value, 0);*/
-/* return FALSE; */
-/*} */
-
diff --git a/src/indicator-sound.h b/src/indicator-sound.h
new file mode 100644
index 0000000..e508390
--- /dev/null
+++ b/src/indicator-sound.h
@@ -0,0 +1,35 @@
+#ifndef __INCLUDE_INDICATOR_SOUND_H__
+#define __INCLUDE_INDICATOR_SOUND_H__
+
+/*
+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/>.
+*/
+
+// Essentially these are all exported to faciltiate testing
+void prepare_state_machine();
+void determine_state_from_volume(gdouble volume_percent);
+gint get_state();
+gchar* get_state_image_name(gint state);
+void prepare_for_tests(IndicatorObject * io);
+void tidy_up_hash();
+
+#endif
diff --git a/src/pulse-manager.c b/src/pulse-manager.c
index d5377bf..40add4e 100644
--- a/src/pulse-manager.c
+++ b/src/pulse-manager.c
@@ -26,8 +26,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <pulse/gccmacro.h>
#include "pulse-manager.h"
-#include "sound-service.h"
-
+#include "dbus-menu-manager.h"
static GHashTable *sink_hash = NULL;
static SoundServiceDbus *dbus_service = NULL;
@@ -69,8 +68,7 @@ void establish_pulse_activities(SoundServiceDbus *service)
// Establish event callback registration
pa_context_set_state_callback(pulse_context, context_state_callback, NULL);
// BUILD MENU ANYWHO - it will be updated
- update_pa_state(FALSE, FALSE, FALSE, 0);
-
+ dbus_menu_manager_update_pa_state(FALSE, FALSE, FALSE, 0);
pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
}
@@ -108,7 +106,7 @@ static void reconnect_to_pulse()
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);
- update_pa_state(FALSE, FALSE, FALSE, 0);
+ dbus_menu_manager_update_pa_state(FALSE, FALSE, FALSE, 0);
pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL);
}
@@ -276,12 +274,15 @@ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, in
gboolean device_available = determine_sink_availability();
if(device_available == TRUE)
{
- update_pa_state(TRUE, device_available, default_sink_is_muted(), get_default_sink_volume());
+ dbus_menu_manager_update_pa_state(TRUE,
+ 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 ...");
- update_pa_state(FALSE, device_available, TRUE, 0);
+ dbus_menu_manager_update_pa_state(FALSE, device_available, default_sink_is_muted(), get_default_sink_volume());
}
}
else{
@@ -322,7 +323,7 @@ static void pulse_default_sink_info_callback(pa_context *c, const pa_sink_info *
}
else
{
- update_pa_state(TRUE, determine_sink_availability(), default_sink_is_muted(), get_default_sink_volume());
+ dbus_menu_manager_update_pa_state(TRUE, determine_sink_availability(), default_sink_is_muted(), get_default_sink_volume());
}
}
}
@@ -385,7 +386,7 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v
{
g_debug("Updating Mute from PA manager with mute = %i", s->mute);
sound_service_dbus_update_sink_mute(dbus_service, s->mute);
- update_mute_ui(s->mute);
+ dbus_menu_manager_update_mute_ui(s->mute);
if(s->mute == FALSE){
pa_volume_t vol = pa_cvolume_avg(&s->volume);
gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM;
@@ -397,6 +398,7 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v
}
else
{
+
sink_info *value;
value = g_new0(sink_info, 1);
value->index = value->device_index = info->index;
@@ -410,7 +412,8 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v
value->channel_map = info->channel_map;
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_sink_availability(dbus_service, TRUE);
+ }
}
@@ -421,7 +424,7 @@ static void pulse_server_info_callback(pa_context *c, const pa_server_info *info
if (info == NULL)
{
g_warning("No server - get the hell out of here");
- update_pa_state(FALSE, FALSE, TRUE, 0);
+ dbus_menu_manager_update_pa_state(FALSE, FALSE, TRUE, 0);
pa_server_available = FALSE;
return;
}
@@ -459,8 +462,11 @@ static void subscribed_events_callback(pa_context *c, enum pa_subscription_event
g_debug("PA_SUBSCRIPTION_EVENT_SINK event triggered");
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
{
- //TODO handle the remove event => if its our default sink - update date pa state
- } else
+ if(index == DEFAULT_SINK_INDEX)
+ g_debug("PA_SUBSCRIPTION_EVENT_SINK REMOVAL event triggered");
+ sound_service_dbus_update_sink_availability(dbus_service, FALSE);
+ }
+ else
{
pa_operation_unref(pa_context_get_sink_info_by_index(c, index, update_sink_info, userdata));
}
@@ -469,7 +475,7 @@ static void subscribed_events_callback(pa_context *c, enum pa_subscription_event
g_debug("PA_SUBSCRIPTION_EVENT_SINK_INPUT event triggered!!");
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
{
- //TODO handle the remove event
+ //handle the remove event - not relevant for current design
}
else
{
diff --git a/src/pulse-manager.h b/src/pulse-manager.h
index 1be5e44..e1777fb 100644
--- a/src/pulse-manager.h
+++ b/src/pulse-manager.h
@@ -1,3 +1,5 @@
+#ifndef __INCLUDE_PULSE_MANAGER_H__
+#define __INCLUDE_PULSE_MANAGER_H__
/*
A small wrapper utility to load indicators and put them as menu items
into the gnome-panel using it's applet interface.
@@ -21,7 +23,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
#include <pulse/pulseaudio.h>
#include <glib.h>
#include "sound-service-dbus.h"
@@ -47,4 +48,5 @@ void set_sink_volume(gdouble percent);
void toggle_global_mute(gboolean mute_value);
void close_pulse_activites();
+#endif
diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c
index ef3b1fa..a14f4f9 100644
--- a/src/slider-menu-item.c
+++ b/src/slider-menu-item.c
@@ -92,6 +92,7 @@ SliderMenuItem* slider_menu_item_new(gboolean sinks_available, gdouble start_vol
SliderMenuItem *self = g_object_new(SLIDER_MENU_ITEM_TYPE, NULL);
dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_SLIDER_MENUITEM_TYPE);
dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_ENABLED, sinks_available);
+ dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, sinks_available);
return self;
}
diff --git a/src/sound-service-client.h b/src/sound-service-client.h
index 19a3b81..b3d50da 100644
--- a/src/sound-service-client.h
+++ b/src/sound-service-client.h
@@ -134,6 +134,44 @@ org_ayatana_indicator_sound_get_sink_mute_async (DBusGProxy *proxy, org_ayatana_
stuff->userdata = userdata;
return dbus_g_proxy_begin_call (proxy, "GetSinkMute", org_ayatana_indicator_sound_get_sink_mute_async_callback, stuff, _dbus_glib_async_data_free, G_TYPE_INVALID);
}
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+gboolean
+org_ayatana_indicator_sound_get_sink_availability (DBusGProxy *proxy, gboolean* OUT_availability_input, GError **error)
+
+{
+ return dbus_g_proxy_call (proxy, "GetSinkAvailability", error, G_TYPE_INVALID, G_TYPE_BOOLEAN, OUT_availability_input, G_TYPE_INVALID);
+}
+
+typedef void (*org_ayatana_indicator_sound_get_sink_availability_reply) (DBusGProxy *proxy, gboolean OUT_availability_input, GError *error, gpointer userdata);
+
+static void
+org_ayatana_indicator_sound_get_sink_availability_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+ DBusGAsyncData *data = (DBusGAsyncData*) user_data;
+ GError *error = NULL;
+ gboolean OUT_availability_input;
+ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_BOOLEAN, &OUT_availability_input, G_TYPE_INVALID);
+ (*(org_ayatana_indicator_sound_get_sink_availability_reply)data->cb) (proxy, OUT_availability_input, error, data->userdata);
+ return;
+}
+
+static
+#ifdef G_HAVE_INLINE
+inline
+#endif
+DBusGProxyCall*
+org_ayatana_indicator_sound_get_sink_availability_async (DBusGProxy *proxy, org_ayatana_indicator_sound_get_sink_availability_reply callback, gpointer userdata)
+
+{
+ DBusGAsyncData *stuff;
+ stuff = g_slice_new (DBusGAsyncData);
+ stuff->cb = G_CALLBACK (callback);
+ stuff->userdata = userdata;
+ return dbus_g_proxy_begin_call (proxy, "GetSinkAvailability", org_ayatana_indicator_sound_get_sink_availability_async_callback, stuff, _dbus_glib_async_data_free, G_TYPE_INVALID);
+}
#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_ayatana_indicator_sound */
G_END_DECLS
diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c
index 99a9d34..1cc5f0d 100644
--- a/src/sound-service-dbus.c
+++ b/src/sound-service-dbus.c
@@ -29,10 +29,10 @@
#include "sound-service-marshal.h"
#include "pulse-manager.h"
-// DBUS methods -
-// TODO - other should be static and moved from the header to here
+// DBUS methods
static gboolean sound_service_dbus_get_sink_volume(SoundServiceDbus* service, gdouble* volume_percent_input, GError** gerror);
static gboolean sound_service_dbus_get_sink_mute(SoundServiceDbus* service, gboolean* mute_input, GError** gerror);
+static gboolean sound_service_dbus_get_sink_availability(SoundServiceDbus* service, gboolean* availability_input, GError** gerror);
static void sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror);
#include "sound-service-server.h"
@@ -41,18 +41,19 @@ typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate;
struct _SoundServiceDbusPrivate
{
- DBusGConnection *system_bus;
DBusGConnection *connection;
gdouble volume_percent;
gboolean mute;
+ gboolean sink_availability;
};
/* Signals */
enum {
- SINK_INPUT_WHILE_MUTED,
+ SINK_INPUT_WHILE_MUTED,
SINK_VOLUME_UPDATE,
SINK_MUTE_UPDATE,
+ SINK_AVAILABLE_UPDATE,
LAST_SIGNAL
};
@@ -107,6 +108,15 @@ sound_service_dbus_class_init (SoundServiceDbusClass *klass)
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ signals[SINK_AVAILABLE_UPDATE] = g_signal_new("sink-available-update",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+
}
@@ -116,25 +126,23 @@ sound_service_dbus_init (SoundServiceDbus *self)
GError *error = NULL;
SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self);
- priv->system_bus = NULL;
priv->connection = NULL;
priv->volume_percent = 0;
+ priv->mute = FALSE;
+ priv->sink_availability = FALSE;
- /* Get the system bus */
- priv->system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
- /* Put the object on DBus */
+ /* Fetch the session bus */
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("sound-service-dbus:Unable to connect to the session bus when creating indicator sound service : %s", error->message);
g_error_free(error);
return;
}
+ /* register the service on it */
dbus_g_connection_register_g_object(priv->connection,
"/org/ayatana/indicator/sound/service",
G_OBJECT(self));
-
- return;
}
@@ -178,6 +186,14 @@ static gboolean sound_service_dbus_get_sink_mute (SoundServiceDbus *self, gboole
return TRUE;
}
+static gboolean sound_service_dbus_get_sink_availability (SoundServiceDbus *self, gboolean *availability_input, GError** gerror)
+{
+ SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self);
+ g_debug("Get sink availability - sound service dbus!, about to send over availability_value of %i", priv->sink_availability);
+ *availability_input = priv->sink_availability;
+ return TRUE;
+}
+
/**
SIGNALS
Utility methods to emit signals from the service into the ether.
@@ -216,5 +232,18 @@ void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, gboolean sink_mu
priv->mute);
}
+void sound_service_dbus_update_sink_availability(SoundServiceDbus* obj, gboolean sink_availability)
+{
+ g_debug("Emitting signal: SINK_AVAILABILITY_UPDATE, with value %i", sink_availability);
+
+ SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (obj);
+ priv->sink_availability = sink_availability;
+
+ g_signal_emit(obj,
+ signals[SINK_AVAILABLE_UPDATE],
+ 0,
+ priv->sink_availability);
+}
+
diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h
index ef0d7dd..ae4953e 100644
--- a/src/sound-service-dbus.h
+++ b/src/sound-service-dbus.h
@@ -49,10 +49,6 @@ struct _SoundServiceDbus {
struct _SoundServiceDbusClass {
GObjectClass parent_class;
- /* Signals -> outward messages to the DBUS and beyond*/
- // TODO - ARE THESE NECESSARY ?
- //void (* sink_input_while_muted) (SoundServiceDbus *self, gboolean block_value, gpointer sound_data);
- //void (* sink_volume_update) (SoundServiceDbus *self, gdouble sink_volume, gpointer sound_data);
};
GType sound_service_dbus_get_type (void) G_GNUC_CONST;
@@ -60,6 +56,7 @@ GType sound_service_dbus_get_type (void) G_GNUC_CONST;
void sound_service_dbus_sink_input_while_muted (SoundServiceDbus* obj, gboolean block_value);
void sound_service_dbus_update_sink_volume(SoundServiceDbus* obj, gdouble sink_volume);
void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, gboolean sink_mute);
+void sound_service_dbus_update_sink_availability(SoundServiceDbus* obj, gboolean sink_availibity);
G_END_DECLS
diff --git a/src/sound-service-server.h b/src/sound-service-server.h
index f2bcdb2..2773619 100644
--- a/src/sound-service-server.h
+++ b/src/sound-service-server.h
@@ -156,14 +156,15 @@ static const DBusGMethodInfo dbus_glib__sound_service_server_methods[] = {
{ (GCallback) sound_service_dbus_set_sink_volume, dbus_glib_marshal__sound_service_server_BOOLEAN__UINT_POINTER, 0 },
{ (GCallback) sound_service_dbus_get_sink_volume, dbus_glib_marshal__sound_service_server_BOOLEAN__POINTER_POINTER, 64 },
{ (GCallback) sound_service_dbus_get_sink_mute, dbus_glib_marshal__sound_service_server_BOOLEAN__POINTER_POINTER, 138 },
+ { (GCallback) sound_service_dbus_get_sink_availability, dbus_glib_marshal__sound_service_server_BOOLEAN__POINTER_POINTER, 200 },
};
const DBusGObjectInfo dbus_glib__sound_service_server_object_info = {
0,
dbus_glib__sound_service_server_methods,
- 3,
-"org.ayatana.indicator.sound\0SetSinkVolume\0S\0volume_percent\0I\0u\0\0org.ayatana.indicator.sound\0GetSinkVolume\0S\0volume_percent_input\0O\0F\0N\0d\0\0org.ayatana.indicator.sound\0GetSinkMute\0S\0mute_input\0O\0F\0N\0b\0\0\0",
-"org.ayatana.indicator.sound\0SinkInputWhileMuted\0org.ayatana.indicator.sound\0SinkVolumeUpdate\0org.ayatana.indicator.sound\0SinkMuteUpdate\0\0",
+ 4,
+"org.ayatana.indicator.sound\0SetSinkVolume\0S\0volume_percent\0I\0u\0\0org.ayatana.indicator.sound\0GetSinkVolume\0S\0volume_percent_input\0O\0F\0N\0d\0\0org.ayatana.indicator.sound\0GetSinkMute\0S\0mute_input\0O\0F\0N\0b\0\0org.ayatana.indicator.sound\0GetSinkAvailability\0S\0availability_input\0O\0F\0N\0b\0\0\0",
+"org.ayatana.indicator.sound\0SinkInputWhileMuted\0org.ayatana.indicator.sound\0SinkVolumeUpdate\0org.ayatana.indicator.sound\0SinkMuteUpdate\0org.ayatana.indicator.sound\0SinkAvailableUpdate\0\0",
"\0"
};
diff --git a/src/sound-service.c b/src/sound-service.c
index 91b5af3..403b2b0 100644
--- a/src/sound-service.c
+++ b/src/sound-service.c
@@ -19,117 +19,24 @@ 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.h"
-#include "sound-service-dbus.h"
+#include "dbus-menu-manager.h"
#include "pulse-manager.h"
-#include "slider-menu-item.h"
-#include "common-defs.h"
-
-// GTK + DBUS
static GMainLoop *mainloop = NULL;
-static DbusmenuMenuitem *root_menuitem = NULL;
-static DbusmenuMenuitem *mute_all_menuitem = NULL;
-static SliderMenuItem *volume_slider_menuitem = NULL;
-static SoundServiceDbus *dbus_interface = NULL;
-
-// PULSEAUDIO
-static gboolean b_sink_available = FALSE;
-static gboolean b_all_muted = FALSE;
-static gboolean b_pulse_ready = FALSE;
-static gboolean b_startup = TRUE;
-static gdouble volume_percent = 0.0;
-
-static void set_global_mute_from_ui();
-static gboolean idle_routine (gpointer data);
-static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service);
-static void refresh_menu();
+
/**********************************************************************************************************************/
-// Init functions (GTK and DBUS)
+// Init and exit functions
/**********************************************************************************************************************/
-/**
-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;
-}
-
-static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data)
-{
- GError * error = NULL;
- if (!g_spawn_command_line_async("gnome-volume-control", &error))
- {
- g_warning("Unable to show dialog: %s", error->message);
- g_error_free(error);
- }
-}
/**
-rebuild_sound_menu:
-Build the DBus menu items, mute/unmute, slider, separator and sound preferences 'link'
+service_shutdown:
+When the service interface starts to shutdown, we
+should follow it.
**/
-static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service)
-{
- // Mute button
- mute_all_menuitem = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute"));
- g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute_from_ui), NULL);
- dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_ENABLED, b_sink_available);
-
- // Slider
- volume_slider_menuitem = slider_menu_item_new(b_sink_available, volume_percent);
- dbusmenu_menuitem_child_append(root, mute_all_menuitem);
- dbusmenu_menuitem_child_append(root, DBUSMENU_MENUITEM(volume_slider_menuitem));
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_ENABLED,
- b_sink_available);
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_VISIBLE,
- b_sink_available);
- // Separator
- DbusmenuMenuitem *separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_child_append(root, separator);
-
- // Sound preferences dialog
- DbusmenuMenuitem *settings_mi = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(settings_mi, DBUSMENU_MENUITEM_PROP_LABEL,
- _("Sound Preferences..."));
- dbusmenu_menuitem_child_append(root, settings_mi);
- g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(show_sound_settings_dialog), NULL);
-}
-/**
-update_mute_ui:
-'public' method allowing the server to update the mute UI
-**/
-void update_mute_ui(gboolean incoming_mute_value)
-{
- b_all_muted = incoming_mute_value;
- dbusmenu_menuitem_property_set(mute_all_menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL,
- _(b_all_muted == FALSE ? "Mute All" : "Unmute"));
-}
-/**
-set_global_mute_from_ui:
-Callback for the dbusmenuitem button
-**/
-static void set_global_mute_from_ui()
-{
- b_all_muted = !b_all_muted;
- toggle_global_mute(b_all_muted);
- dbusmenu_menuitem_property_set(mute_all_menuitem,
- DBUSMENU_MENUITEM_PROP_LABEL,
- _(b_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)
{
@@ -143,61 +50,10 @@ service_shutdown (IndicatorService *service, gpointer user_data)
return;
}
-void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble percent)
-{
- b_sink_available = sink_available;
- b_all_muted = sink_muted;
- b_pulse_ready = pa_state;
- volume_percent = percent;
- g_debug("update pa state with state %i, availability of %i, mute value of %i and a volume percent is %f", pa_state, sink_available, sink_muted, volume_percent);
- // Only rebuild the menu on start up...
- if(b_startup == TRUE){
- rebuild_sound_menu(root_menuitem, dbus_interface);
- b_startup = FALSE;
- }
- else{
- refresh_menu();
- }
- // Emit the signals after the menus are setup/torn down
- sound_service_dbus_update_sink_volume(dbus_interface, percent);
- sound_service_dbus_update_sink_mute(dbus_interface, sink_muted);
-
-}
-
-static void refresh_menu()
-{
- g_debug("in the refresh menu method");
- if(b_sink_available == FALSE || b_pulse_ready == FALSE)
- {
-
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_ENABLED,
- FALSE);
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_VISIBLE,
- FALSE);
- dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
- DBUSMENU_MENUITEM_PROP_ENABLED,
- FALSE);
-
- }
- else if(b_sink_available == TRUE && b_pulse_ready == TRUE){
-
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_ENABLED,
- TRUE);
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem),
- DBUSMENU_MENUITEM_PROP_VISIBLE,
- TRUE);
- dbusmenu_menuitem_property_set_bool(mute_all_menuitem,
- DBUSMENU_MENUITEM_PROP_ENABLED,
- TRUE);
- }
-}
-
-/* Main, is well, main. It brings everything up and throws
- us into the mainloop of no return. Some refactoring needed.*/
+/**
+main:
+**/
int
main (int argc, char ** argv)
{
@@ -213,16 +69,7 @@ main (int argc, char ** argv)
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);
-
- 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);
- establish_pulse_activities(dbus_interface);
+ dbus_menu_manager_setup();
// Run the loop
mainloop = g_main_loop_new(NULL, FALSE);
diff --git a/src/sound-service.h b/src/sound-service.h
index d36ea41..cefbf45 100644
--- a/src/sound-service.h
+++ b/src/sound-service.h
@@ -27,13 +27,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#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 "dbus-shared-names.h"
@@ -41,7 +34,5 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
// ENTRY AND EXIT POINTS
void service_shutdown(IndicatorService * service, gpointer user_data);
int main (int argc, char ** argv);
-void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble current_vol);
-void update_mute_ui(gboolean incoming_mute_value);
-#endif
+#endif
diff --git a/src/sound-service.xml b/src/sound-service.xml
index 580f0e1..12ed03e 100644
--- a/src/sound-service.xml
+++ b/src/sound-service.xml
@@ -5,6 +5,7 @@
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_set_sink_volume"/>
<arg type='u' name='volume_percent' direction="in"/>
</method>
+
<method name = "GetSinkVolume">
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_volume"/>
<arg type='d' name='volume_percent_input' direction="out"/>
@@ -15,6 +16,11 @@
<arg type='b' name='mute_input' direction="out"/>
</method>
+ <method name = "GetSinkAvailability">
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_availability"/>
+ <arg type='b' name='availability_input' direction="out"/>
+ </method>
+
<!-- Will need to hook up another signal which monitors for volume change
Our respective UI element should listen to this and therefore will be updated with accurate setting-->
<!-- Triggered when a sink is muted but the input has been sent to that sink -->
@@ -30,6 +36,10 @@ Our respective UI element should listen to this and therefore will be updated wi
<arg name="mute_value" type="b" direction="out"/>
</signal>
+ <signal name="SinkAvailableUpdate">
+ <arg name="available_value" type="b" direction="out"/>
+ </signal>
+
</interface>
</node>