diff options
-rw-r--r-- | src/common-defs.h | 3 | ||||
-rw-r--r-- | src/indicator-sound.c | 224 | ||||
-rw-r--r-- | src/pulse-manager.c | 193 | ||||
-rw-r--r-- | src/pulse-manager.h | 1 | ||||
-rw-r--r-- | src/slider-menu-item.c | 3 | ||||
-rw-r--r-- | src/slider-menu-item.h | 1 | ||||
-rw-r--r-- | src/sound-service-dbus.c | 138 | ||||
-rw-r--r-- | src/sound-service-dbus.h | 19 | ||||
-rw-r--r-- | src/sound-service.c | 34 | ||||
-rw-r--r-- | src/sound-service.h | 4 | ||||
-rw-r--r-- | src/sound-service.xml | 19 |
11 files changed, 466 insertions, 173 deletions
diff --git a/src/common-defs.h b/src/common-defs.h index 6cacb49..896caba 100644 --- a/src/common-defs.h +++ b/src/common-defs.h @@ -1,6 +1,7 @@ /* constants used for signals on the dbus. This file is shared between client and server implementation */ #define SIGNAL_SINK_INPUT_WHILE_MUTED "SinkInputWhileMuted" -#define METHOD_SET_SINK_VOLUME "SetSinkVolume" +#define SIGNAL_SINK_VOLUME_UPDATE "SinkVolumeUpdate" +#define SIGNAL_SINK_MUTE_UPDATE "SinkMuteUpdate" // DBUS items #define DBUSMENU_SLIDER_MENUITEM_TYPE "x-canonical-ido-slider-item" diff --git a/src/indicator-sound.c b/src/indicator-sound.c index e8babdf..07ffe30 100644 --- a/src/indicator-sound.c +++ b/src/indicator-sound.c @@ -78,18 +78,40 @@ static GtkLabel * get_label (IndicatorObject * io); static GtkImage * get_icon (IndicatorObject * io); static GtkMenu * get_menu (IndicatorObject * io); static GtkWidget *volume_slider = NULL; -static gdouble input_value_from_across_the_dbus = 0.0; -static GtkImage *speaker_image = NULL; static gboolean new_slider_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client); static void slider_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkWidget *widget); -static gboolean slider_value_changed_event_cb(GtkRange *range, GtkScrollType scroll, double value, gpointer user_data); -static void change_speaker_image(gdouble volume_percent); +static gboolean slider_value_changed_event_cb(GtkRange *range, GtkScrollType scroll_type, gdouble input_value, gpointer user_data); + +static void prepare_state_machine(); +static void determine_state_from_volume(gdouble volume_percent); +static void update_state(const gint state); +static void fetch_volume_percent_from_dbus(); +static void fetch_mute_value_from_dbus(); +/*static void revert_state();*/ // DBUS communication static DBusGProxy *sound_dbus_proxy = NULL; static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata); -static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gint sink_index, gboolean value, gpointer userdata); + +static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gboolean value, gpointer userdata); +static void catch_signal_sink_volume_update(DBusGProxy * proxy, gdouble volume_percent, gpointer userdata); +static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value, gpointer userdata); + +/****Volume States 'members' ***/ +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 GHashTable *volume_states = NULL; +static GtkImage *speaker_image = NULL; +static gint current_state = 0; +static gint previous_state = 0; +static gdouble initial_volume_percent = 0; +static gboolean initial_mute = FALSE; static void indicator_sound_class_init (IndicatorSoundClass *klass) @@ -119,9 +141,37 @@ static void indicator_sound_init (IndicatorSound *self) /* Now let's fire these guys up. */ self->service = indicator_service_manager_new_version(INDICATOR_SOUND_DBUS_NAME, INDICATOR_SOUND_DBUS_VERSION); g_signal_connect(G_OBJECT(self->service), INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_changed), self); + prepare_state_machine(); return; } + +/*static void test_images_hash()*/ +/*{*/ +/* g_debug("about to test the images hash"); */ +/* gchar* current_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(current_state));*/ +/* g_debug("start up current image name = %s", current_name); */ +/* gchar* previous_name = g_hash_table_lookup(volume_states, GINT_TO_POINTER(previous_state));*/ +/* g_debug("start up previous image name = %s", previous_name); */ +/*}*/ + +/* +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")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_ZERO), g_strdup(/*"audio-volume-zero"*/"audio-volume-muted")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_LOW), g_strdup("audio-volume-low")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MEDIUM), g_strdup("audio-volume-medium")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_HIGH), g_strdup("audio-volume-high")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_MUTED_WHILE_INPUT), g_strdup(/*"audio-volume-muted-blocking"*/"audio-volume-muted")); + g_hash_table_insert(volume_states, GINT_TO_POINTER(STATE_SINKS_NONE), g_strdup(/*"audio-output-none"*/"audio-volume-muted")); + //test_images_hash(); +} + static void connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer userdata) { @@ -142,8 +192,18 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u g_error_free(error); } g_debug("about to connect to the signals"); - dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_INPUT_WHILE_MUTED, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INVALID); + dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_INPUT_WHILE_MUTED, G_TYPE_BOOLEAN, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_INPUT_WHILE_MUTED, G_CALLBACK(catch_signal_sink_input_while_muted), NULL, NULL); + dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_VOLUME_UPDATE, G_TYPE_DOUBLE, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_VOLUME_UPDATE, G_CALLBACK(catch_signal_sink_volume_update), NULL, NULL); + dbus_g_proxy_add_signal(sound_dbus_proxy, SIGNAL_SINK_MUTE_UPDATE, G_TYPE_BOOLEAN, G_TYPE_INVALID); + dbus_g_proxy_connect_signal(sound_dbus_proxy, SIGNAL_SINK_MUTE_UPDATE, G_CALLBACK(catch_signal_sink_mute_update), NULL, NULL); + + // Ensure we are in a coherent state with the service at start up. + // Preserve ordering! + fetch_volume_percent_from_dbus(); + fetch_mute_value_from_dbus(); } } else { @@ -153,13 +213,75 @@ connection_changed (IndicatorServiceManager * sm, gboolean connected, gpointer u return; } +static void fetch_volume_percent_from_dbus() +{ + GError * error = NULL; + gdouble *volume_percent_input; + volume_percent_input = g_new0(gdouble, 1); + org_ayatana_indicator_sound_get_sink_volume(sound_dbus_proxy, volume_percent_input, &error); + if (error != NULL) { + g_warning("Unable to fetch VOLUME at indicator start up: %s", error->message); + g_error_free(error); + g_free(volume_percent_input); + return; + } + initial_volume_percent = *volume_percent_input; + determine_state_from_volume(initial_volume_percent); + g_free(volume_percent_input); + g_debug("at the indicator start up and the volume percent returned from dbus method is %f", initial_volume_percent); +} -static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gint sink_index, gboolean value, gpointer userdata) +static void fetch_mute_value_from_dbus() { - //gtk_image_set_from_icon_name(speaker_image, "audio-volume-muted-blocking-symbolic", GTK_ICON_SIZE_MENU); - g_debug("signal caught - I don't believe it ! with index %i and value %i", sink_index, value); + GError * error = NULL; + gboolean *mute_input; + mute_input = g_new0(gboolean, 1); + org_ayatana_indicator_sound_get_sink_mute(sound_dbus_proxy, mute_input, &error); + if (error != NULL) { + g_warning("Unable to fetch MUTE at indicator start up: %s", error->message); + g_error_free(error); + g_free(mute_input); + return; + } + initial_mute = *mute_input; + if (initial_mute == TRUE) + update_state(STATE_MUTED); + g_free(mute_input); + g_debug("at the indicator start up and the MUTE returned from dbus method is %i", initial_mute); +} + +static void catch_signal_sink_input_while_muted(DBusGProxy * proxy, gboolean block_value, gpointer userdata) +{ + g_debug("signal caught - sink input while muted with value %i", block_value); +} + +static void catch_signal_sink_volume_update(DBusGProxy *proxy, gdouble volume_percent, gpointer userdata) +{ + g_debug("signal caught - update sink volume with value : %f", volume_percent); + GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); + gboolean in_use = gtk_widget_has_grab(volume_slider); + if(in_use == TRUE) + { + g_debug("looks like the slider is the UI element in use therefore ignore this volume message => prevent circular semi feedback."); + return; + } + GtkRange *range = (GtkRange*)slider; + gtk_range_set_value(range, volume_percent); + determine_state_from_volume(volume_percent); } +static void catch_signal_sink_mute_update(DBusGProxy *proxy, gboolean mute_value, gpointer userdata) +{ + //We can be sure the service won't send a mute signal unless it has changed ! + //UNMUTE's force a volume update therefore icon is updated appropriately => no need for unmute handling here. + if(mute_value == TRUE) + { + g_debug("signal caught - sink mute update - MUTE"); + update_state(STATE_MUTED); + } +} + + static void indicator_sound_dispose (GObject *object) { @@ -169,6 +291,7 @@ indicator_sound_dispose (GObject *object) 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; } @@ -189,26 +312,55 @@ get_label (IndicatorObject * io) static GtkImage * get_icon (IndicatorObject * io) { - speaker_image = GTK_IMAGE(gtk_image_new_from_icon_name("audio-volume-high", GTK_ICON_SIZE_MENU)); + 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 change_speaker_image(gdouble volume_percent) -{ +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 revert_state()*/ +/*{*/ + +/* g_debug("revert state beginning - previous_state = %i", previous_state);*/ +/* current_state = previous_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);*/ +/* g_debug("after reverting back to previous state of %i", current_state);*/ +/*}*/ + +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){ - gtk_image_set_from_icon_name(speaker_image, "audio-volume-low", GTK_ICON_SIZE_MENU); + state = STATE_LOW; } else if(volume_percent < 70.0 && volume_percent > 30.0){ - gtk_image_set_from_icon_name(speaker_image, "audio-volume-medium", GTK_ICON_SIZE_MENU); + state = STATE_MEDIUM; } else if(volume_percent > 70.0){ - gtk_image_set_from_icon_name(speaker_image, "audio-volume-high", GTK_ICON_SIZE_MENU); - } - else if(volume_percent <= 0.0){ - gtk_image_set_from_icon_name(speaker_image, "audio-volume-zero", GTK_ICON_SIZE_MENU); + 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 @@ -233,10 +385,13 @@ static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * p 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); - - GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); + + GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); + g_signal_connect(slider, "change-value", G_CALLBACK(slider_value_changed_event_cb), newitem); GtkRange* range = (GtkRange*)slider; - g_signal_connect(G_OBJECT(range), "change-value", G_CALLBACK(slider_value_changed_event_cb), newitem); + gtk_range_set_value(range, initial_volume_percent); + + gtk_widget_show_all(volume_slider); return TRUE; } @@ -244,30 +399,23 @@ static gboolean new_slider_item(DbusmenuMenuitem * newitem, DbusmenuMenuitem * p we need to be responsive to that. */ static void slider_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GValue * value, GtkWidget *widget) { - g_debug("slider_prop_change_cb "); + g_debug("slider_prop_change_cb - dodgy updater "); g_debug("about to set the slider to %f", g_value_get_double(value)); GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)volume_slider); - GtkRange* range = (GtkRange*)slider; - gdouble level; - level = gtk_range_get_fill_level(range); - input_value_from_across_the_dbus = level; - g_debug("the current level is %f", level); - + GtkRange* range = (GtkRange*)slider; gtk_range_set_value(range, g_value_get_double(value)); return; } -static gboolean slider_value_changed_event_cb(GtkRange *range, GtkScrollType scroll, double slider_value, gpointer user_data) +static gboolean slider_value_changed_event_cb(GtkRange *range, GtkScrollType scroll_type, gdouble input_value, gpointer user_data) { - if(slider_value != input_value_from_across_the_dbus) - { - DbusmenuMenuitem *item = (DbusmenuMenuitem*)user_data; - GValue value = {0}; - g_value_init(&value, G_TYPE_DOUBLE); - g_value_set_double(&value, slider_value); - dbusmenu_menuitem_handle_event (item, "slider_change", &value, 0); - change_speaker_image(slider_value); - } + DbusmenuMenuitem *item = (DbusmenuMenuitem*)user_data; + gdouble clamped_input = CLAMP(input_value, 0, 100); + GValue value = {0}; + g_debug("User input on SLIDER - = %f", clamped_input); + g_value_init(&value, G_TYPE_DOUBLE); + g_value_set_double(&value, clamped_input); + dbusmenu_menuitem_handle_event (item, "slider_change", &value, 0); return FALSE; } diff --git a/src/pulse-manager.c b/src/pulse-manager.c index ce63b0d..8bf6cac 100644 --- a/src/pulse-manager.c +++ b/src/pulse-manager.c @@ -19,34 +19,13 @@ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink_inf static void context_success_callback(pa_context *c, int success, void *userdata); static void pulse_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata); static void pulse_server_info_callback(pa_context *c, const pa_server_info *info, void *userdata); +static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, void *userdata); static void destroy_sink_info(void *value); + /* -Refine the resolution of the slider or binary scale it to achieve a more subtle volume control. -Use the base volume stored in the sink struct to calculate actual linear volumes. +Entry point */ -void set_sink_volume(gdouble percent) -{ - g_debug("in the pulse manager:set_sink_volume with percent %f", percent); - if(DEFAULT_SINK_INDEX < 0) - { - g_warning("We have no default sink !!! - returning after not attempting to set any volume of any sink"); - return; - } - gdouble linear_input = (gdouble)(percent); - linear_input /= 100.0; - g_debug("linear double input = %f", linear_input); - pa_volume_t new_volume = pa_sw_volume_from_linear(linear_input); - // Use this to achieve more accurate scaling using the base volume (in the sink struct already!) - //pa_volume_t new_volume = (pa_volume_t) ((GPOINTER_TO_INT(linear_input) * s->base_volume) / 100); - g_debug("about to try to set the sw volume to a linear volume of %f", pa_sw_volume_to_linear(new_volume)); - g_debug("and an actual volume of %f", (gdouble)new_volume); - pa_cvolume dev_vol; - sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX)); - pa_cvolume_set(&dev_vol, s->volume.channels, new_volume); - pa_operation_unref(pa_context_set_sink_volume_by_index(pulse_context, DEFAULT_SINK_INDEX, &dev_vol, NULL, NULL)); -} - void establish_pulse_activities(SoundServiceDbus *service) { dbus_service = service; @@ -70,20 +49,9 @@ void close_pulse_activites() g_hash_table_destroy(sink_hash); pa_glib_mainloop_free(pa_main_loop); pa_main_loop = NULL; + g_debug("I just closed communication with Pulse"); } -static void mute_each_sink(gpointer key, gpointer value, gpointer user_data) -{ - sink_info *info = (sink_info*)value; - pa_operation_unref(pa_context_set_sink_mute_by_index(pulse_context, info->index, GPOINTER_TO_INT(user_data), context_success_callback, NULL)); - g_debug("in the pulse manager: mute each sink %i", GPOINTER_TO_INT(user_data)); -} - -void toggle_global_mute(gboolean mute_value) -{ - g_hash_table_foreach(sink_hash, mute_each_sink, GINT_TO_POINTER(mute_value)); - g_debug("in the pulse manager: toggle global mute value %i", mute_value); -} static void destroy_sink_info(void *value) { @@ -94,15 +62,19 @@ static void destroy_sink_info(void *value) g_free(sink); } -static void test_hash(){ - guint size = 0; - size = g_hash_table_size(sink_hash); - g_debug("Size of hash = %i", size); - sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX)); - g_debug("The name of our sink is %s", s->name); - g_debug("and the max volume is %f", (gdouble)s->base_volume); +/*static void test_hash(){*/ +/* guint size = 0;*/ +/* size = g_hash_table_size(sink_hash);*/ +/* g_debug("Size of hash = %i", size);*/ +/* sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX)); */ +/* g_debug("The name of our sink is %s", s->name); */ +/* g_debug("and the max volume is %f", (gdouble)s->base_volume); */ -} +/*}*/ + +/* +Controllers & Utilities +*/ static gboolean sink_available() { @@ -121,19 +93,22 @@ static gboolean default_sink_is_muted() return FALSE; if (g_hash_table_size(sink_hash) < 1) return FALSE; - // TODO ensure hash has a key with this value! sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX)); return s->mute; } static void check_sink_input_while_muted_event(gint sink_index) { + g_debug("SINKINPUTWHILEMUTED SIGNAL EVENT TO BE SENT FROM PA MANAGER - check trace for value"); + if (default_sink_is_muted(sink_index) == TRUE) { - g_debug("SINKINPUTWHILEMUTED SIGNAL EVENT TO BE SENT FROM PA MANAGER"); - sound_service_dbus_sink_input_while_muted (dbus_service, sink_index, TRUE); + sound_service_dbus_sink_input_while_muted (dbus_service, TRUE); + } + else + { + sound_service_dbus_sink_input_while_muted(dbus_service, FALSE); } - return; } static gdouble get_default_sink_volume() @@ -147,6 +122,47 @@ static gdouble get_default_sink_volume() return value; } +static void mute_each_sink(gpointer key, gpointer value, gpointer user_data) +{ + sink_info *info = (sink_info*)value; + pa_operation_unref(pa_context_set_sink_mute_by_index(pulse_context, info->index, GPOINTER_TO_INT(user_data), context_success_callback, NULL)); + g_debug("in the pulse manager: mute each sink %i", GPOINTER_TO_INT(user_data)); +} + +void toggle_global_mute(gboolean mute_value) +{ + g_hash_table_foreach(sink_hash, mute_each_sink, GINT_TO_POINTER(mute_value)); + g_debug("in the pulse manager: toggle global mute value %i", mute_value); +} + + +/* +Refine the resolution of the slider or binary scale it to achieve a more subtle volume control. +Use the base volume stored in the sink struct to calculate actual linear volumes. +*/ +void set_sink_volume(gdouble percent) +{ + g_debug("in the pulse manager:set_sink_volume with percent %f", percent); + if(DEFAULT_SINK_INDEX < 0) + { + g_warning("We have no default sink !!! - returning after not attempting to set any volume of any sink"); + return; + } + gdouble linear_input = (gdouble)(percent); + linear_input /= 100.0; + g_debug("linear double input = %f", linear_input); + pa_volume_t new_volume = pa_sw_volume_from_linear(linear_input); + // Use this to achieve more accurate scaling using the base volume (in the sink struct already!) + //pa_volume_t new_volume = (pa_volume_t) ((GPOINTER_TO_INT(linear_input) * s->base_volume) / 100); + g_debug("about to try to set the sw volume to a linear volume of %f", pa_sw_volume_to_linear(new_volume)); + g_debug("and an actual volume of %f", (gdouble)new_volume); + pa_cvolume dev_vol; + sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(DEFAULT_SINK_INDEX)); + pa_cvolume_set(&dev_vol, s->volume.channels, new_volume); + + pa_operation_unref(pa_context_set_sink_volume_by_index(pulse_context, DEFAULT_SINK_INDEX, &dev_vol, NULL, NULL)); +} + /**********************************************************************************************************************/ // Pulse-Audio asychronous call-backs @@ -177,33 +193,33 @@ static void context_success_callback(pa_context *c, int success, void *userdata) /** On Service startup this callback will be called multiple times resulting our sinks_hash container to be filled with the available sinks. -key -> index -value -> sink_info -For now this callback it assumes it only used at startup. It may be necessary to use if sinks become available after startup +For now this callback it assumes it only used at startup. It may be necessary to use if sinks become available after startup. +Major candidate for refactoring. **/ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, int eol, void *userdata) { if (eol > 0) { - gboolean device_available; - device_available = sink_available(); + gboolean device_available = sink_available(); if(device_available == TRUE) { // Hopefully the PA server has set the default device if not default to 0 DEFAULT_SINK_INDEX = (DEFAULT_SINK_INDEX < 0) ? 0 : DEFAULT_SINK_INDEX; - test_hash(); + // TODO optimize + // Cache method returns! (unneccessary multiple utility calls) + // test_hash(); update_pa_state(TRUE, device_available, default_sink_is_muted(), get_default_sink_volume()); - g_debug("default sink index : %d", DEFAULT_SINK_INDEX); - + sound_service_dbus_update_sink_volume(dbus_service, get_default_sink_volume()); + sound_service_dbus_update_sink_mute(dbus_service, default_sink_is_muted()); + g_debug("default sink index : %d", DEFAULT_SINK_INDEX); } 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); } - } else{ - g_debug("About to add an item to our hash"); + g_debug("About to add an item to our hash"); sink_info *value; value = g_new0(sink_info, 1); value->index = value->device_index = sink->index; @@ -216,7 +232,7 @@ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, in value->base_volume = sink->base_volume; value->channel_map = sink->channel_map; g_hash_table_insert(sink_hash, GINT_TO_POINTER(sink->index), value); - g_debug("After adding an item to our hash"); + g_debug("After adding an item to our hash"); } } @@ -253,7 +269,58 @@ static void pulse_sink_input_info_callback(pa_context *c, const pa_sink_input_in g_debug("\n SINK INPUT INFO sink index : %d \n", info->sink); check_sink_input_while_muted_event(info->sink); } -} +} + +static void update_sink_info(pa_context *c, const pa_sink_info *info, int eol, void *userdata) +{ + if (eol > 0) { + if (pa_context_errno(c) == PA_ERR_NOENTITY) + return; + g_warning("Sink INPUT info callback failure"); + return; + } + + GList *keys = g_hash_table_get_keys(sink_hash); + gint position = g_list_index(keys, GINT_TO_POINTER(info->index)); + if(position >= 0) // => index is within the keys of the hash. + { + sink_info *s = g_hash_table_lookup(sink_hash, GINT_TO_POINTER(info->index)); + //g_debug("attempting to update sink with name %s", s->name); + s->name = g_strdup(info->name); + s->description = g_strdup(info->description); + s->icon_name = g_strdup(pa_proplist_gets(info->proplist, PA_PROP_DEVICE_ICON_NAME)); + s->active_port = (info->active_port != NULL); + // NASTY!! + gboolean mute_changed = s->mute != !!info->mute; + s->mute = !!info->mute; + s->volume = info->volume; + s->base_volume = info->base_volume; + s->channel_map = info->channel_map; + if(DEFAULT_SINK_INDEX == s->index) + { + //update the UI + pa_volume_t vol = pa_cvolume_avg(&s->volume); + // Use the base of the device to ensure maximum acceptable levels on the hardware + gdouble volume_percent = (vol/s->base_volume) * 100; + g_debug("When using base volume => volume = %f", volume_percent); + g_debug("about to update ui with linear volume of %f", pa_sw_volume_to_linear(vol)); + sound_service_dbus_update_sink_volume(dbus_service, pa_sw_volume_to_linear(vol)); + if (mute_changed == TRUE) + sound_service_dbus_update_sink_mute(dbus_service, s->mute); + + update_mute_ui(s->mute); + } + } + 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. + } +} + static void pulse_server_info_callback(pa_context *c, const pa_server_info *info, void *userdata) { @@ -289,7 +356,11 @@ static void pulse_server_info_callback(pa_context *c, const pa_server_info *info static void subscribed_events_callback(pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata){ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK: - //g_debug("Event sink for %i", index); + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + //TODO handle the remove event => if its our default sink - grey out the ui with update_pa_state + } else { + pa_operation_unref(pa_context_get_sink_info_by_index(c, index, update_sink_info, userdata)); + } break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: // This will be triggered when the sink receives input from a new stream diff --git a/src/pulse-manager.h b/src/pulse-manager.h index ddfc721..8fa32b3 100644 --- a/src/pulse-manager.h +++ b/src/pulse-manager.h @@ -24,4 +24,3 @@ void toggle_global_mute(gboolean mute_value); void close_pulse_activites(); - diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c index 2620e6e..2e8321d 100644 --- a/src/slider-menu-item.c +++ b/src/slider-menu-item.c @@ -91,11 +91,8 @@ handle_event (DbusmenuMenuitem * mi, const gchar * name, const GValue * value, g SliderMenuItem* slider_menu_item_new(gboolean sinks_available, gdouble start_volume) { SliderMenuItem *self = g_object_new(SLIDER_MENU_ITEM_TYPE, NULL); - //SliderMenuItemPrivate * priv = SLIDER_MENU_ITEM_GET_PRIVATE(self); dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_SLIDER_MENUITEM_TYPE); - //TODO: If no valid sinks are found grey out the item(s) dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_ENABLED, sinks_available); - //g_debug("Sending the initial value of the slider over dbus = %f", start_volume * 100); return self; } diff --git a/src/slider-menu-item.h b/src/slider-menu-item.h index 48e71ff..c4423e3 100644 --- a/src/slider-menu-item.h +++ b/src/slider-menu-item.h @@ -46,6 +46,7 @@ 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); G_END_DECLS diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c index 5e22dae..a4c6eda 100644 --- a/src/sound-service-dbus.c +++ b/src/sound-service-dbus.c @@ -25,24 +25,34 @@ #include <dbus/dbus-glib.h> #include "dbus-shared-names.h" #include "sound-service-dbus.h" -#include "sound-service-server.h" #include "common-defs.h" #include "sound-service-marshal.h" #include "pulse-manager.h" +// DBUS methods - +// TODO - other should be static and moved from the header to here +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); + +#include "sound-service-server.h" + typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; struct _SoundServiceDbusPrivate { DBusGConnection *system_bus; DBusGConnection *connection; - GHashTable *sinks_hash; + gdouble volume_percent; + gboolean mute; }; /* Signals */ enum { SINK_INPUT_WHILE_MUTED, + SINK_VOLUME_UPDATE, + SINK_MUTE_UPDATE, LAST_SIGNAL }; @@ -51,12 +61,12 @@ static guint signals[LAST_SIGNAL] = { 0 }; #define SOUND_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusPrivate)) - static void sound_service_dbus_class_init (SoundServiceDbusClass *klass); static void sound_service_dbus_init (SoundServiceDbus *self); static void sound_service_dbus_dispose (GObject *object); static void sound_service_dbus_finalize (GObject *object); + /* GObject Boilerplate */ G_DEFINE_TYPE (SoundServiceDbus, sound_service_dbus, G_TYPE_OBJECT); @@ -79,49 +89,27 @@ sound_service_dbus_class_init (SoundServiceDbusClass *klass) G_SIGNAL_RUN_LAST, 0, NULL, NULL, - _sound_service_marshal_VOID__INT_BOOLEAN, - G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BOOLEAN); -} - -/** -DBUS Method Callbacks -TODO do not see the point in this returning a boolean and also needing a sink index since the service needs to be ultimately aware of what sink is chosen. -**/ -void sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror) -{ - g_debug("in the set sink volume method in the sound service dbus!, with volume_percent of %i", volume_percent); - set_sink_volume(volume_percent); -} + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); -GList * -sound_service_dbus_get_sink_list (SoundServiceDbus *self) -{ - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); - - return g_hash_table_get_keys (priv->sinks_hash); -} + signals[SINK_VOLUME_UPDATE] = g_signal_new("sink-volume-update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, G_TYPE_DOUBLE); + signals[SINK_MUTE_UPDATE] = g_signal_new("sink-mute-update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); -/** -Utility methods to emit signals from the service into the ether. -**/ -void sound_service_dbus_sink_input_while_muted(SoundServiceDbus* obj, gint sink_index, gboolean value) -{ -/* g_assert((num < LAST_SIGNAL) && (num >= 0));*/ - g_debug("Emitting signal: SINK_INPUT_WHILE_MUTED, with sink_index %i and value %i", sink_index, value); - g_signal_emit(obj, - signals[SINK_INPUT_WHILE_MUTED], - 0, - sink_index, - value); } -void set_pa_sinks_hash(SoundServiceDbus *self, GHashTable *sinks) -{ - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); - priv->sinks_hash = sinks; -} - static void sound_service_dbus_init (SoundServiceDbus *self) { @@ -130,6 +118,7 @@ sound_service_dbus_init (SoundServiceDbus *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); @@ -148,6 +137,7 @@ sound_service_dbus_init (SoundServiceDbus *self) return; } + static void sound_service_dbus_dispose (GObject *object) { @@ -162,3 +152,69 @@ sound_service_dbus_finalize (GObject *object) return; } + +/** +DBUS Method Callbacks +**/ +static void sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror) +{ + g_debug("in the set sink volume method in the sound service dbus!, with volume_percent of %i", volume_percent); + set_sink_volume(volume_percent); +} + +static gboolean sound_service_dbus_get_sink_volume (SoundServiceDbus *self, gdouble *volume_percent_input, GError** gerror) +{ + SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); + g_debug("Get sink volume method in the sound service dbus!, about to send over volume percent of %f", priv->volume_percent); + *volume_percent_input = priv->volume_percent; + return TRUE; +} + +static gboolean sound_service_dbus_get_sink_mute (SoundServiceDbus *self, gboolean *mute_input, GError** gerror) +{ + SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); + g_debug("Get sink mute - sound service dbus!, about to send over mute_value of %i", priv->mute); + *mute_input = priv->mute; + return TRUE; +} + +/** +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) +{ + g_debug("Emitting signal: SINK_INPUT_WHILE_MUTED, with block_value: %i", block_value); + g_signal_emit(obj, + signals[SINK_INPUT_WHILE_MUTED], + 0, + block_value); +} + +void sound_service_dbus_update_sink_volume(SoundServiceDbus* obj, gdouble sink_volume) +{ + SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (obj); + priv->volume_percent = sink_volume * 100; + + g_debug("Emitting signal: SINK_VOLUME_UPDATE, with sink_volme %f", priv->volume_percent); + g_signal_emit(obj, + signals[SINK_VOLUME_UPDATE], + 0, + priv->volume_percent); +} + +void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, gboolean sink_mute) +{ + 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; + + g_signal_emit(obj, + signals[SINK_MUTE_UPDATE], + 0, + priv->mute); +} + + + diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h index eea6d26..ef0d7dd 100644 --- a/src/sound-service-dbus.h +++ b/src/sound-service-dbus.h @@ -50,19 +50,16 @@ struct _SoundServiceDbus { struct _SoundServiceDbusClass { GObjectClass parent_class; /* Signals -> outward messages to the DBUS and beyond*/ - void (* sink_input_while_muted) (SoundServiceDbus *self, gint sink_index, gboolean is_muted, gpointer sound_data); -}; - + // TODO - ARE THESE NECESSARY ? + //void (* sink_input_while_muted) (SoundServiceDbus *self, gboolean block_value, gpointer sound_data); + //void (* sink_volume_update) (SoundServiceDbus *self, gdouble sink_volume, gpointer sound_data); +}; GType sound_service_dbus_get_type (void) G_GNUC_CONST; -// Utility methods to get the messages across into the sound-service-dbus -void sound_service_dbus_sink_input_while_muted (SoundServiceDbus* obj, gint sink_index, gboolean value); -void set_pa_sinks_hash(SoundServiceDbus *self, GHashTable *sinks); - -// DBUS METHODS -void sound_service_dbus_set_sink_volume(SoundServiceDbus* service, const guint volume_percent, GError** gerror); -GList *sound_service_dbus_get_sink_list(SoundServiceDbus* service); - +// 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_volume(SoundServiceDbus* obj, gdouble sink_volume); +void sound_service_dbus_update_sink_mute(SoundServiceDbus* obj, gboolean sink_mute); G_END_DECLS diff --git a/src/sound-service.c b/src/sound-service.c index 76975cd..4f76dd1 100644 --- a/src/sound-service.c +++ b/src/sound-service.c @@ -5,7 +5,6 @@ Copyright 2010 Canonical Ltd. Authors: Conor Curran <conor.curran@canonical.com> Ted Gould <ted@canonical.com> - Christoph Korn <c_korn@gmx.de> Cody Russell <crussell@canonical.com> This program is free software: you can redistribute it and/or modify it @@ -40,7 +39,7 @@ static gboolean b_all_muted = FALSE; static gboolean b_pulse_ready = FALSE; static gdouble volume_percent = 0.0; -static void set_global_mute(); +static void set_global_mute_from_ui(); static gboolean idle_routine (gpointer data); static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service); @@ -74,7 +73,7 @@ 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), NULL); + g_signal_connect(G_OBJECT(mute_all_menuitem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(set_global_mute_from_ui), NULL); //TODO: If no valid sinks are found grey out the item(s) dbusmenu_menuitem_property_set_bool(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_ENABLED, b_sink_available); @@ -93,8 +92,14 @@ static void rebuild_sound_menu(DbusmenuMenuitem *root, SoundServiceDbus *service g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(show_sound_settings_dialog), NULL); } +/* 'public' method allowing the server to update the mute UI*/ +void update_mute_ui(gboolean incoming_mute_value) +{ + b_all_muted = incoming_mute_value; + dbusmenu_menuitem_property_set(mute_all_menuitem, DBUSMENU_MENUITEM_PROP_LABEL, _(b_all_muted == FALSE ? "Mute All" : "Unmute")); +} -static void set_global_mute() +static void set_global_mute_from_ui() { b_all_muted = !b_all_muted; toggle_global_mute(b_all_muted); @@ -102,8 +107,9 @@ static void set_global_mute() /* GValue value = {0};*/ /* g_value_init(&value, G_TYPE_DOUBLE);*/ -/* g_value_set_double(&value, 100.0);*/ +/* g_value_set_double(&value, 99.0);*/ /* // Testing*/ +/* g_debug("BUGGY volume update");*/ /* dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(volume_slider_menuitem), DBUSMENU_SLIDER_MENUITEM_PROP_VOLUME, &value);*/ } @@ -116,9 +122,9 @@ service_shutdown (IndicatorService *service, gpointer user_data) { if (mainloop != NULL) { - - g_debug("Service shutdown - but commented out for right now"); - close_pulse_activites(); + g_debug("Service shutdown !"); + // TODO: uncomment for release !! + close_pulse_activites(); g_main_loop_quit(mainloop); } return; @@ -132,11 +138,6 @@ void update_pa_state(gboolean pa_state, gboolean sink_available, gboolean sink_m 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); rebuild_sound_menu(root_menuitem, dbus_interface); - g_debug("About to send over the volume slider"); - GValue value = {0}; - g_value_init(&value, G_TYPE_DOUBLE); - g_value_set_double(&value, volume_percent * 100); - dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(volume_slider_menuitem), DBUSMENU_SLIDER_MENUITEM_PROP_VOLUME, &value); } @@ -168,6 +169,13 @@ main (int argc, char ** argv) dbusmenu_server_set_root(server, root_menuitem); establish_pulse_activities(dbus_interface); +/* // THIS DOES NOT WORK FROM HERE*/ +/* GValue value = {0};*/ +/* g_value_init(&value, G_TYPE_DOUBLE);*/ +/* g_value_set_double(&value, volume_percent * 100);*/ +/* g_debug("About to send over the volume slider initial value %f", (volume_percent * 100));*/ +/* dbusmenu_menuitem_property_set_value(DBUSMENU_MENUITEM(volume_slider_menuitem), DBUSMENU_SLIDER_MENUITEM_PROP_VOLUME, &value);*/ + // Run the loop mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); diff --git a/src/sound-service.h b/src/sound-service.h index fec52a3..d36ea41 100644 --- a/src/sound-service.h +++ b/src/sound-service.h @@ -8,7 +8,6 @@ Copyright 2010 Canonical Ltd. Authors: Conor Curran <conor.curran@canonical.com> Ted Gould <ted@canonical.com> - Christoph Korn <c_korn@gmx.de> Cody Russell <crussell@canonical.com> This program is free software: you can redistribute it and/or modify it @@ -39,11 +38,10 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include "dbus-shared-names.h" - // 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 diff --git a/src/sound-service.xml b/src/sound-service.xml index 26d83e5..580f0e1 100644 --- a/src/sound-service.xml +++ b/src/sound-service.xml @@ -5,14 +5,31 @@ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_set_sink_volume"/> <arg type='u' name='volume_percent' direction="in"/> </method> + <method name = "GetSinkVolume"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="sound_service_dbus_get_sink_volume"/> + <arg type='d' name='volume_percent_input' direction="out"/> + </method> + + <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> <!-- 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="sink_index" type="x" direction="out"/> + <arg name="block_value" type="b" direction="out"/> + </signal> + + <signal name="SinkVolumeUpdate"> + <arg name="volume_percent" type="u" direction="out"/> + </signal> + + <signal name="SinkMuteUpdate"> <arg name="mute_value" type="b" direction="out"/> </signal> + </interface> </node> |