From c879c510dbef95deea70687d59c2013005bf946d Mon Sep 17 00:00:00 2001 From: Conor Curran Date: Wed, 24 Feb 2010 14:19:01 +0000 Subject: unit tests for the indicator side added --- src/indicator-sound.c | 282 ++++++++++++++++++++++++++------------------------ 1 file changed, 144 insertions(+), 138 deletions(-) (limited to 'src') diff --git a/src/indicator-sound.c b/src/indicator-sound.c index acacddc..ffe7acb 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -100,13 +100,15 @@ static void fetch_mute_value_from_dbus(); static void prepare_state_machine(); static void determine_state_from_volume(gdouble volume_percent); static void update_state(const gint state); + static const gint STATE_MUTED = 0; static const gint STATE_ZERO = 1; static const gint STATE_LOW = 2; static const gint STATE_MEDIUM = 3; static const gint STATE_HIGH = 4; static const gint STATE_MUTED_WHILE_INPUT = 5; -static const gint STATE_SINKS_NONE = 5; +static const gint STATE_SINKS_NONE = 6; + static GHashTable *volume_states = NULL; static GtkImage *speaker_image = NULL; static GtkWidget* primary_image = NULL; @@ -115,6 +117,7 @@ static gint previous_state = 0; static gdouble initial_volume_percent = 0; static gboolean initial_mute = FALSE; +// Construction static void indicator_sound_class_init (IndicatorSoundClass *klass) { @@ -147,21 +150,91 @@ static void indicator_sound_init (IndicatorSound *self) return; } +static void +indicator_sound_dispose (GObject *object) +{ + IndicatorSound * self = INDICATOR_SOUND(object); -/* -Prepare states Array. -*/ -static void prepare_state_machine() + if (self->service != NULL) { + g_object_unref(G_OBJECT(self->service)); + self->service = NULL; + } + g_hash_table_destroy(volume_states); + G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object); + return; +} + +static void +indicator_sound_finalize (GObject *object) { - // TODO we need three more images - volume_states = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED), g_strdup("audio-volume-muted-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_ZERO), g_strdup("audio-volume-zero-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_LOW), g_strdup("audio-volume-low-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MEDIUM), g_strdup("audio-volume-medium-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_HIGH), g_strdup("audio-volume-high-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT), g_strdup("audio-volume-muted-blocking-panel")); - g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_SINKS_NONE), g_strdup("audio-output-none-panel")); + G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object); + return; +} + +static GtkLabel * +get_label (IndicatorObject * io) +{ + return NULL; +} + +static GtkImage * +get_icon (IndicatorObject * io) +{ + gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state)); + //g_debug("At start-up attempting to set the image to %s", current_name); + speaker_image = GTK_IMAGE(gtk_image_new_from_icon_name(current_name, GTK_ICON_SIZE_MENU)); + gtk_widget_show(GTK_WIDGET(speaker_image)); + return speaker_image; +} + +/* Indicator based function to get the menu for the whole + applet. This starts up asking for the parts of the menu + from the various services. */ +static GtkMenu * +get_menu (IndicatorObject * io) +{ + DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT); + DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu); + dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item); + + // register Key-press listening on the menu widget as the slider does not allow this. + g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL); + + return GTK_MENU(menu); +} + +/** +new_slider_item: +Create a new dBusMenu Slider item. +**/ +static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); + g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); + + volume_slider = ido_scale_menu_item_new_with_range ("Volume", initial_volume_percent, 0, 100, 0.5); + g_object_set(volume_slider, "reverse-scroll-events", TRUE, NULL); + + GtkMenuItem *menu_volume_slider = GTK_MENU_ITEM(volume_slider); + + dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_volume_slider, parent); + g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(slider_prop_change_cb), volume_slider); + + // register slider changes listening on the range + GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); + g_signal_connect(slider, "value-changed", G_CALLBACK(value_changed_event_cb), newitem); + // alternative callback mechanism which i could use again at some point. +/* g_signal_connect(slider, "change-value", G_CALLBACK(user_change_value_event_cb), newitem); */ + + // Set images on the ido + primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)volume_slider); + gtk_image_set_from_icon_name(GTK_IMAGE(primary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_ZERO)), GTK_ICON_SIZE_MENU); + GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)volume_slider); + gtk_image_set_from_icon_name(GTK_IMAGE(secondary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_HIGH)), GTK_ICON_SIZE_MENU); + + gtk_widget_show_all(volume_slider); + + return TRUE; } static void @@ -205,6 +278,63 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u return; } + + + +/* +Prepare states Array. +*/ +static void prepare_state_machine() +{ + // TODO we need three more images + volume_states = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED), g_strdup("audio-volume-muted-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_ZERO), g_strdup("audio-volume-low-zero-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_LOW), g_strdup("audio-volume-low-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MEDIUM), g_strdup("audio-volume-medium-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_HIGH), g_strdup("audio-volume-high-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT), g_strdup("audio-volume-muted-blocking-panel")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_SINKS_NONE), g_strdup("audio-output-none-panel")); +} + + + +static void update_state(const gint state) +{ +/* g_debug("update state beginning - previous_state = %i", previous_state);*/ + + previous_state = current_state; + +/* g_debug("update state 3rd line - previous_state = %i", previous_state);*/ + + current_state = state; + gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state)); + gtk_image_set_from_icon_name(speaker_image, image_name, GTK_ICON_SIZE_MENU); +} + + +static void determine_state_from_volume(gdouble volume_percent) +{ +/* g_debug("determine_state_from_volume - previous_state = %i", previous_state);*/ + + 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; + } + update_state(state); +} + + + static void fetch_volume_percent_from_dbus() { GError * error = NULL; @@ -270,130 +400,6 @@ static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value g_debug("signal caught - sink mute update with mute value: %i", mute_value); gtk_widget_set_sensitive(volume_slider, !mute_value); } - - -static void -indicator_sound_dispose (GObject *object) -{ - IndicatorSound * self = INDICATOR_SOUND(object); - - if (self->service != NULL) { - g_object_unref(G_OBJECT(self->service)); - self->service = NULL; - } - g_hash_table_destroy(volume_states); - G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object); - return; -} - -static void -indicator_sound_finalize (GObject *object) -{ - G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object); - return; -} - -static GtkLabel * -get_label (IndicatorObject * io) -{ - return NULL; -} - -static GtkImage * -get_icon (IndicatorObject * io) -{ - gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state)); - g_debug("At start-up attempting to set the image to %s", current_name); - speaker_image = GTK_IMAGE(gtk_image_new_from_icon_name(current_name, GTK_ICON_SIZE_MENU)); - gtk_widget_show(GTK_WIDGET(speaker_image)); - return speaker_image; -} - -static void update_state(const gint state) -{ -/* g_debug("update state beginning - previous_state = %i", previous_state);*/ - - previous_state = current_state; - -/* g_debug("update state 3rd line - previous_state = %i", previous_state);*/ - - current_state = state; - gchar* image_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state)); - gtk_image_set_from_icon_name(speaker_image, image_name, GTK_ICON_SIZE_MENU); -} - - -static void determine_state_from_volume(gdouble volume_percent) -{ -/* g_debug("determine_state_from_volume - previous_state = %i", previous_state);*/ - - 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; - } - update_state(state); -} - - -/* Indicator based function to get the menu for the whole - applet. This starts up asking for the parts of the menu - from the various services. */ -static GtkMenu * -get_menu (IndicatorObject * io) -{ - DbusmenuGtkMenu *menu = dbusmenu_gtkmenu_new(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_OBJECT); - DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu); - dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), DBUSMENU_SLIDER_MENUITEM_TYPE, new_slider_item); - - // register Key-press listening on the menu widget as the slider does not allow this. - g_signal_connect(menu, "key-press-event", G_CALLBACK(key_press_cb), NULL); - - return GTK_MENU(menu); -} - -/** -new_slider_item: -Create a new dBusMenu Slider item, register the -**/ -static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) -{ - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - volume_slider = ido_scale_menu_item_new_with_range ("Volume", initial_volume_percent, 0, 100, 0.5); - g_object_set(volume_slider, "reverse-scroll-events", TRUE, NULL); - - GtkMenuItem *menu_volume_slider = GTK_MENU_ITEM(volume_slider); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, menu_volume_slider, parent); - g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(slider_prop_change_cb), volume_slider); - - // register slider changes listening on the range - GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); - g_signal_connect(slider, "value-changed", G_CALLBACK(value_changed_event_cb), newitem); - // alternative callback mechanism which i could use again at some point. -/* g_signal_connect(slider, "change-value", G_CALLBACK(user_change_value_event_cb), newitem); */ - - // Set images on the ido - primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)volume_slider); - gtk_image_set_from_icon_name(GTK_IMAGE(primary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_ZERO)), GTK_ICON_SIZE_MENU); - GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)volume_slider); - gtk_image_set_from_icon_name(GTK_IMAGE(secondary_image), g_hash_table_lookup(volume_states, GINT_TO_POINTER(STATE_HIGH)), GTK_ICON_SIZE_MENU); - - gtk_widget_show_all(volume_slider); - - return TRUE; -} - /** slider_prop_change_cb: Whenever we have a property change on a DbusmenuMenuitem this will be called. -- cgit v1.2.3 From be2b2e73044d5ba0c656431f0d19723af6b55946 Mon Sep 17 00:00:00 2001 From: Conor Curran Date: Tue, 2 Mar 2010 10:20:25 +0000 Subject: refactored the indicator tests so as no c file needs to be included --- src/indicator-sound.c | 11 +++++++++++ src/indicator-sound.h | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/indicator-sound.c b/src/indicator-sound.c index 546f404..3aca517 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -300,11 +300,22 @@ gint get_state() return current_state; } +gchar* get_state_image_name(gint state) +{ + return g_hash_table_lookup(volume_states, GINT_TO_POINTER(state)); +} + void prepare_for_tests(IndicatorObject *io) { + prepare_state_machine(); get_icon(io); } +void tidy_up_hash() +{ + g_hash_table_destroy(volume_states); +} + static void update_state(const gint state) { /* g_debug("update state beginning - previous_state = %i", previous_state);*/ diff --git a/src/indicator-sound.h b/src/indicator-sound.h index eaf8948..6d0c85c 100644 --- a/src/indicator-sound.h +++ b/src/indicator-sound.h @@ -21,9 +21,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - +// Essentially these are all exported to faciltiate testing void prepare_state_machine(); void determine_state_from_volume(gdouble volume_percent); gint get_state(); -// Not nice +gchar* get_state_image_name(gint state); void prepare_for_tests(IndicatorObject * io); +void tidy_up_hash(); -- cgit v1.2.3 From 4bde209ef0f8ad381d70b2ade7eb3cf834eed074 Mon Sep 17 00:00:00 2001 From: Conor Curran Date: Tue, 2 Mar 2010 19:36:53 +0000 Subject: one big refactor --- src/Makefile.am | 2 + src/dbus-menu-manager.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ src/dbus-menu-manager.h | 31 +++++++ src/indicator-sound.h | 5 ++ src/pulse-manager.c | 79 +++++++++++++---- src/pulse-manager.h | 4 +- src/sound-service.c | 134 +++------------------------- src/sound-service.h | 11 +-- 8 files changed, 344 insertions(+), 152 deletions(-) create mode 100644 src/dbus-menu-manager.c create mode 100644 src/dbus-menu-manager.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5abacda..40a8fdd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,8 @@ indicator_sound_service_SOURCES = \ common-defs.h \ sound-service.h \ sound-service.c \ + dbus-menu-manager.c \ + dbus-menu-manager.h \ pulse-manager.h \ pulse-manager.c \ sound-service-dbus.h \ diff --git a/src/dbus-menu-manager.c b/src/dbus-menu-manager.c new file mode 100644 index 0000000..652e6b2 --- /dev/null +++ b/src/dbus-menu-manager.c @@ -0,0 +1,230 @@ +/* +This service primarily controls PulseAudio and is driven by the sound indicator menu on the panel. +Copyright 2010 Canonical Ltd. + +Authors: + Conor Curran + +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 . +*/ + +#include +#include + +#include +#include +#include + +#include "dbus-menu-manager.h" +#include "sound-service-dbus.h" +#include "pulse-manager.h" +#include "slider-menu-item.h" + +#include "dbus-shared-names.h" + +// DBUS items +static DbusmenuMenuitem *root_menuitem = NULL; +static DbusmenuMenuitem *mute_all_menuitem = NULL; +static SliderMenuItem *volume_slider_menuitem = NULL; +static SoundServiceDbus *dbus_interface = NULL; + +// PULSEAUDIO +static gboolean b_sink_available = FALSE; +static gboolean b_all_muted = FALSE; +static gboolean b_pulse_ready = FALSE; +static gboolean b_startup = TRUE; +static gdouble volume_percent = 0.0; + +static void set_global_mute_from_ui(); +static gboolean idle_routine (gpointer data); +static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service); +static void refresh_menu(); + +/*-------------------------------------------------------------------------*/ +// Public Methods +/*-------------------------------------------------------------------------*/ + +/** +setup: +**/ +void dbus_menu_manager_setup() +{ + root_menuitem = dbusmenu_menuitem_new(); + g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); + + g_idle_add(idle_routine, root_menuitem); + + dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL); + + DbusmenuServer *server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT); + dbusmenu_server_set_root(server, root_menuitem); + establish_pulse_activities(dbus_interface); +} + +/** +teardown: +**/ +void dbus_menu_manager_teardown() +{ + //TODO tidy up dbus_interface and items! +} + +/** +update_pa_state: +**/ +void dbus_menu_manager_update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble percent) +{ + b_sink_available = sink_available; + b_all_muted = sink_muted; + b_pulse_ready = pa_state; + volume_percent = percent; + g_debug("update pa state with state %i, availability of %i, mute value of %i and a volume percent is %f", pa_state, sink_available, sink_muted, volume_percent); + // Only rebuild the menu on start up... + if(b_startup == TRUE){ + rebuild_sound_menu(root_menuitem, dbus_interface); + b_startup = FALSE; + } + else{ + refresh_menu(); + } + // Emit the signals after the menus are setup/torn down + sound_service_dbus_update_sink_volume(dbus_interface, percent); + sound_service_dbus_update_sink_mute(dbus_interface, sink_muted); + dbus_menu_manager_update_mute_ui(b_all_muted); +} + +/** +update_mute_ui: +'public' method allowing the pa manager to update the mute menu item. +**/ +void dbus_menu_manager_update_mute_ui(gboolean incoming_mute_value) +{ + b_all_muted = incoming_mute_value; + dbusmenu_menuitem_property_set(mute_all_menuitem, + DBUSMENU_MENUITEM_PROP_LABEL, + (b_all_muted == FALSE ? "Mute All" : "Unmute")); + //dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, (b_all_muted == FALSE ? _("Mute All") : _("Unmute"))); +} + + +/*-------------------------------------------------------------------------*/ +// Private Methods +/*-------------------------------------------------------------------------*/ + +static void refresh_menu() +{ + g_debug("in the refresh menu method"); + if(b_sink_available == FALSE || b_pulse_ready == FALSE) + { + + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_ENABLED, + FALSE); + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_VISIBLE, + FALSE); + dbusmenu_menuitem_property_set_bool(mute_all_menuitem, + DBUSMENU_MENUITEM_PROP_ENABLED, + FALSE); + + } + else if(b_sink_available == TRUE && b_pulse_ready == TRUE){ + + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_ENABLED, + TRUE); + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_VISIBLE, + TRUE); + dbusmenu_menuitem_property_set_bool(mute_all_menuitem, + DBUSMENU_MENUITEM_PROP_ENABLED, + TRUE); + } +} + + + +/** + +**/ +static gboolean idle_routine (gpointer data) +{ + return FALSE; +} + + + +/** +show_sound_settings_dialog: +Bring up the gnome volume preferences dialog +**/ +static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data) +{ + GError * error = NULL; + if (!g_spawn_command_line_async("gnome-volume-control", &error)) + { + g_warning("Unable to show dialog: %s", error->message); + g_error_free(error); + } +} + +/** +rebuild_sound_menu: +Build the DBus menu items, mute/unmute, slider, separator and sound preferences 'link' +**/ +static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service) +{ + // Mute button + mute_all_menuitem = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, (b_all_muted == FALSE ? "Mute All" : "Unmute")); + g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute_from_ui), NULL); + dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_ENABLED, b_sink_available); + + // Slider + volume_slider_menuitem = slider_menu_item_new(b_sink_available, volume_percent); + dbusmenu_menuitem_child_append(root, mute_all_menuitem); + dbusmenu_menuitem_child_append(root, DBUSMENU_MENUITEM(volume_slider_menuitem)); + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_ENABLED, + b_sink_available); + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(volume_slider_menuitem), + DBUSMENU_MENUITEM_PROP_VISIBLE, + b_sink_available); + // Separator + DbusmenuMenuitem *separator = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); + dbusmenu_menuitem_child_append(root, separator); + + // Sound preferences dialog + DbusmenuMenuitem *settings_mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(settings_mi, DBUSMENU_MENUITEM_PROP_LABEL, + ("Sound Preferences...")); + dbusmenu_menuitem_child_append(root, settings_mi); + g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK(show_sound_settings_dialog), NULL); +} + +/** +set_global_mute_from_ui: +Callback for the dbusmenuitem button +**/ +static void set_global_mute_from_ui() +{ + b_all_muted = !b_all_muted; + toggle_global_mute(b_all_muted); + dbusmenu_menuitem_property_set(mute_all_menuitem, + DBUSMENU_MENUITEM_PROP_LABEL, + (b_all_muted == FALSE ? "Mute All" : "Unmute")); +} + + diff --git a/src/dbus-menu-manager.h b/src/dbus-menu-manager.h new file mode 100644 index 0000000..5f49e5f --- /dev/null +++ b/src/dbus-menu-manager.h @@ -0,0 +1,31 @@ +#ifndef __INCLUDE_DBUS_MENU_MANAGER_H__ +#define __INCLUDE_DBUS_MENU_MANAGER_H__ + +/* +This handles the management of the dbusmeneu items. +Copyright 2010 Canonical Ltd. + +Authors: + Conor Curran + +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 . +*/ + +void dbus_menu_manager_setup(); +void dbus_menu_manager_teardown(); +void dbus_menu_manager_update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble current_vol); +// TODO update pa_state should incorporate the method below ! +void dbus_menu_manager_update_mute_ui(gboolean incoming_mute_value); + +#endif + diff --git a/src/indicator-sound.h b/src/indicator-sound.h index 6d0c85c..e508390 100644 --- a/src/indicator-sound.h +++ b/src/indicator-sound.h @@ -1,3 +1,6 @@ +#ifndef __INCLUDE_INDICATOR_SOUND_H__ +#define __INCLUDE_INDICATOR_SOUND_H__ + /* A small wrapper utility to load indicators and put them as menu items into the gnome-panel using it's applet interface. @@ -28,3 +31,5 @@ gint get_state(); gchar* get_state_image_name(gint state); void prepare_for_tests(IndicatorObject * io); void tidy_up_hash(); + +#endif diff --git a/src/pulse-manager.c b/src/pulse-manager.c index 9b9d7cd..4594d2f 100644 --- a/src/pulse-manager.c +++ b/src/pulse-manager.c @@ -26,12 +26,10 @@ with this program. If not, see . #include #include "pulse-manager.h" -#include "sound-service.h" - +#include "dbus-menu-manager.h" static GHashTable *sink_hash = NULL; static SoundServiceDbus *dbus_service = NULL; -// Until we find a satisfactory default sink this index should remain < 0 static gint DEFAULT_SINK_INDEX = -1; static gboolean pa_server_available = FALSE; // PA related @@ -46,6 +44,7 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v static void pulse_source_info_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata); static void destroy_sink_info(void *value); static gboolean determine_sink_availability(); +static void reconnect_to_pulse(); /** @@ -65,14 +64,19 @@ void establish_pulse_activities(SoundServiceDbus *service) g_assert(pulse_context); sink_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_sink_info); + // Establish event callback registration pa_context_set_state_callback(pulse_context, context_state_callback, NULL); - pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); + // BUILD MENU ANYWHO - it will be updated + dbus_menu_manager_update_pa_state(FALSE, FALSE, FALSE, 0); + + pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); } void close_pulse_activites() { - if (pulse_context){ + if (pulse_context != NULL){ + g_debug("freeing the pulse context"); pa_context_unref(pulse_context); pulse_context = NULL; } @@ -82,6 +86,30 @@ void close_pulse_activites() g_debug("I just closed communication with Pulse"); } +/** +reconnect_to_pulse() +In the event of Pulseaudio flapping in the wind handle gracefully without +memory leaks ! +*/ +static void reconnect_to_pulse() +{ + // reset + if (pulse_context != NULL){ + g_debug("freeing the pulse context"); + pa_context_unref(pulse_context); + pulse_context = NULL; + } + g_hash_table_destroy(sink_hash); + + // reconnect + pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound"); + g_assert(pulse_context); + sink_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_sink_info); + // Establish event callback registration + pa_context_set_state_callback(pulse_context, context_state_callback, NULL); + dbus_menu_manager_update_pa_state(FALSE, FALSE, FALSE, 0); + pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); +} static void destroy_sink_info(void *value) { @@ -186,6 +214,8 @@ Use the base volume stored in the sink struct to calculate actual linear volumes */ void set_sink_volume(gdouble percent) { + if(pa_server_available == FALSE) + return; g_debug("in the pulse manager:set_sink_volume with percent %f", percent); if(DEFAULT_SINK_INDEX < 0) { @@ -245,12 +275,15 @@ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, in gboolean device_available = determine_sink_availability(); if(device_available == TRUE) { - update_pa_state(TRUE, device_available, default_sink_is_muted(), get_default_sink_volume()); + dbus_menu_manager_update_pa_state(TRUE, + device_available, + default_sink_is_muted(), + get_default_sink_volume()); } else{ //Update the indicator to show PA either is not ready or has no available sink g_warning("Cannot find a suitable default sink ..."); - update_pa_state(FALSE, device_available, TRUE, 0); + dbus_menu_manager_update_pa_state(FALSE, device_available, TRUE, 0); } } else{ @@ -291,7 +324,7 @@ static void pulse_default_sink_info_callback(pa_context *c, const pa_sink_info * } else { - update_pa_state(TRUE, determine_sink_availability(), default_sink_is_muted(), get_default_sink_volume()); + dbus_menu_manager_update_pa_state(TRUE, determine_sink_availability(), default_sink_is_muted(), get_default_sink_volume()); } } } @@ -354,7 +387,7 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v { g_debug("Updating Mute from PA manager with mute = %i", s->mute); sound_service_dbus_update_sink_mute(dbus_service, s->mute); - update_mute_ui(s->mute); + dbus_menu_manager_update_mute_ui(s->mute); if(s->mute == FALSE){ pa_volume_t vol = pa_cvolume_avg(&s->volume); gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM; @@ -366,12 +399,20 @@ static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, v } else { - // TODO ADD new sink - part of big refactor - g_debug("attempting to add new sink with name %s", info->name); - //sink_info *s; - //s = g_new0(sink_info, 1); - //update the sinks hash with new sink. - } + sink_info *value; + value = g_new0(sink_info, 1); + value->index = value->device_index = info->index; + value->name = g_strdup(info->name); + value->description = g_strdup(info->description); + value->icon_name = g_strdup(pa_proplist_gets(info->proplist, PA_PROP_DEVICE_ICON_NAME)); + value->active_port = (info->active_port != NULL); + value->mute = !!info->mute; + value->volume = info->volume; + value->base_volume = info->base_volume; + value->channel_map = info->channel_map; + g_hash_table_insert(sink_hash, GINT_TO_POINTER(value->index), value); + g_debug("pulse-manager:update_sink_info -> After adding a new sink to our hash"); + } } @@ -382,7 +423,7 @@ static void pulse_server_info_callback(pa_context *c, const pa_server_info *info if (info == NULL) { g_warning("No server - get the hell out of here"); - update_pa_state(FALSE, FALSE, TRUE, 0); + dbus_menu_manager_update_pa_state(FALSE, FALSE, TRUE, 0); pa_server_available = FALSE; return; } @@ -474,7 +515,7 @@ static void context_state_callback(pa_context *c, void *userdata) { g_debug("unconnected"); break; case PA_CONTEXT_CONNECTING: - g_debug("connecting"); + g_debug("connecting - waiting for the server to become available"); break; case PA_CONTEXT_AUTHORIZING: g_debug("authorizing"); @@ -484,8 +525,8 @@ static void context_state_callback(pa_context *c, void *userdata) { break; case PA_CONTEXT_FAILED: g_warning("FAILED to retrieve context - Is PulseAudio Daemon running ?"); - //Update the indicator to show PA either is not ready or has no available sink - update_pa_state(FALSE, FALSE, TRUE, 0); + pa_server_available = FALSE; + reconnect_to_pulse(); break; case PA_CONTEXT_TERMINATED: g_debug("context terminated"); diff --git a/src/pulse-manager.h b/src/pulse-manager.h index 1be5e44..e1777fb 100644 --- a/src/pulse-manager.h +++ b/src/pulse-manager.h @@ -1,3 +1,5 @@ +#ifndef __INCLUDE_PULSE_MANAGER_H__ +#define __INCLUDE_PULSE_MANAGER_H__ /* A small wrapper utility to load indicators and put them as menu items into the gnome-panel using it's applet interface. @@ -21,7 +23,6 @@ with this program. If not, see . */ - #include #include #include "sound-service-dbus.h" @@ -47,4 +48,5 @@ void set_sink_volume(gdouble percent); void toggle_global_mute(gboolean mute_value); void close_pulse_activites(); +#endif diff --git a/src/sound-service.c b/src/sound-service.c index 61bf702..815fcdc 100644 --- a/src/sound-service.c +++ b/src/sound-service.c @@ -19,107 +19,22 @@ 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 . */ + #include "sound-service.h" -#include "sound-service-dbus.h" +#include "dbus-menu-manager.h" #include "pulse-manager.h" -#include "slider-menu-item.h" -#include "common-defs.h" - -// GTK + DBUS static GMainLoop *mainloop = NULL; -static DbusmenuMenuitem *root_menuitem = NULL; -static DbusmenuMenuitem *mute_all_menuitem = NULL; -static SliderMenuItem *volume_slider_menuitem = NULL; -static SoundServiceDbus *dbus_interface = NULL; - -// PULSEAUDIO -static gboolean b_sink_available = FALSE; -static gboolean b_all_muted = FALSE; -static gboolean b_pulse_ready = FALSE; -static 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); - /**********************************************************************************************************************/ -// Init functions (GTK and DBUS) +// Init and exit functions /**********************************************************************************************************************/ -/** -Pass to the g_idle_add method - returning False will ensure that this method is never called again as it is removed as an event source. -**/ -static gboolean idle_routine (gpointer data) -{ - return FALSE; -} - - -static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data) -{ - GError * error = NULL; - if (!g_spawn_command_line_async("gnome-volume-control", &error)) - { - g_warning("Unable to show dialog: %s", error->message); - g_error_free(error); - } -} -/** -Build the DBus menu items. For now Mute all/Unmute is the only available option -**/ -static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service) -{ - // Mute button - mute_all_menuitem = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute")); - g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute_from_ui), NULL); - dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_ENABLED, b_sink_available); - - // Slider - volume_slider_menuitem = slider_menu_item_new(b_sink_available, volume_percent); - dbusmenu_menuitem_child_append(root, mute_all_menuitem); - dbusmenu_menuitem_child_append(root, DBUSMENU_MENUITEM(volume_slider_menuitem)); - - DbusmenuMenuitem *separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_append(root, separator); - DbusmenuMenuitem *settings_mi = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(settings_mi, DBUSMENU_MENUITEM_PROP_LABEL, - _("Sound Preferences...")); - dbusmenu_menuitem_child_append(root, settings_mi); - g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(show_sound_settings_dialog), NULL); -} /** -update_mute_ui: -'public' method allowing the server to update the mute UI +service_shutdown: +When the service interface starts to shutdown, we +should follow it. **/ -void update_mute_ui(gboolean incoming_mute_value) -{ - b_all_muted = incoming_mute_value; - dbusmenu_menuitem_property_set(mute_all_menuitem, - DBUSMENU_MENUITEM_PROP_LABEL, - _(b_all_muted == FALSE ? "Mute All" : "Unmute")); -} -/** -set_global_mute_from_ui: -Callback for the dbusmenuitem button -**/ -static void set_global_mute_from_ui() -{ - b_all_muted = !b_all_muted; - toggle_global_mute(b_all_muted); - dbusmenu_menuitem_property_set(mute_all_menuitem, - DBUSMENU_MENUITEM_PROP_LABEL, - _(b_all_muted == FALSE ? "Mute All" : "Unmute")); -} - - -/* When the service interface starts to shutdown, we - should follow it. - -*/ void service_shutdown (IndicatorService *service, gpointer user_data) { @@ -127,31 +42,15 @@ service_shutdown (IndicatorService *service, gpointer user_data) if (mainloop != NULL) { g_debug("Service shutdown !"); // TODO: uncomment for release !! - // TODO free the dbus interface !! - close_pulse_activites(); - g_main_loop_quit(mainloop); +/* close_pulse_activites();*/ +/* g_main_loop_quit(mainloop);*/ } return; } -void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble percent) -{ - b_sink_available = sink_available; - b_all_muted = sink_muted; - b_pulse_ready = pa_state; - volume_percent = percent; - g_debug("update pa state with state %i, availability of %i, mute value of %i and a volume percent is %f", pa_state, sink_available, sink_muted, volume_percent); - sound_service_dbus_update_sink_volume(dbus_interface, percent); - sound_service_dbus_update_sink_mute(dbus_interface, sink_muted); - - // Only rebuild the menu on start up... - if(volume_slider_menuitem == NULL) - rebuild_sound_menu(root_menuitem, dbus_interface); -} - - -/* Main, is well, main. It brings everything up and throws - us into the mainloop of no return. Some refactoring needed.*/ +/** +main: +**/ int main (int argc, char ** argv) { @@ -167,16 +66,7 @@ main (int argc, char ** argv) INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); - root_menuitem = dbusmenu_menuitem_new(); - g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); - - g_idle_add(idle_routine, root_menuitem); - - dbus_interface = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL); - - DbusmenuServer *server = dbusmenu_server_new(INDICATOR_SOUND_DBUS_OBJECT); - dbusmenu_server_set_root(server, root_menuitem); - establish_pulse_activities(dbus_interface); + dbus_menu_manager_setup(); // Run the loop mainloop = g_main_loop_new(NULL, FALSE); diff --git a/src/sound-service.h b/src/sound-service.h index d36ea41..cefbf45 100644 --- a/src/sound-service.h +++ b/src/sound-service.h @@ -27,13 +27,6 @@ with this program. If not, see . #include #include -#include -#include - -#include -#include -#include - #include #include "dbus-shared-names.h" @@ -41,7 +34,5 @@ with this program. If not, see . // ENTRY AND EXIT POINTS void service_shutdown(IndicatorService * service, gpointer user_data); int main (int argc, char ** argv); -void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_muted, gdouble current_vol); -void update_mute_ui(gboolean incoming_mute_value); -#endif +#endif -- cgit v1.2.3 From 259f58ec21be89f22f478243f4c8acf9a44231f9 Mon Sep 17 00:00:00 2001 From: Conor Curran Date: Wed, 3 Mar 2010 15:05:08 +0000 Subject: tests compiling --- src/dbus-menu-manager.c | 1 - src/indicator-sound.c | 1 + src/sound-service-dbus.c | 10 +++------- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/dbus-menu-manager.c b/src/dbus-menu-manager.c index 652e6b2..3e33932 100644 --- a/src/dbus-menu-manager.c +++ b/src/dbus-menu-manager.c @@ -113,7 +113,6 @@ void dbus_menu_manager_update_mute_ui(gboolean incoming_mute_value) dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, (b_all_muted == FALSE ? "Mute All" : "Unmute")); - //dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, (b_all_muted == FALSE ? _("Mute All") : _("Unmute"))); } diff --git a/src/indicator-sound.c b/src/indicator-sound.c index 3aca517..4bdfbb7 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -238,6 +238,7 @@ static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * p static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata) { + // TODO: This could be safer. if (connected) { if (sound_dbus_proxy == NULL) { GError * error = NULL; diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c index 99a9d34..952a2d3 100644 --- a/src/sound-service-dbus.c +++ b/src/sound-service-dbus.c @@ -29,8 +29,7 @@ #include "sound-service-marshal.h" #include "pulse-manager.h" -// DBUS methods - -// TODO - other should be static and moved from the header to here +// DBUS methods static gboolean sound_service_dbus_get_sink_volume(SoundServiceDbus* service, gdouble* volume_percent_input, GError** gerror); static gboolean sound_service_dbus_get_sink_mute(SoundServiceDbus* service, gboolean* mute_input, GError** gerror); static void sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror); @@ -41,7 +40,6 @@ typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; struct _SoundServiceDbusPrivate { - DBusGConnection *system_bus; DBusGConnection *connection; gdouble volume_percent; gboolean mute; @@ -116,13 +114,10 @@ sound_service_dbus_init (SoundServiceDbus *self) GError *error = NULL; SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); - priv->system_bus = NULL; priv->connection = NULL; priv->volume_percent = 0; - /* Get the system bus */ - priv->system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); - /* Put the object on DBus */ + /* Fetch the session bus */ priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { @@ -130,6 +125,7 @@ sound_service_dbus_init (SoundServiceDbus *self) g_error_free(error); return; } + /* register the service on it */ dbus_g_connection_register_g_object(priv->connection, "/org/ayatana/indicator/sound/service", G_OBJECT(self)); -- cgit v1.2.3 From e2a152628f20271f5373702ad88fda483e0403ca Mon Sep 17 00:00:00 2001 From: Conor Curran Date: Wed, 3 Mar 2010 21:57:43 +0000 Subject: debug trace was incorrect --- src/sound-service-dbus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c index 952a2d3..72337fd 100644 --- a/src/sound-service-dbus.c +++ b/src/sound-service-dbus.c @@ -48,7 +48,7 @@ struct _SoundServiceDbusPrivate /* Signals */ enum { - SINK_INPUT_WHILE_MUTED, + SINK_INPUT_WHILE_MUTED, SINK_VOLUME_UPDATE, SINK_MUTE_UPDATE, LAST_SIGNAL @@ -121,7 +121,7 @@ sound_service_dbus_init (SoundServiceDbus *self) priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { - g_error("Unable to connect to the session bus when creating application indicator: %s", error->message); + g_error("sound-service-dbus:Unable to connect to the session bus when creating indicator sound service : %s", error->message); g_error_free(error); return; } -- cgit v1.2.3