diff options
Diffstat (limited to 'src/sound-state-manager.c')
-rw-r--r-- | src/sound-state-manager.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/sound-state-manager.c b/src/sound-state-manager.c new file mode 100644 index 0000000..e8865d8 --- /dev/null +++ b/src/sound-state-manager.c @@ -0,0 +1,387 @@ +/* +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_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_warning("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 ); +} + |