diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/common-defs.h | 25 | ||||
-rw-r--r-- | src/dbus-menu-manager.c | 234 | ||||
-rw-r--r-- | src/dbus-menu-manager.h | 34 | ||||
-rw-r--r-- | src/dbus-shared-names.h | 7 | ||||
-rw-r--r-- | src/indicator-sound.c | 673 | ||||
-rw-r--r-- | src/indicator-sound.h | 1 | ||||
-rw-r--r-- | src/metadata-widget.c | 33 | ||||
-rw-r--r-- | src/mpris2-controller.vala | 55 | ||||
-rw-r--r-- | src/mute-menu-item.c | 130 | ||||
-rw-r--r-- | src/mute-menu-item.h | 59 | ||||
-rw-r--r-- | src/pulse-manager.c | 54 | ||||
-rw-r--r-- | src/pulse-manager.h | 3 | ||||
-rw-r--r-- | src/slider-menu-item.c | 42 | ||||
-rw-r--r-- | src/slider-menu-item.h | 8 | ||||
-rw-r--r-- | src/sound-service-dbus.c | 324 | ||||
-rw-r--r-- | src/sound-service-dbus.h | 16 | ||||
-rw-r--r-- | src/sound-service.c | 8 | ||||
-rw-r--r-- | src/sound-service.h | 3 | ||||
-rw-r--r-- | src/sound-service.list | 2 | ||||
-rw-r--r-- | src/sound-service.xml | 30 | ||||
-rw-r--r-- | src/sound-state-manager.c | 388 | ||||
-rw-r--r-- | src/sound-state-manager.h | 66 | ||||
-rw-r--r-- | src/transport-menu-item.vala | 3 | ||||
-rw-r--r-- | src/volume-widget.c | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/test-indicator-sound-dbus-client.c | 6 |
29 files changed, 1183 insertions, 1039 deletions
diff --git a/Makefile.am b/Makefile.am index 466806a..86af943 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,6 @@ SUBDIRS = \ src \ data \ - tests \ po EXTRA_DIST = autogen.sh vapi/common-defs.vapi diff --git a/configure.ac b/configure.ac index f36c9e2..dfc78ab 100644 --- a/configure.ac +++ b/configure.ac @@ -148,7 +148,6 @@ data/icons/16x16/Makefile data/icons/16x16/status/Makefile data/icons/scalable/Makefile data/icons/scalable/status/Makefile -tests/Makefile po/Makefile.in ]) diff --git a/src/Makefile.am b/src/Makefile.am index 32b6928..ed64aeb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,11 +9,13 @@ soundmenulib_LTLIBRARIES = libsoundmenu.la libsoundmenu_la_SOURCES = \ common-defs.h \ indicator-sound.h \ + indicator-sound.c \ + sound-state-manager.c \ + sound-state-manager.h \ transport-widget.c \ transport-widget.h \ metadata-widget.c \ metadata-widget.h \ - indicator-sound.c \ title-widget.c \ title-widget.h \ volume-widget.c \ @@ -87,15 +89,15 @@ 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 \ sound-service-dbus.c \ slider-menu-item.h \ slider-menu-item.c \ - gen-sound-service.xml.h \ + mute-menu-item.h \ + mute-menu-item.c \ + gen-sound-service.xml.h \ gen-sound-service.xml.c \ $(music_bridge_VALASOURCES:.vala=.c) diff --git a/src/common-defs.h b/src/common-defs.h index 8ec69e8..5458dc5 100644 --- a/src/common-defs.h +++ b/src/common-defs.h @@ -16,13 +16,21 @@ 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 __COMMON_DEFS_H__ +#define __COMMON_DEFS_H__ -/* constants used for signals on the dbus. This file is shared between client and server implementation */ -#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" +typedef enum { + MUTED, + ZERO_LEVEL, + LOW_LEVEL, + MEDIUM_LEVEL, + HIGH_LEVEL, + BLOCKED, + UNAVAILABLE, + AVAILABLE +}SoundState; + #define DBUSMENU_PROPERTY_EMPTY -1 @@ -30,6 +38,9 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #define DBUSMENU_VOLUME_MENUITEM_TYPE "x-canonical-ido-volume-type" #define DBUSMENU_VOLUME_MENUITEM_LEVEL "x-canonical-ido-volume-level" +#define DBUSMENU_MUTE_MENUITEM_TYPE "x-canonical-sound-menu-mute-type" +#define DBUSMENU_MUTE_MENUITEM_VALUE "x-canonical-sound-menu-mute-value" + #define DBUSMENU_TRANSPORT_MENUITEM_TYPE "x-canonical-sound-menu-player-transport-type" #define DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE "x-canonical-sound-menu-player-transport-state" @@ -51,4 +62,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #define DBUSMENU_PLAYLISTS_MENUITEM_TYPE "x-canonical-sound-menu-player-playlists-type" #define DBUSMENU_PLAYLISTS_MENUITEM_TITLE "x-canonical-sound-menu-player-playlists-title" -#define DBUSMENU_PLAYLISTS_MENUITEM_PLAYLISTS "x-canonical-sound-menu-player-playlists-playlists"
\ No newline at end of file +#define DBUSMENU_PLAYLISTS_MENUITEM_PLAYLISTS "x-canonical-sound-menu-player-playlists-playlists" + +#endif diff --git a/src/dbus-menu-manager.c b/src/dbus-menu-manager.c deleted file mode 100644 index 92bfba5..0000000 --- a/src/dbus-menu-manager.c +++ /dev/null @@ -1,234 +0,0 @@ -/* -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 <libdbusmenu-glib/server.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 "common-defs.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: -**/ -DbusmenuMenuitem* 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_MENU_DBUS_OBJECT_PATH); - dbusmenu_server_set_root(server, root_menuitem); - establish_pulse_activities(dbus_interface); - return root_menuitem; -} - -void dbus_menu_manager_update_volume(gdouble volume) -{ - GVariant* new_volume = g_variant_new_double(volume); - dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(volume_slider_menuitem), - DBUSMENU_VOLUME_MENUITEM_LEVEL, - new_volume); -} - - -/** -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); - dbus_menu_manager_update_volume(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") : _("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); - } -} - - -/** -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 --page=applications", &error) && - !g_spawn_command_line_async("xfce4-mixer", &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") : _("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") : _("Unmute")); -} - - diff --git a/src/dbus-menu-manager.h b/src/dbus-menu-manager.h deleted file mode 100644 index ec2b2e2..0000000 --- a/src/dbus-menu-manager.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __INCLUDE_DBUS_MENU_MANAGER_H__ -#define __INCLUDE_DBUS_MENU_MANAGER_H__ - -#include <libdbusmenu-glib/menuitem.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/>. -*/ - -DbusmenuMenuitem* dbus_menu_manager_setup(); -void dbus_menu_manager_teardown(); -void dbus_menu_manager_update_volume(gdouble volume); -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/dbus-shared-names.h b/src/dbus-shared-names.h index bdc40cb..346a031 100644 --- a/src/dbus-shared-names.h +++ b/src/dbus-shared-names.h @@ -23,16 +23,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef __DBUS_SHARED_NAMES_H__ -#define __DBUS_SHARED_NAMES_H__ 1 +#define __DBUS_SHARED_NAMES_H__ #define INDICATOR_SOUND_DBUS_NAME "com.canonical.indicators.sound" #define INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH "/com/canonical/indicators/sound/menu" #define INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH "/com/canonical/indicators/sound/service" #define INDICATOR_SOUND_DBUS_INTERFACE "com.canonical.indicators.sound" #define INDICATOR_SOUND_DBUS_VERSION 0 -#define INDICATOR_SOUND_SIGNAL_SINK_INPUT_WHILE_MUTED "SinkInputWhileMuted" -#define INDICATOR_SOUND_SIGNAL_SINK_MUTE_UPDATE "SinkMuteUpdate" -#define INDICATOR_SOUND_SIGNAL_SINK_AVAILABLE_UPDATE "SinkAvailableUpdate" + +#define INDICATOR_SOUND_SIGNAL_STATE_UPDATE "SoundStateUpdate" #endif /* __DBUS_SHARED_NAMES_H__ */ diff --git a/src/indicator-sound.c b/src/indicator-sound.c index 8e86f8b..29a9f91 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -1,12 +1,8 @@ /* -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> - 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 @@ -20,6 +16,7 @@ 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 <math.h> #include <glib.h> #include <glib-object.h> @@ -42,6 +39,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include "gen-sound-service.xml.h" #include "common-defs.h" +#include "sound-state-manager.h" typedef struct _IndicatorSoundPrivate IndicatorSoundPrivate; @@ -49,7 +47,8 @@ struct _IndicatorSoundPrivate { GtkWidget* volume_widget; GList* transport_widgets_list; - GDBusProxy *dbus_proxy; + GDBusProxy *dbus_proxy; + SoundStateManager* state_manager; NotifyNotification* notification; }; @@ -70,22 +69,32 @@ 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); -static void indicator_sound_scroll (IndicatorObject* io, gint delta, IndicatorScrollDirection direction); +static void indicator_sound_scroll (IndicatorObject* io, + gint delta, + IndicatorScrollDirection direction); //Notification static void indicator_sound_notification_init (IndicatorSound *self); -static void indicator_sound_notification_show (IndicatorSound *self, double value); +static void indicator_sound_notification_show (IndicatorSound *self, + SoundState state, double value); -//Slider related -static gboolean new_volume_slider_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client); +//key/moust event handlers static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data); static gboolean key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data); -static void style_changed_cb(GtkWidget *widget, gpointer user_data); -//player widget realisation methods -static gboolean new_transport_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client); -static gboolean new_metadata_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client); -static gboolean new_title_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client); +//custom widget realisation methods +static gboolean new_volume_slider_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client); +static gboolean new_transport_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client); +static gboolean new_metadata_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client); +static gboolean new_title_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client); // DBUS communication @@ -97,60 +106,6 @@ static void create_connection_to_service (GObject *source_object, static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata); -static void g_signal_cb ( GDBusProxy* proxy, - gchar* sender_name, - gchar* signal_name, - GVariant* parameters, - gpointer user_data); - -static void react_to_signal_sink_input_while_muted (gboolean value, - IndicatorSound* self); -static void react_to_signal_sink_mute_update (gboolean value, - IndicatorSound* self); -static void react_to_signal_sink_availability_update (gboolean value, - IndicatorSound* self); -static void fetch_state ( IndicatorSound* self ); - -static void get_sink_mute_cb ( GObject *object, - GAsyncResult *res, - gpointer user_data ); - -static void get_sink_availability_cb ( GObject *object, - GAsyncResult *res, - gpointer user_data ); - -/****Volume States 'members' ***/ -static void update_state(const gint state); - -static const gint STATE_INVALID = -1; -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 = 6; - -static GHashTable *volume_states = NULL; -static GtkImage *speaker_image = NULL; -static gint current_state = 0; -static gint previous_state = 0; - -static gboolean initial_mute ; -static gboolean device_available; - -static GtkIconSize design_team_size; -static gint blocked_id; -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(); -static gboolean start_animation(); -static void reset_mute_blocking_animation(); -static void free_the_animation_list(); - static void indicator_sound_class_init (IndicatorSoundClass *klass) @@ -168,7 +123,6 @@ indicator_sound_class_init (IndicatorSoundClass *klass) io_class->get_image = get_icon; io_class->get_menu = get_menu; io_class->scroll = indicator_sound_scroll; - design_team_size = gtk_icon_size_register("design-team-size", 22, 22); } static void @@ -177,43 +131,32 @@ indicator_sound_init (IndicatorSound *self) self->service = NULL; self->service = indicator_service_manager_new_version(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_VERSION); - - prepare_state_machine(); - prepare_blocked_animation(); - animation_id = 0; - blocked_id = 0; - initial_mute = FALSE; - device_available = TRUE; IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); priv->volume_widget = NULL; priv->dbus_proxy = NULL; GList* t_list = NULL; priv->transport_widgets_list = t_list; + priv->state_manager = g_object_new (SOUND_TYPE_STATE_MANAGER, NULL); priv->notification = NULL; - priv->notification = NULL; indicator_sound_notification_init (self); - - g_signal_connect(G_OBJECT(self->service), - INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, - G_CALLBACK(connection_changed), self); + + g_signal_connect ( G_OBJECT(self->service), + INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, + G_CALLBACK(connection_changed), self ); } static void indicator_sound_dispose (GObject *object) { IndicatorSound * self = INDICATOR_SOUND(object); + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); if (self->service != NULL) { g_object_unref(G_OBJECT(self->service)); self->service = NULL; } - g_hash_table_destroy(volume_states); - - free_the_animation_list(); - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (self)); g_list_free ( priv->transport_widgets_list ); @@ -225,7 +168,6 @@ indicator_sound_dispose (GObject *object) return; } - static void indicator_sound_finalize (GObject *object) { @@ -242,14 +184,9 @@ get_label (IndicatorObject * io) 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; + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + gtk_widget_show( GTK_WIDGET(sound_state_manager_get_current_icon (priv->state_manager)) ); + return sound_state_manager_get_current_icon (priv->state_manager); } /* Indicator based function to get the menu for the whole @@ -267,124 +204,16 @@ get_menu (IndicatorObject * io) dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_TRANSPORT_MENUITEM_TYPE, new_transport_widget); dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_METADATA_MENUITEM_TYPE, new_metadata_widget); dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_TITLE_MENUITEM_TYPE, new_title_widget); - // 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), io); - g_signal_connect(menu, "key-release-event", G_CALLBACK(key_release_cb), io); + // Note: Not ideal but all key handling needs to be managed here and then + // delegated to the appropriate widget. + g_signal_connect (menu, "key-press-event", G_CALLBACK(key_press_cb), io); + g_signal_connect (menu, "key-release-event", G_CALLBACK(key_release_cb), io); return GTK_MENU(menu); } static void -free_the_animation_list() -{ - if (blocked_animation_list != NULL) { - g_list_foreach (blocked_animation_list, (GFunc)g_object_unref, NULL); - g_list_free(blocked_animation_list); - blocked_animation_list = NULL; - } -} - -static gboolean -new_transport_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) -{ - g_debug("indicator-sound: new_transport_bar() called "); - - GtkWidget* bar = NULL; - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - bar = transport_widget_new(newitem); - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - priv->transport_widgets_list = g_list_append ( priv->transport_widgets_list, bar ); - - GtkMenuItem *menu_transport_bar = GTK_MENU_ITEM(bar); - - gtk_widget_show_all(bar); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_transport_bar, parent); - - return TRUE; -} - -static gboolean -new_metadata_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) -{ - g_debug("indicator-sound: new_metadata_widget"); - - GtkWidget* metadata = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - metadata = metadata_widget_new (newitem); - GtkMenuItem *menu_metadata_widget = GTK_MENU_ITEM(metadata); - - gtk_widget_show_all(metadata); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_metadata_widget, parent); - - return TRUE; -} - -static gboolean -new_title_widget(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); - - g_debug ("%s (\"%s\")", __func__, dbusmenu_menuitem_property_get(newitem, DBUSMENU_TITLE_MENUITEM_NAME)); - - GtkWidget* title = NULL; - - title = title_widget_new (newitem); - GtkMenuItem *menu_title_widget = GTK_MENU_ITEM(title); - - gtk_widget_show_all(title); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), - newitem, - menu_title_widget, parent); - return TRUE; -} - -static gboolean -new_volume_slider_widget(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) -{ - g_debug("indicator-sound: new_volume_slider_widget"); - - GtkWidget* volume_widget = NULL; - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - volume_widget = volume_widget_new (newitem); - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - priv->volume_widget = volume_widget; - - if (priv->notification) - notify_notification_attach_to_widget(priv->notification, volume_widget); - - GtkWidget* ido_slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - - g_signal_connect(ido_slider_widget, "style-set", G_CALLBACK(style_changed_cb), NULL); - gtk_widget_set_sensitive(ido_slider_widget, - !initial_mute); - gtk_widget_show_all(ido_slider_widget); - - - GtkMenuItem *menu_volume_item = GTK_MENU_ITEM(ido_slider_widget); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), - newitem, - menu_volume_item, - parent); - fetch_state(INDICATOR_SOUND (io)); - return TRUE; -} - -static void indicator_sound_notification_init (IndicatorSound *self) +indicator_sound_notification_init (IndicatorSound *self) { IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); @@ -421,7 +250,7 @@ connection_changed (IndicatorServiceManager * sm, GError *error = NULL; if (connected == FALSE){ - update_state (STATE_SINKS_NONE); + sound_state_manager_deal_with_disconnect (priv->state_manager); return; //TODO: Gracefully handle disconnection // do a timeout to wait for reconnection @@ -432,7 +261,14 @@ connection_changed (IndicatorServiceManager * sm, // we don't need to anything, gdbus takes care of the rest - bless. // just fetch the state. if (priv->dbus_proxy != NULL){ - fetch_state (indicator); + g_dbus_proxy_call ( priv->dbus_proxy, + "GetSoundState", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback)sound_state_manager_get_state_cb, + priv->state_manager); return; } @@ -484,344 +320,121 @@ static void create_connection_to_service (GObject *source_object, g_error_free(error); return; } - - g_signal_connect(priv->dbus_proxy, "g-signal", - G_CALLBACK(g_signal_cb), self); - - fetch_state (self); -} - -static void fetch_state (IndicatorSound* self) -{ - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - if(priv->volume_widget != NULL){ - update_state_from_volume (volume_widget_get_current_volume(priv->volume_widget)); - } + g_debug ("Connection to dbus seemed to work fine from the indicator side"); + sound_state_manager_connect_to_dbus (priv->state_manager, + priv->dbus_proxy); - g_dbus_proxy_call ( priv->dbus_proxy, - "GetSinkMute", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback)get_sink_mute_cb, - self); - - g_dbus_proxy_call ( priv->dbus_proxy, - "GetSinkAvailability", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback)get_sink_availability_cb, - self); - } -static void get_sink_availability_cb ( GObject *object, - GAsyncResult *res, - gpointer user_data ) +static gboolean +new_transport_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client) { - IndicatorSound* self = INDICATOR_SOUND(user_data); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - - GVariant *result, *value; - GError *error = NULL; - - result = g_dbus_proxy_call_finish ( priv->dbus_proxy, - res, - &error ); - - if (error != NULL) { - g_debug("get_sink_availability call failed: %s", error->message); - g_error_free(error); - return; - } - - value = g_variant_get_child_value(result, 0); - device_available = g_variant_get_boolean(value); - - if (device_available == FALSE) { - update_state(STATE_SINKS_NONE); - } - - if(priv->volume_widget != NULL){ - GtkWidget* slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - gtk_widget_set_sensitive(slider_widget, device_available); - } - - g_variant_unref(value); - g_variant_unref(result); -} + g_debug("indicator-sound: new_transport_bar() called "); + GtkWidget* bar = NULL; + IndicatorObject *io = NULL; -static void get_sink_mute_cb ( GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - IndicatorSound* self = INDICATOR_SOUND(user_data); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - - GVariant *result, *value; - GError *error = NULL; - gboolean is_muted; - result = g_dbus_proxy_call_finish ( priv->dbus_proxy, - res, - &error ); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - if (error != NULL) { - g_debug("get_sink_mute call failed: %s", error->message); - g_error_free(error); - return; - } + bar = transport_widget_new(newitem); + io = g_object_get_data (G_OBJECT (client), "indicator"); + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + priv->transport_widgets_list = g_list_append ( priv->transport_widgets_list, bar ); - value = g_variant_get_child_value(result, 0); - is_muted = g_variant_get_boolean(value); - - if ( is_muted == TRUE ){ - update_state(STATE_MUTED); - } - - g_variant_unref(value); - g_variant_unref(result); -} + GtkMenuItem *menu_transport_bar = GTK_MENU_ITEM(bar); -/* -Prepare states Array. -*/ -void -prepare_state_machine() -{ - 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")); + gtk_widget_show_all(bar); + dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client), + newitem, + menu_transport_bar, + parent); + return TRUE; } -/* -prepare_blocked_animation: -Prepares the array of images to be used in the blocked animation. -Only called at startup. -*/ -static void -prepare_blocked_animation() +static gboolean +new_metadata_widget (DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client) { - 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)); - - GtkImage* temp_image = indicator_image_helper(muted_name); - GdkPixbuf* mute_buf = gtk_image_get_pixbuf(temp_image); - - temp_image = indicator_image_helper(blocked_name); - GdkPixbuf* blocked_buf = gtk_image_get_pixbuf(temp_image); - - if (mute_buf == NULL || blocked_buf == NULL) { - //g_debug("Don bother with the animation, the theme aint got the goods !"); - return; - } - - int i; - - // sample 51 snapshots - range : 0-256 - for (i = 0; i < 51; 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 * 5)); - blocked_animation_list = g_list_append(blocked_animation_list, gdk_pixbuf_copy(blocked_buf)); - } - g_object_ref_sink(mute_buf); - g_object_unref(mute_buf); - g_object_ref_sink(blocked_buf); - g_object_unref(blocked_buf); -} + g_debug("indicator-sound: new_metadata_widget"); -gint -get_state() -{ - return current_state; -} + GtkWidget* metadata = NULL; -gchar* -get_state_image_name(gint state) -{ - return g_hash_table_lookup(volume_states, GINT_TO_POINTER(state)); -} + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); -void -prepare_for_tests(IndicatorObject *io) -{ - prepare_state_machine(); - get_icon(io); -} + metadata = metadata_widget_new (newitem); + GtkMenuItem *menu_metadata_widget = GTK_MENU_ITEM(metadata); -void -tidy_up_hash() -{ - g_hash_table_destroy(volume_states); + gtk_widget_show_all(metadata); + dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client), + newitem, menu_metadata_widget, parent); + return TRUE; } -static void -update_state(const gint state) +static gboolean +new_title_widget(DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client) { - previous_state = current_state; - current_state = state; - gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state)); - indicator_image_helper_update(speaker_image, image_name); -} + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); -static gint -determine_state_from_volume(gdouble volume_percent) -{ - if (device_available == FALSE) - return STATE_INVALID; - 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) { - state = STATE_MEDIUM; - } else if (volume_percent >= 70.0) { - state = STATE_HIGH; - } else if (volume_percent == 0.0) { - state = STATE_ZERO; - } - return state; -} + g_debug ("%s (\"%s\")", __func__, dbusmenu_menuitem_property_get(newitem, DBUSMENU_TITLE_MENUITEM_NAME)); -void -update_state_from_volume(gdouble volume_percent) -{ - gint state = determine_state_from_volume(volume_percent); - if (state == STATE_INVALID) - return; + GtkWidget* title = NULL; - update_state(state); -} + title = title_widget_new (newitem); + GtkMenuItem *menu_title_widget = GTK_MENU_ITEM(title); + + gtk_widget_show_all(title); -static gboolean -start_animation() -{ - blocked_iter = blocked_animation_list; - blocked_id = 0; - animation_id = g_timeout_add(50, fade_back_to_mute_image, NULL); - return FALSE; + dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), + newitem, + menu_title_widget, parent); + return TRUE; } static gboolean -fade_back_to_mute_image() +new_volume_slider_widget(DbusmenuMenuitem * newitem, + DbusmenuMenuitem * parent, + DbusmenuClient * client) { - if (blocked_iter != NULL) { - 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 now\n"); - return FALSE; - } -} - -static void -reset_mute_blocking_animation() -{ - if (animation_id != 0) { - //g_debug("about to remove the animation_id callback from the mainloop!!**"); - g_source_remove(animation_id); - animation_id = 0; - } - if (blocked_id != 0) { - //g_debug("about to remove the blocked_id callback from the mainloop!!**"); - g_source_remove(blocked_id); - blocked_id = 0; - } -} - - - -/*******************************************************************/ -// DBUS Signal reactions -/*******************************************************************/ -static void g_signal_cb ( GDBusProxy* proxy, - gchar* sender_name, - gchar* signal_name, - GVariant* parameters, - gpointer user_data) -{ - IndicatorSound *self = INDICATOR_SOUND(user_data); - g_return_if_fail ( IS_INDICATOR_SOUND(self) ); + g_debug("indicator-sound: new_volume_slider_widget"); - g_variant_ref (parameters); - GVariant *value = g_variant_get_child_value (parameters, 0); - gboolean input = g_variant_get_boolean (value); - g_variant_unref (parameters); + GtkWidget* volume_widget = NULL; + IndicatorObject *io = NULL; - g_debug ( "!!! signal_cb with value %i", input); - - if (g_strcmp0(signal_name, INDICATOR_SOUND_SIGNAL_SINK_AVAILABLE_UPDATE) == 0){ - react_to_signal_sink_availability_update ( input, self ); - } - else if (g_strcmp0(signal_name, INDICATOR_SOUND_SIGNAL_SINK_MUTE_UPDATE) == 0){ - react_to_signal_sink_mute_update ( input, self ); - } - else if (g_strcmp0(signal_name, INDICATOR_SOUND_SIGNAL_SINK_INPUT_WHILE_MUTED) == 0){ - react_to_signal_sink_input_while_muted ( input, self ); - } -} + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); -static void -react_to_signal_sink_input_while_muted(gboolean block_value, IndicatorSound* self) -{ - //g_debug("signal caught - sink input while muted with value %i", block_value); - if (block_value == 1 && blocked_id == 0 && animation_id == 0 && blocked_animation_list != NULL) { - gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT)); - indicator_image_helper_update(speaker_image, image_name); - blocked_id = g_timeout_add_seconds(5, start_animation, NULL); - } -} + volume_widget = volume_widget_new (newitem); + io = g_object_get_data (G_OBJECT (client), "indicator"); + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + priv->volume_widget = volume_widget; -/* - 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. -*/ -static void -react_to_signal_sink_mute_update(gboolean mute_value, IndicatorSound* self) -{ - if (mute_value == TRUE && device_available == TRUE) { - update_state(STATE_MUTED); - } else { - reset_mute_blocking_animation(); - } - //g_debug("signal caught - sink mute update with mute value: %i", mute_value); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); + GtkWidget* ido_slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - if(priv->volume_widget == NULL){ - return; - } - GtkWidget* slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - gtk_widget_set_sensitive(slider_widget, !mute_value); - if(mute_value == FALSE){ - update_state_from_volume (volume_widget_get_current_volume(priv->volume_widget)); - } -} + gtk_widget_show_all(ido_slider_widget); + // register the style callback on this widget with state manager's style change + // handler (needs to remake the blocking animation for each style). + g_signal_connect (ido_slider_widget, "style-set", + G_CALLBACK(sound_state_manager_style_changed_cb), + priv->state_manager); + GtkMenuItem *menu_volume_item = GTK_MENU_ITEM(ido_slider_widget); + dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), + newitem, + menu_volume_item, + parent); -static void -react_to_signal_sink_availability_update(gboolean available_value, IndicatorSound* self) -{ - device_available = available_value; - if (device_available == FALSE) { - update_state(STATE_SINKS_NONE); - return; - } - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); + if (priv->notification) + notify_notification_attach_to_widget(priv->notification, volume_widget); - update_state_from_volume (volume_widget_get_current_volume(priv->volume_widget)); - //g_debug("signal caught - sink availability update with value: %i", available_value); + return TRUE; } /*******************************************************************/ @@ -885,7 +498,7 @@ key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) break; } new_value = CLAMP(new_value, 0, 100); - if (new_value != current_value && current_state != STATE_MUTED) { + if (new_value != current_value && sound_state_manager_get_current_state (priv->state_manager) != MUTED) { //g_debug("Attempting to set the range from the key listener to %f", new_value); volume_widget_update(VOLUME_WIDGET(priv->volume_widget), new_value); } @@ -983,18 +596,7 @@ key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) } static void -style_changed_cb(GtkWidget *widget, gpointer user_data) -{ - //g_debug("Just caught a style change event"); - update_state(current_state); - reset_mute_blocking_animation(); - update_state(current_state); - free_the_animation_list(); - prepare_blocked_animation(); -} - -static void -indicator_sound_notification_show(IndicatorSound *self, double value) +indicator_sound_notification_show(IndicatorSound *self, SoundState state, double value) { IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); @@ -1003,16 +605,15 @@ indicator_sound_notification_show(IndicatorSound *self, double value) char *icon; const int notify_value = CLAMP((int)value, -1, 101); - gint state = determine_state_from_volume (CLAMP(value, 0, 100)); - if (state == STATE_ZERO) { + if (state == ZERO_LEVEL) { // Not available for all the themes icon = "audio-volume-off"; - } else if (state == STATE_LOW) { + } else if (state == LOW_LEVEL) { icon = "audio-volume-low"; - } else if (state == STATE_MEDIUM) { + } else if (state == MEDIUM_LEVEL) { icon = "audio-volume-medium"; - } else if (state == STATE_HIGH) { + } else if (state == HIGH_LEVEL) { icon = "audio-volume-high"; } else { icon = "audio-volume-muted"; @@ -1024,13 +625,15 @@ indicator_sound_notification_show(IndicatorSound *self, double value) } static void -indicator_sound_scroll (IndicatorObject *io, gint delta, IndicatorScrollDirection direction) +indicator_sound_scroll (IndicatorObject *io, gint delta, + IndicatorScrollDirection direction) { //g_debug("indicator-sound-scroll - current slider value"); + IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); + SoundState current_state = sound_state_manager_get_current_state (priv->state_manager); - if (device_available == FALSE || current_state == STATE_MUTED) + if (current_state == UNAVAILABLE || current_state == MUTED) return; - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); GtkWidget* slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)slider_widget); @@ -1048,5 +651,5 @@ indicator_sound_scroll (IndicatorObject *io, gint delta, IndicatorScrollDirectio //g_debug("indicator-sound-scroll - update slider with value %f", value); volume_widget_update(VOLUME_WIDGET(priv->volume_widget), value); - indicator_sound_notification_show(INDICATOR_SOUND (io), value); + indicator_sound_notification_show(INDICATOR_SOUND (io), current_state, value); } diff --git a/src/indicator-sound.h b/src/indicator-sound.h index 276136d..a7468fb 100644 --- a/src/indicator-sound.h +++ b/src/indicator-sound.h @@ -28,7 +28,6 @@ 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> #define INDICATOR_SOUND_TYPE (indicator_sound_get_type ()) #define INDICATOR_SOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_SOUND_TYPE, IndicatorSound)) diff --git a/src/metadata-widget.c b/src/metadata-widget.c index 09365d5..a37053b 100644 --- a/src/metadata-widget.c +++ b/src/metadata-widget.c @@ -101,8 +101,7 @@ metadata_widget_init (MetadataWidget *self) // image priv->album_art = gtk_image_new(); - priv->image_path = g_string_new(dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTURL)); + priv->image_path = g_string_new(""); priv->old_image_path = g_string_new(""); g_signal_connect(priv->album_art, "expose-event", @@ -120,8 +119,7 @@ metadata_widget_init (MetadataWidget *self) // artist GtkWidget* artist; - artist = gtk_label_new(dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTIST)); + artist = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(artist), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(artist), (gfloat)10, (gfloat)0); gtk_widget_set_size_request (artist, 140, 15); @@ -131,8 +129,7 @@ metadata_widget_init (MetadataWidget *self) // title GtkWidget* piece; - piece = gtk_label_new(dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_TITLE) ); + piece = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(piece), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(piece), (gfloat)10, (gfloat)-5); gtk_widget_set_size_request (piece, 140, 15); @@ -142,8 +139,7 @@ metadata_widget_init (MetadataWidget *self) // container GtkWidget* container; - container = gtk_label_new(dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ALBUM) ); + container = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(container), (gfloat)0, (gfloat)0); gtk_misc_set_padding (GTK_MISC(container), (gfloat)10, (gfloat)0); gtk_widget_set_size_request (container, 140, 15); @@ -397,7 +393,6 @@ metadata_widget_button_press_event (GtkWidget *menuitem, return FALSE; } -// TODO: Manage empty/mangled music details <unknown artist> etc. static void metadata_widget_property_update(DbusmenuMenuitem* item, gchar* property, GVariant* value, gpointer userdata) @@ -480,16 +475,18 @@ metadata_widget_set_twin_item(MetadataWidget* self, metadata_widget_style_labels( self, GTK_LABEL(priv->artist_label)); g_string_erase(priv->image_path, 0, -1); - g_string_overwrite( priv->image_path, - 0, - dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTURL )); + const gchar *arturl = dbusmenu_menuitem_property_get( priv->twin_item, + DBUSMENU_METADATA_MENUITEM_ARTURL ); + if (arturl != NULL){ + g_string_overwrite( priv->image_path, + 0, + arturl); - // if its a remote image queue a redraw incase the download took too long - if (g_str_has_prefix (dbusmenu_menuitem_property_get (priv->twin_item, DBUSMENU_METADATA_MENUITEM_ARTURL ), - g_get_user_cache_dir())){ - gtk_widget_queue_draw(GTK_WIDGET(self)); - } + // if its a remote image queue a redraw incase the download took too long + if (g_str_has_prefix (arturl, g_get_user_cache_dir())){ + gtk_widget_queue_draw(GTK_WIDGET(self)); + } + } } /** diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index fc61c12..a7b3de1 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -93,6 +93,16 @@ public class Mpris2Controller : GLib.Object if ( playlist_v != null && this.owner.use_playlists == true ){ this.fetch_active_playlist(); } + Variant? playlist_count_v = changed_properties.lookup("PlaylistCount"); + if ( playlist_count_v != null && this.owner.use_playlists == true ){ + this.fetch_playlists(); + this.fetch_active_playlist(); + } + Variant? playlist_orderings_v = changed_properties.lookup("Orderings"); + if ( playlist_orderings_v != null && this.owner.use_playlists == true ){ + this.fetch_playlists(); + this.fetch_active_playlist(); + } } private bool ensure_correct_playback_status(){ @@ -107,16 +117,20 @@ public class Mpris2Controller : GLib.Object GLib.HashTable<string, Variant?> changed_updates = this.player.Metadata; Variant? artist_v = this.player.Metadata.lookup("xesam:artist"); if(artist_v != null){ - string[] artists = (string[])this.player.Metadata.lookup("xesam:artist"); - string display_artists = string.joinv(", ", artists); + Variant? v_artists = this.player.Metadata.lookup("xesam:artist"); + debug("artists is of type %s", v_artists.get_type_string ()); + string display_artists; + if(v_artists.get_type_string() == "s"){ + debug("SPOTIFY is that you ?"); + display_artists = v_artists.get_string(); + } + else{ + string[] artists = v_artists.dup_strv(); + display_artists = string.joinv(", ", artists); + } changed_updates.replace("xesam:artist", display_artists); debug("artist : %s", (string)changed_updates.lookup("xesam:artist")); } - Variant? length_v = this.player.Metadata.lookup("mpris:length"); - if(length_v != null){ - int64 duration = this.player.Metadata.lookup("mpris:length").get_int64(); - changed_updates.replace("mpris:length", duration/1000000); - } return changed_updates; } @@ -161,6 +175,19 @@ public class Mpris2Controller : GLib.Object } } + + public bool connected() + { + return (this.player != null && this.mpris2_root != null); + } + + public void expose() + { + if(this.connected() == true){ + this.mpris2_root.Raise.begin(); + } + } + public void fetch_playlists() { PlaylistDetails[] current_playlists = this.playlists.GetPlaylists(0, @@ -176,7 +203,6 @@ public class Mpris2Controller : GLib.Object warning(" Playlists are on but its returning no current_playlists" ); this.owner.use_playlists = false; } - return; } private void fetch_active_playlist() @@ -188,19 +214,6 @@ public class Mpris2Controller : GLib.Object playlists_item.update_active_playlist ( this.playlists.ActivePlaylist.details ); } - - public bool connected() - { - return (this.player != null && this.mpris2_root != null); - } - - public void expose() - { - if(this.connected() == true){ - this.mpris2_root.Raise.begin(); - } - } - public void activate_playlist (ObjectPath path) { try{ diff --git a/src/mute-menu-item.c b/src/mute-menu-item.c new file mode 100644 index 0000000..2f40177 --- /dev/null +++ b/src/mute-menu-item.c @@ -0,0 +1,130 @@ +/* +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/>. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <glib/gi18n.h> + +#include "common-defs.h" +#include "mute-menu-item.h" +#include "pulse-manager.h" + +typedef struct _MuteMenuItemPrivate MuteMenuItemPrivate; + +struct _MuteMenuItemPrivate { + DbusmenuMenuitem* button; +}; + +#define MUTE_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUTE_MENU_ITEM_TYPE, MuteMenuItemPrivate)) + +/* Prototypes */ +static void mute_menu_item_class_init (MuteMenuItemClass *klass); +static void mute_menu_item_init (MuteMenuItem *self); +static void mute_menu_item_dispose (GObject *object); +static void mute_menu_item_finalize (GObject *object); +static void mute_menu_item_set_global_mute_from_ui (gpointer user_data); + +G_DEFINE_TYPE (MuteMenuItem, mute_menu_item, G_TYPE_OBJECT); + +static void mute_menu_item_class_init (MuteMenuItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (MuteMenuItemPrivate)); + + object_class->dispose = mute_menu_item_dispose; + object_class->finalize = mute_menu_item_finalize; + + return; +} + +static void 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(); + + g_signal_connect (G_OBJECT (priv->button), + DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK (mute_menu_item_set_global_mute_from_ui), + self); + return; +} + +static void mute_menu_item_dispose (GObject *object) +{ + G_OBJECT_CLASS (mute_menu_item_parent_class)->dispose (object); + return; +} + +static void +mute_menu_item_finalize (GObject *object) +{ + G_OBJECT_CLASS (mute_menu_item_parent_class)->finalize (object); +} + +static void +mute_menu_item_set_global_mute_from_ui (gpointer user_data) +{ + g_return_if_fail (DBUSMENU_IS_MENUITEM (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); +} + + +void mute_menu_item_update(MuteMenuItem* item, gboolean value_update) +{ + MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); + + dbusmenu_menuitem_property_set_bool (priv->button, + DBUSMENU_MUTE_MENUITEM_VALUE, + value_update); + dbusmenu_menuitem_property_set (priv->button, + DBUSMENU_MENUITEM_PROP_LABEL, + value_update == FALSE ? _("Mute") : _("Unmute")); +} + +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_ENABLED, + active); +} + +DbusmenuMenuitem* mute_menu_item_get_button (MuteMenuItem* item) +{ + MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); + return priv->button; +} + +MuteMenuItem* mute_menu_item_new (gboolean initial_update, gboolean enabled) +{ + MuteMenuItem *self = g_object_new (MUTE_MENU_ITEM_TYPE, NULL); + mute_menu_item_update (self, initial_update); + mute_menu_item_enable (self, enabled); + return self; +} diff --git a/src/mute-menu-item.h b/src/mute-menu-item.h new file mode 100644 index 0000000..8240441 --- /dev/null +++ b/src/mute-menu-item.h @@ -0,0 +1,59 @@ +/* +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 __MUTE_MENU_ITEM_H__ +#define __MUTE_MENU_ITEM_H__ + +#include <glib.h> +#include <glib-object.h> +#include <libdbusmenu-glib/menuitem.h> + +G_BEGIN_DECLS + +#define MUTE_MENU_ITEM_TYPE (mute_menu_item_get_type ()) +#define MUTE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTE_MENU_ITEM_TYPE, MuteMenuItem)) +#define MUTE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTE_MENU_ITEM_TYPE, MuteMenuItemClass)) +#define IS_MUTE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTE_MENU_ITEM_TYPE)) +#define IS_MUTE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTE_MENU_ITEM_TYPE)) +#define MUTE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTE_MENU_ITEM_TYPE, MuteMenuItemClass)) + +typedef struct _MuteMenuItem MuteMenuItem; +typedef struct _MuteMenuItemClass MuteMenuItemClass; + +struct _MuteMenuItemClass { + GObjectClass parent_class; +}; + +struct _MuteMenuItem { + GObject parent; +}; + +GType mute_menu_item_get_type (void); + +MuteMenuItem* mute_menu_item_new (); + +void mute_menu_item_update (MuteMenuItem* item, gboolean update); +void mute_menu_item_enable (MuteMenuItem* item, gboolean active); + +DbusmenuMenuitem* mute_menu_item_get_button (MuteMenuItem* item); + +G_END_DECLS + +#endif + diff --git a/src/pulse-manager.c b/src/pulse-manager.c index 4443044..ca008a7 100644 --- a/src/pulse-manager.c +++ b/src/pulse-manager.c @@ -25,7 +25,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <pulse/gccmacro.h> #include "pulse-manager.h" -#include "dbus-menu-manager.h" #define RECONNECT_DELAY 5 @@ -75,7 +74,7 @@ void establish_pulse_activities(SoundServiceDbus *service) // Establish event callback registration pa_context_set_state_callback (pulse_context, context_state_callback, NULL); - dbus_menu_manager_update_pa_state (FALSE, FALSE, FALSE, 0); + sound_service_dbus_update_pa_state (dbus_service, FALSE, FALSE, 0); pa_context_connect (pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); } @@ -199,10 +198,12 @@ 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_sink_input_while_muted (dbus_service, TRUE); - } else { - sound_service_dbus_sink_input_while_muted(dbus_service, FALSE); + 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() @@ -223,10 +224,8 @@ static void mute_each_sink(gpointer key, gpointer value, gpointer user_data) if (GPOINTER_TO_INT(user_data) == 1) { sound_service_dbus_update_sink_mute(dbus_service, TRUE); } else { - dbus_menu_manager_update_volume(get_default_sink_volume()); + sound_service_dbus_update_volume(dbus_service, get_default_sink_volume()); } - - /* g_debug("in the pulse manager: mute each sink %i", GPOINTER_TO_INT(user_data));*/ } void toggle_global_mute(gboolean mute_value) @@ -309,14 +308,17 @@ 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) { - dbus_menu_manager_update_pa_state(TRUE, - device_available, - default_sink_is_muted(), - get_default_sink_volume()); + 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 ..."); - dbus_menu_manager_update_pa_state(FALSE, device_available, default_sink_is_muted(), get_default_sink_volume()); + 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");*/ @@ -346,7 +348,10 @@ static void pulse_default_sink_info_callback(pa_context *c, const pa_sink_info * if (position < 0) { pa_operation_unref(pa_context_get_sink_info_list(c, pulse_sink_info_callback, NULL)); } else { - dbus_menu_manager_update_pa_state(TRUE, determine_sink_availability(), default_sink_is_muted(), get_default_sink_volume()); + sound_service_dbus_update_pa_state(dbus_service, + determine_sink_availability(), + default_sink_is_muted(), + get_default_sink_volume()); } } } @@ -408,18 +413,17 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v 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);*/ - dbus_menu_manager_update_volume(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); - dbus_menu_manager_update_mute_ui(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);*/ - dbus_menu_manager_update_volume(volume_percent); + /* g_debug("Updating volume from PA manager with volume = %f", volume_percent);*/ + sound_service_dbus_update_volume(dbus_service, volume_percent); } } } @@ -434,7 +438,7 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v 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_sink_availability(dbus_service, TRUE); + sound_service_dbus_update_sound_state(dbus_service, AVAILABLE); } } @@ -463,7 +467,7 @@ static void pulse_server_info_callback(pa_context *c, pa_operation *operation; if (info == NULL) { g_warning("No server - get the hell out of here"); - dbus_menu_manager_update_pa_state(FALSE, FALSE, TRUE, 0); + sound_service_dbus_update_pa_state(dbus_service, FALSE, TRUE, 0); pa_server_available = FALSE; return; } @@ -493,7 +497,7 @@ static void subscribed_events_callback(pa_context *c, enum pa_subscription_event 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_sink_availability(dbus_service, FALSE); + 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)); @@ -548,10 +552,10 @@ static void context_state_callback(pa_context *c, void *userdata) case PA_CONTEXT_FAILED: g_warning("PA_CONTEXT_FAILED - Is PulseAudio Daemon running ?"); pa_server_available = FALSE; - dbus_menu_manager_update_pa_state(TRUE, - pa_server_available, - default_sink_is_muted(), - get_default_sink_volume()); + 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, diff --git a/src/pulse-manager.h b/src/pulse-manager.h index fc01f1c..dfe1256 100644 --- a/src/pulse-manager.h +++ b/src/pulse-manager.h @@ -1,9 +1,6 @@ #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. - Copyright 2010 Canonical Ltd. Authors: diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c index 0f2f07b..64db277 100644 --- a/src/slider-menu-item.c +++ b/src/slider-menu-item.c @@ -29,7 +29,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>. typedef struct _SliderMenuItemPrivate SliderMenuItemPrivate; struct _SliderMenuItemPrivate { - gdouble slider_value; }; #define SLIDER_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SLIDER_MENU_ITEM_TYPE, SliderMenuItemPrivate)) @@ -76,45 +75,56 @@ slider_menu_item_finalize (GObject *object) G_OBJECT_CLASS (slider_menu_item_parent_class)->finalize (object); } - static void handle_event (DbusmenuMenuitem * mi, const gchar * name, GVariant * value, guint timestamp) { - gdouble volume_input = 0; /*g_debug ( "handle-event in the slider at the backend, input is of type %s", g_variant_get_type_string(value));*/ GVariant* input = NULL; input = value; - g_variant_ref (input); - // Please note: Subject to change in future DBusmenu revisions if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT) == TRUE) { input = g_variant_get_variant(value); } - volume_input = g_variant_get_double(input); + gboolean volume_input = g_variant_get_double(input); if (value != NULL){ set_sink_volume(volume_input); } - g_variant_unref (input); } +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, + gboolean active) +{ + dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item), + DBUSMENU_MENUITEM_PROP_ENABLED, + active ); +} -SliderMenuItem* slider_menu_item_new(gboolean sinks_available, gdouble start_volume) +SliderMenuItem* slider_menu_item_new (gboolean sinks_available, + gdouble start_volume) { 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); + 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); - 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; -} - - - - +}
\ No newline at end of file diff --git a/src/slider-menu-item.h b/src/slider-menu-item.h index 763944f..51336ae 100644 --- a/src/slider-menu-item.h +++ b/src/slider-menu-item.h @@ -45,8 +45,12 @@ struct _SliderMenuItem { }; GType slider_menu_item_get_type (void); -// TODO for now the volume percent param is not being used - remove once init mystery is solved -SliderMenuItem* slider_menu_item_new(gboolean sinks_available, gdouble current_vol); + +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); G_END_DECLS diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c index 6057a29..b444a91 100644 --- a/src/sound-service-dbus.c +++ b/src/sound-service-dbus.c @@ -3,7 +3,6 @@ * * Authors: * Conor Curran <conor.curran@canonical.com> - * Ted Gould <ted.gould@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 @@ -23,12 +22,19 @@ #endif #include <gio/gio.h> +#include <unistd.h> +#include <glib/gi18n.h> #include <libindicator/indicator-service.h> +#include <libdbusmenu-glib/server.h> +#include <libdbusmenu-glib/client.h> + +#include "sound-service-dbus.h" + #include "gen-sound-service.xml.h" #include "dbus-shared-names.h" -#include "sound-service-dbus.h" -#include "common-defs.h" #include "pulse-manager.h" +#include "slider-menu-item.h" +#include "mute-menu-item.h" // DBUS methods static void bus_method_call (GDBusConnection * connection, @@ -50,14 +56,16 @@ static GDBusInterfaceVTable interface_table = { typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; struct _SoundServiceDbusPrivate { - GDBusConnection *connection; - gboolean mute; - gboolean sink_availability; + GDBusConnection* connection; + DbusmenuMenuitem* root_menuitem; + SliderMenuItem* volume_slider_menuitem; + MuteMenuItem* mute_menuitem; + SoundState current_sound_state; }; 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); @@ -65,7 +73,18 @@ static void sound_service_dbus_init (SoundServiceDbus *self); static void sound_service_dbus_dispose (GObject *object); static void sound_service_dbus_finalize (GObject *object); -/* GObject Boilerplate */ +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); + G_DEFINE_TYPE (SoundServiceDbus, sound_service_dbus, G_TYPE_OBJECT); static void @@ -108,8 +127,8 @@ sound_service_dbus_init (SoundServiceDbus *self) SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); priv->connection = NULL; - priv->mute = FALSE; - priv->sink_availability = FALSE; + + priv->current_sound_state = UNAVAILABLE; /* Fetch the session bus */ priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); @@ -134,6 +153,104 @@ sound_service_dbus_init (SoundServiceDbus *self) } } +DbusmenuMenuitem* sound_service_dbus_create_root_item (SoundServiceDbus* self) +{ + SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); + priv->root_menuitem = dbusmenu_menuitem_new(); + g_debug("Root ID: %d", dbusmenu_menuitem_get_id(priv->root_menuitem)); + 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); + return priv->root_menuitem; +} + +static void sound_service_dbus_build_sound_menu ( SoundServiceDbus* self, + gboolean mute_update, + gboolean availability, + gdouble volume ) +{ + 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 )); + + // Separator + + DbusmenuMenuitem* separator = dbusmenu_menuitem_new(); + + dbusmenu_menuitem_property_set( separator, + DBUSMENU_MENUITEM_PROP_TYPE, + DBUSMENU_CLIENT_TYPES_SEPARATOR); + dbusmenu_menuitem_child_append(priv->root_menuitem, separator); + g_object_unref (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(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); +} + +/** +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 --page=applications", &error) && + !g_spawn_command_line_async("xfce4-mixer", &error)) + { + g_warning("Unable to show dialog: %s", error->message); + g_error_free(error); + } +} + +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) { @@ -148,112 +265,135 @@ sound_service_dbus_finalize (GObject *object) return; } -/* A method has been called from our dbus inteface. Figure out what it - is and dispatch it. */ -static void -bus_method_call (GDBusConnection * connection, - const gchar * sender, - const gchar * path, - const gchar * interface, - const gchar * method, - GVariant * params, - GDBusMethodInvocation * invocation, - gpointer user_data) -{ - SoundServiceDbus* service = SOUND_SERVICE_DBUS(user_data); - g_return_if_fail ( IS_SOUND_SERVICE_DBUS(service) ); - GVariant * retval = NULL; - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (service); - - if (g_strcmp0(method, "GetSinkMute") == 0) { - g_debug("Get sink mute - sound service dbus!,about to send over mute_value of %i", priv->mute); - retval = g_variant_new ( "(b)", priv->mute); - } - else if (g_strcmp0(method, "GetSinkAvailability") == 0) { - g_debug("Get sink availability - sound service dbus!, about to send over availability_value of %i", priv->sink_availability); - retval = g_variant_new ( "(b)", priv->sink_availability); - } - else { - g_warning("Calling method '%s' on the sound service but it's unknown", method); - } - g_dbus_method_invocation_return_value(invocation, retval); +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); +} -/** -SIGNALS -Utility methods to emit signals from the service into the ether. -**/ -void sound_service_dbus_sink_input_while_muted(SoundServiceDbus* obj, - gboolean block_value) +static SoundState sound_service_dbus_get_state_from_volume (SoundServiceDbus* self) { - g_debug("Emitting signal: SINK_INPUT_WHILE_MUTED, with block_value: %i", - block_value); - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (obj); - GVariant* v_output = g_variant_new("(b)", block_value); + 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); - GError * error = NULL; + SoundState state = LOW_LEVEL; - g_dbus_connection_emit_signal( priv->connection, - NULL, - INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH, - INDICATOR_SOUND_DBUS_INTERFACE, - INDICATOR_SOUND_SIGNAL_SINK_INPUT_WHILE_MUTED, - v_output, - &error ); - if (error != NULL) { - g_error("Unable to emit signal 'sinkinputwhilemuted' because : %s", error->message); - g_error_free(error); - return; + 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; } -void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, - gboolean sink_mute) +static void sound_service_dbus_determine_state (SoundServiceDbus* self, + gboolean availability, + gboolean mute, + gdouble volume) { - g_debug("Emitting signal: SINK_MUTE_UPDATE, with sink mute %i", sink_mute); - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (obj); - priv->mute = sink_mute; - - GVariant* v_output = g_variant_new("(b)", sink_mute); - GError * error = NULL; - g_dbus_connection_emit_signal( priv->connection, - NULL, - INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, - INDICATOR_SOUND_DBUS_INTERFACE, - INDICATOR_SOUND_SIGNAL_SINK_MUTE_UPDATE, - v_output, - &error ); - if (error != NULL) { - g_error("Unable to emit signal 'sinkmuteupdate' because : %s", error->message); - g_error_free(error); - return; + 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); } -void sound_service_dbus_update_sink_availability(SoundServiceDbus* obj, - gboolean sink_availability) + +// 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) { - g_debug("Emitting signal: SinkAvailableUpdate, with %i", sink_availability); - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (obj); - priv->sink_availability = sink_availability; - - GVariant* v_output = g_variant_new("(b)", priv->sink_availability); + 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 && + dbusmenu_menuitem_property_get_bool ( DBUSMENU_MENUITEM(priv->mute_menuitem), + DBUSMENU_MUTE_MENUITEM_VALUE) == 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); + GError * error = NULL; - + + g_debug ("emitting signal with value %i", (int)update); g_dbus_connection_emit_signal( priv->connection, NULL, INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, INDICATOR_SOUND_DBUS_INTERFACE, - INDICATOR_SOUND_SIGNAL_SINK_AVAILABLE_UPDATE, - v_output, - &error ); + INDICATOR_SOUND_SIGNAL_STATE_UPDATE, + v_output, + &error ); if (error != NULL) { - g_error("Unable to emit signal 'SinkAvailableUpdate' because : %s", error->message); + g_error("Unable to emit signal 'sinkinputwhilemuted' because : %s", error->message); g_error_free(error); return; } } +//HANDLE DBUS METHOD CALLS +// TODO we will need to implement the black_list method. +static void +bus_method_call (GDBusConnection * connection, + const gchar * sender, + const gchar * path, + const gchar * interface, + const gchar * method, + GVariant * params, + GDBusMethodInvocation * invocation, + gpointer user_data) +{ + SoundServiceDbus* service = SOUND_SERVICE_DBUS(user_data); + g_return_if_fail ( IS_SOUND_SERVICE_DBUS(service) ); + GVariant * retval = NULL; + 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); + } + else { + g_warning("Calling method '%s' on the sound service but it's unknown", method); + } + g_dbus_method_invocation_return_value (invocation, retval); +} + + diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h index 0d1573b..fab3549 100644 --- a/src/sound-service-dbus.h +++ b/src/sound-service-dbus.h @@ -3,7 +3,6 @@ * * Authors: * Conor Curran <conor.curran@canonical.com> - * Cody Russell <crussell@canonical.com> * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -23,6 +22,9 @@ #include <glib.h> #include <glib-object.h> +#include <libdbusmenu-glib/menuitem.h> +#include "common-defs.h" + G_BEGIN_DECLS @@ -51,10 +53,14 @@ struct _SoundServiceDbusClass { GType sound_service_dbus_get_type (void) G_GNUC_CONST; -// Utility methods to get the SIGNAL messages across into the sound-service-dbus -void sound_service_dbus_sink_input_while_muted (SoundServiceDbus* obj, gboolean block_value); -void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, gboolean sink_mute); -void sound_service_dbus_update_sink_availability(SoundServiceDbus* obj, gboolean sink_availibity); +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 ); G_END_DECLS diff --git a/src/sound-service.c b/src/sound-service.c index 98f1881..2cb33d3 100644 --- a/src/sound-service.c +++ b/src/sound-service.c @@ -1,5 +1,4 @@ /* -This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel. Copyright 2010 Canonical Ltd. Authors: @@ -19,8 +18,9 @@ with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "sound-service.h" -#include "dbus-menu-manager.h" + #include "pulse-manager.h" +#include "sound-service-dbus.h" #include "music-player-bridge.h" static GMainLoop *mainloop = NULL; @@ -64,7 +64,9 @@ main (int argc, char ** argv) INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); - DbusmenuMenuitem* root_menuitem = dbus_menu_manager_setup(); + SoundServiceDbus* sound_service = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL); + + DbusmenuMenuitem* root_menuitem = sound_service_dbus_create_root_item(sound_service); MusicPlayerBridge* server = music_player_bridge_new(); music_player_bridge_set_root_menu_item(server, root_menuitem); diff --git a/src/sound-service.h b/src/sound-service.h index e95d4c7..7c5d0c3 100644 --- a/src/sound-service.h +++ b/src/sound-service.h @@ -2,13 +2,10 @@ #define __INCLUDE_SOUND_SERVICE_H__ /* -This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel. Copyright 2010 Canonical Ltd. Authors: Conor Curran <conor.curran@canonical.com> - Ted Gould <ted@canonical.com> - Cody Russell <crussell@canonical.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published diff --git a/src/sound-service.list b/src/sound-service.list deleted file mode 100644 index ee376e9..0000000 --- a/src/sound-service.list +++ /dev/null @@ -1,2 +0,0 @@ -VOID:INT,BOOLEAN - diff --git a/src/sound-service.xml b/src/sound-service.xml index a552d52..07c9c3d 100644 --- a/src/sound-service.xml +++ b/src/sound-service.xml @@ -1,31 +1,17 @@ <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node name="/com/canonical/indicators/sound"> <interface name="com.canonical.indicators.sound"> - <method name = "GetSinkMute"> - <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_mute"/> - <arg type='b' name='mute_input' direction="out"/> + <method name = "BlacklistMediaPlayer"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_blacklist_media_player"/> + <arg type='s' name='player_desktop_name' direction="in"/> </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 name = "GetSoundState"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_state"/> + <arg type='i' name='current_state' 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 --> - <signal name="SinkInputWhileMuted"> - <arg name="block_value" type="b" direction="out"/> - </signal> - - <signal name="SinkMuteUpdate"> - <arg name="mute_value" type="b" direction="out"/> + <signal name="SoundStateUpdate"> + <arg name="new_state" type="i" direction="out"/> </signal> - - <signal name="SinkAvailableUpdate"> - <arg name="available_value" type="b" direction="out"/> - </signal> - </interface> </node> diff --git a/src/sound-state-manager.c b/src/sound-state-manager.c new file mode 100644 index 0000000..2ea9b1a --- /dev/null +++ b/src/sound-state-manager.c @@ -0,0 +1,388 @@ +/* +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 <libindicator/indicator-image-helper.h> +#include "sound-state-manager.h" +#include "dbus-shared-names.h" + +typedef struct _SoundStateManagerPrivate SoundStateManagerPrivate; + +struct _SoundStateManagerPrivate +{ + GDBusProxy* dbus_proxy; + GHashTable* volume_states; + GList* blocked_animation_list; + SoundState current_state; + GtkImage* speaker_image; +}; + +#define SOUND_STATE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUND_TYPE_STATE_MANAGER, SoundStateManagerPrivate)) +G_DEFINE_TYPE (SoundStateManager, sound_state_manager, G_TYPE_OBJECT); + +static GtkIconSize design_team_size; +static gint blocked_id; +static gint animation_id; +static GList* blocked_iter = NULL; +static gboolean can_animate = FALSE; + +static void sound_state_manager_prepare_blocked_animation(SoundStateManager* self); +static gboolean sound_state_manager_start_animation (gpointer user_data); +static gboolean sound_state_manager_fade_back_to_mute_image (gpointer user_data); +static void sound_state_manager_reset_mute_blocking_animation (SoundStateManager* self); +static void sound_state_manager_free_the_animation_list (SoundStateManager* self); +static void sound_state_manager_prepare_state_image_names (SoundStateManager* self); +static void sound_state_signal_cb ( GDBusProxy* proxy, + gchar* sender_name, + gchar* signal_name, + GVariant* parameters, + gpointer user_data ); +static gboolean sound_state_manager_can_proceed_with_blocking_animation (SoundStateManager* self); + +static void +sound_state_manager_init (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + priv->dbus_proxy = NULL; + priv->volume_states = NULL; + priv->speaker_image = NULL; + priv->blocked_animation_list = NULL; + + sound_state_manager_prepare_state_image_names (self); + sound_state_manager_prepare_blocked_animation (self); + + priv->current_state = UNAVAILABLE; + priv->speaker_image = indicator_image_helper (g_hash_table_lookup (priv->volume_states, + GINT_TO_POINTER(priv->current_state))); +} + +static void +sound_state_manager_finalize (GObject *object) +{ + /* TODO: Add deinitalization code here */ + + G_OBJECT_CLASS (sound_state_manager_parent_class)->finalize (object); +} + +static void +sound_state_manager_dispose (GObject *object) +{ + SoundStateManager* self = SOUND_STATE_MANAGER (object); + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + g_hash_table_destroy (priv->volume_states); + + sound_state_manager_free_the_animation_list (self); + G_OBJECT_CLASS (sound_state_manager_parent_class)->dispose (object); +} + + +static void +sound_state_manager_class_init (SoundStateManagerClass *klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sound_state_manager_finalize; + object_class->dispose = sound_state_manager_dispose; + + g_type_class_add_private (klass, sizeof (SoundStateManagerPrivate)); + + design_team_size = gtk_icon_size_register("design-team-size", 22, 22); +} + +/* +Prepare states versus images names hash. +*/ +static void +sound_state_manager_prepare_state_image_names (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + priv->volume_states = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(MUTED), g_strdup("audio-volume-muted-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(ZERO_LEVEL), g_strdup("audio-volume-low-zero-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(LOW_LEVEL), g_strdup("audio-volume-low-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(MEDIUM_LEVEL), g_strdup("audio-volume-medium-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(HIGH_LEVEL), g_strdup("audio-volume-high-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(BLOCKED), g_strdup("audio-volume-muted-blocking-panel")); + g_hash_table_insert (priv->volume_states, GINT_TO_POINTER(UNAVAILABLE), g_strdup("audio-output-none-panel")); +} + +/* +prepare_blocked_animation: +Prepares the array of images to be used in the blocked animation. +Only called at startup. +*/ +static void +sound_state_manager_prepare_blocked_animation (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + gchar* blocked_name = g_hash_table_lookup(priv->volume_states, + GINT_TO_POINTER(BLOCKED)); + gchar* muted_name = g_hash_table_lookup(priv->volume_states, + GINT_TO_POINTER(MUTED)); + + GtkImage* temp_image = indicator_image_helper(muted_name); + GdkPixbuf* mute_buf = gtk_image_get_pixbuf(temp_image); + + temp_image = indicator_image_helper(blocked_name); + GdkPixbuf* blocked_buf = gtk_image_get_pixbuf(temp_image); + + if (mute_buf == NULL || blocked_buf == NULL) { + //g_debug("Don bother with the animation, the theme aint got the goods !"); + return; + } + + int i; + + // sample 51 snapshots - range : 0-256 + for (i = 0; i < 51; 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 * 5)); + priv->blocked_animation_list = g_list_append(priv->blocked_animation_list, + gdk_pixbuf_copy(blocked_buf)); + } + can_animate = TRUE; + g_object_ref_sink(mute_buf); + g_object_unref(mute_buf); + g_object_ref_sink(blocked_buf); + g_object_unref(blocked_buf); +} + + +GtkImage* +sound_state_manager_get_current_icon (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + return priv->speaker_image; +} + +SoundState +sound_state_manager_get_current_state (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + return priv->current_state; +} + +/** + * sound_state_manager_connect_to_dbus: + * @returns: void + * When ready the indicator-sound calls this method to enable state communication + * between the indicator and the service. + **/ +void +sound_state_manager_connect_to_dbus (SoundStateManager* self, GDBusProxy* proxy) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + priv->dbus_proxy = proxy; + g_debug (" here about to register for signal callback on %s", g_dbus_proxy_get_name (priv->dbus_proxy)); + g_signal_connect (priv->dbus_proxy, "g-signal", + G_CALLBACK (sound_state_signal_cb), self); + + g_dbus_proxy_call ( priv->dbus_proxy, + "GetSoundState", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback)sound_state_manager_get_state_cb, + self); +} + +void +sound_state_manager_get_state_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + g_return_if_fail (SOUND_IS_STATE_MANAGER (user_data)); + SoundStateManager* self = SOUND_STATE_MANAGER (user_data); + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + GVariant *result, *value; + GError *error = NULL; + result = g_dbus_proxy_call_finish ( priv->dbus_proxy, + res, + &error ); + + if (error != NULL) { + g_debug("get_sound_state call failed: %s", error->message); + g_error_free(error); + return; + } + + value = g_variant_get_child_value(result, 0); + priv->current_state = (SoundState)g_variant_get_int32(value); + + gchar* image_name = g_hash_table_lookup (priv->volume_states, + GINT_TO_POINTER(priv->current_state) ); + indicator_image_helper_update (priv->speaker_image, image_name); + + g_variant_unref(value); + g_variant_unref(result); +} + +void +sound_state_manager_deal_with_disconnect (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + priv->current_state = UNAVAILABLE; + + gchar* image_name = g_hash_table_lookup (priv->volume_states, + GINT_TO_POINTER(priv->current_state) ); + indicator_image_helper_update (priv->speaker_image, image_name); +} + +static void +sound_state_signal_cb ( GDBusProxy* proxy, + gchar* sender_name, + gchar* signal_name, + GVariant* parameters, + gpointer user_data) +{ + //g_debug ( "!!! sound state manager signal_cb" ); + + g_return_if_fail (SOUND_IS_STATE_MANAGER (user_data)); + SoundStateManager* self = SOUND_STATE_MANAGER (user_data); + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + g_variant_ref (parameters); + GVariant *value = g_variant_get_child_value (parameters, 0); + gint update = g_variant_get_int32 (value); + + //g_debug ( "!!! signal_cb with value %i", update); + + priv->current_state = (SoundState)update; + + g_variant_unref (parameters); + + if (g_strcmp0(signal_name, INDICATOR_SOUND_SIGNAL_STATE_UPDATE) == 0){ + + gchar* image_name = g_hash_table_lookup (priv->volume_states, + GINT_TO_POINTER(priv->current_state) ); + if (priv->current_state == BLOCKED && + sound_state_manager_can_proceed_with_blocking_animation (self) == TRUE) { + blocked_id = g_timeout_add_seconds (4, + sound_state_manager_start_animation, + self); + indicator_image_helper_update (priv->speaker_image, image_name); + + } + else{ + indicator_image_helper_update (priv->speaker_image, image_name); + } + } + else { + g_warning ("sorry don't know what signal this is - %s", signal_name); + } + +} + +void +sound_state_manager_style_changed_cb (GtkWidget *widget, + GtkStyle *previous_style, + gpointer user_data) +{ + g_debug("Just caught a style change event"); + g_return_if_fail (SOUND_IS_STATE_MANAGER (user_data)); + SoundStateManager* self = SOUND_STATE_MANAGER (user_data); + sound_state_manager_reset_mute_blocking_animation (self); + sound_state_manager_free_the_animation_list (self); + sound_state_manager_prepare_blocked_animation (self); +} + +static void +sound_state_manager_reset_mute_blocking_animation (SoundStateManager* self) +{ + if (animation_id != 0) { + //g_debug("about to remove the animation_id callback from the mainloop!!**"); + g_source_remove(animation_id); + animation_id = 0; + } + if (blocked_id != 0) { + //g_debug("about to remove the blocked_id callback from the mainloop!!**"); + g_source_remove(blocked_id); + blocked_id = 0; + } +} + +static void +sound_state_manager_free_the_animation_list (SoundStateManager* self) +{ + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + if (priv->blocked_animation_list != NULL) { + g_list_foreach (priv->blocked_animation_list, (GFunc)g_object_unref, NULL); + g_list_free (priv->blocked_animation_list); + priv->blocked_animation_list = NULL; + } +} + + +static gboolean +sound_state_manager_start_animation (gpointer userdata) +{ + g_return_val_if_fail (SOUND_IS_STATE_MANAGER (userdata), FALSE); + SoundStateManager* self = SOUND_STATE_MANAGER (userdata); + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); + + blocked_iter = priv->blocked_animation_list; + blocked_id = 0; + animation_id = g_timeout_add (50, + sound_state_manager_fade_back_to_mute_image, + self); + return FALSE; +} + +static gboolean +sound_state_manager_fade_back_to_mute_image (gpointer user_data) +{ + g_return_val_if_fail (SOUND_IS_STATE_MANAGER (user_data), FALSE); + SoundStateManager* self = SOUND_STATE_MANAGER (user_data); + SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE (self); + + if (blocked_iter != NULL) { + gtk_image_set_from_pixbuf (priv->speaker_image, blocked_iter->data); + blocked_iter = blocked_iter->next; + return TRUE; + } else { + animation_id = 0; + //g_debug("exit from animation now\n"); + g_dbus_proxy_call ( priv->dbus_proxy, + "GetSoundState", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback)sound_state_manager_get_state_cb, + self); + + return FALSE; + } +} + + +// Simple static helper to determine if the coast is clear to animate +static +gboolean sound_state_manager_can_proceed_with_blocking_animation (SoundStateManager* self) +{ + return (can_animate && blocked_id == 0 && animation_id == 0 ); +} + diff --git a/src/sound-state-manager.h b/src/sound-state-manager.h new file mode 100644 index 0000000..d73d5d9 --- /dev/null +++ b/src/sound-state-manager.h @@ -0,0 +1,66 @@ +/* +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/>. +*/ + +#ifndef _SOUND_STATE_MANAGER_H_ +#define _SOUND_STATE_MANAGER_H_ + +#include <glib.h> +#include "common-defs.h" + +G_BEGIN_DECLS + +#define SOUND_TYPE_STATE_MANAGER (sound_state_manager_get_type ()) +#define SOUND_STATE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUND_TYPE_STATE_MANAGER, SoundStateManager)) +#define SOUND_STATE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUND_TYPE_STATE_MANAGER, SoundStateManagerClass)) +#define SOUND_IS_STATE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUND_TYPE_STATE_MANAGER)) +#define SOUND_IS_STATE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUND_TYPE_STATE_MANAGER)) +#define SOUND_STATE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUND_TYPE_STATE_MANAGER, SoundStateManagerClass)) + +typedef struct _SoundStateManagerClass SoundStateManagerClass; +typedef struct _SoundStateManager SoundStateManager; + +struct _SoundStateManagerClass +{ + GObjectClass parent_class; +}; + +struct _SoundStateManager +{ + GObject parent_instance; +}; + +GType sound_state_manager_get_type (void) G_GNUC_CONST; + +void sound_state_manager_style_changed_cb (GtkWidget *widget, + GtkStyle *previous_style, + gpointer user_data); +GtkImage* sound_state_manager_get_current_icon (SoundStateManager* self); +SoundState sound_state_manager_get_current_state (SoundStateManager* self); +void sound_state_manager_connect_to_dbus (SoundStateManager* self, + GDBusProxy* proxy); +void sound_state_manager_deal_with_disconnect (SoundStateManager* self); +void sound_state_manager_get_state_cb (GObject *object, + GAsyncResult *res, + gpointer user_data); + + + +G_END_DECLS + +#endif /* _SOUND_STATE_MANAGER_H_ */ diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala index e93f0bb..b8f55b5 100644 --- a/src/transport-menu-item.vala +++ b/src/transport-menu-item.vala @@ -44,7 +44,8 @@ public class TransportMenuitem : PlayerItem { debug("UPDATING THE TRANSPORT DBUSMENUITEM PLAY STATE WITH VALUE %i", (int)update); - this.property_set_int(MENUITEM_PLAY_STATE, update); + int temp = (int)update; + this.property_set_int(MENUITEM_PLAY_STATE, temp); } public override void handle_event(string name, diff --git a/src/volume-widget.c b/src/volume-widget.c index 9fa1395..ceebec5 100644 --- a/src/volume-widget.c +++ b/src/volume-widget.c @@ -137,7 +137,6 @@ volume_widget_property_update( DbusmenuMenuitem* item, gchar* property, gdouble update = g_variant_get_double (value); //g_debug("volume-widget - update level with value %f", update); gtk_range_set_value(range, update); - update_state_from_volume(update); } } } @@ -157,7 +156,6 @@ volume_widget_set_twin_item(VolumeWidget* self, GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); GtkRange *range = (GtkRange*)slider; gtk_range_set_value(range, initial_level); - update_state_from_volume(initial_level); } static gboolean @@ -169,7 +167,6 @@ volume_widget_change_value_cb (GtkRange *range, g_return_val_if_fail (IS_VOLUME_WIDGET (user_data), FALSE); VolumeWidget* mitem = VOLUME_WIDGET(user_data); volume_widget_update(mitem, new_value); - update_state_from_volume(new_value); return FALSE; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 620668e..574279f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,6 +7,7 @@ check_PROGRAMS = \ TESTS = \ test-indicator-sound \ test-pulse-manager +XTESTS_FAIL = $(TESTS) DISTCLEANFILES = $(TESTS) @@ -46,6 +47,8 @@ test_pulse_manager_SOURCES = \ test-pulse-manager.c \ $(top_builddir)/src/dbus-menu-manager.c \ $(top_builddir)/src/sound-service-dbus.c \ + $(top_builddir)/src/gen-sound-service.xml.h \ + $(top_builddir)/src/gen-sound-service.xml.c \ $(top_builddir)/src/slider-menu-item.c test_pulse_manager_CFLAGS = \ diff --git a/tests/test-indicator-sound-dbus-client.c b/tests/test-indicator-sound-dbus-client.c index 265a36d..eafd03a 100644 --- a/tests/test-indicator-sound-dbus-client.c +++ b/tests/test-indicator-sound-dbus-client.c @@ -21,17 +21,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ + #include <glib.h> -#include <dbus/dbus-glib.h> #include "../src/dbus-shared-names.h" #include "test-defines.h" #include "../src/sound-service-client.h" static GMainLoop * mainloop = NULL; -static DBusGProxy * proxy= NULL; +static GDBusProxy * proxy= NULL; static void -test_fetch_mute(DBusGProxy * proxy) +test_fetch_mute(GDBusProxy * proxy) { GError * error = NULL; gboolean *fetched_mute_value; |