From ea30986e03e54ee650a1cca610904de9f4d0f745 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 6 Mar 2013 22:40:55 -0500 Subject: Remove libsoundmenu (panel plugin) Indicator panel plugins are deprecated. --- src/Makefile.am | 38 - src/indicator-sound.c | 834 -------------------- src/indicator-sound.h | 59 -- src/metadata-widget.c | 880 --------------------- src/metadata-widget.h | 51 -- src/mute-widget.c | 134 ---- src/mute-widget.h | 63 -- src/sound-state-manager.c | 480 ------------ src/sound-state-manager.h | 67 -- src/transport-widget.c | 1886 --------------------------------------------- src/transport-widget.h | 67 -- src/voip-input-widget.c | 284 ------- src/voip-input-widget.h | 56 -- src/volume-widget.c | 338 -------- src/volume-widget.h | 58 -- 15 files changed, 5295 deletions(-) delete mode 100644 src/indicator-sound.c delete mode 100644 src/indicator-sound.h delete mode 100644 src/metadata-widget.c delete mode 100644 src/metadata-widget.h delete mode 100644 src/mute-widget.c delete mode 100644 src/mute-widget.h delete mode 100644 src/sound-state-manager.c delete mode 100644 src/sound-state-manager.h delete mode 100644 src/transport-widget.c delete mode 100644 src/transport-widget.h delete mode 100644 src/voip-input-widget.c delete mode 100644 src/voip-input-widget.h delete mode 100644 src/volume-widget.c delete mode 100644 src/volume-widget.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5a36822..a56f82a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,43 +1,5 @@ libexec_PROGRAMS = indicator-sound-service -################### -# Indicator Stuff -################### - -soundmenulibdir = $(INDICATORDIR) -soundmenulib_LTLIBRARIES = libsoundmenu.la -libsoundmenu_la_SOURCES = \ - common-defs.h \ - indicator-sound.h \ - indicator-sound.c \ - sound-state.c \ - sound-state.h \ - sound-state-manager.c \ - sound-state-manager.h \ - transport-widget.c \ - transport-widget.h \ - metadata-widget.c \ - metadata-widget.h \ - mute-widget.c \ - mute-widget.h \ - volume-widget.c \ - volume-widget.h \ - voip-input-widget.c \ - voip-input-widget.h \ - gen-sound-service.xml.h \ - gen-sound-service.xml.c \ - dbus-shared-names.h - -libsoundmenu_la_CFLAGS = \ - $(APPLET_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - -Wall -Werror -DG_LOG_DOMAIN=\"Indicator-Sound\" -libsoundmenu_la_LIBADD = $(APPLET_LIBS) -lm -libsoundmenu_la_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - -module -avoid-version - - checkxml: $(srcdir)/sound-service.xml @xmllint -valid -noout $< @echo $< checks out ok diff --git a/src/indicator-sound.c b/src/indicator-sound.c deleted file mode 100644 index d21d722..0000000 --- a/src/indicator-sound.c +++ /dev/null @@ -1,834 +0,0 @@ -/* -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 "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "indicator-sound.h" -#include "transport-widget.h" -#include "metadata-widget.h" -#include "volume-widget.h" -#include "voip-input-widget.h" -#include "dbus-shared-names.h" -#include "sound-state-manager.h" -#include "mute-widget.h" - -#include "gen-sound-service.xml.h" -#include "common-defs.h" - -typedef struct _IndicatorSoundPrivate IndicatorSoundPrivate; - -struct _IndicatorSoundPrivate -{ - GtkWidget* volume_widget; - GtkWidget* voip_widget; - MuteWidget *mute_widget; - GList* transport_widgets_list; - GDBusProxy *dbus_proxy; - SoundStateManager* state_manager; - gchar *accessible_desc; - GSettings *settings; -}; - -#define INDICATOR_SOUND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_SOUND_TYPE, IndicatorSoundPrivate)) - -#define SOUND_INDICATOR_GSETTINGS_SCHEMA_ID "com.canonical.indicator.sound" - -// GObject Boiler plate -INDICATOR_SET_VERSION -INDICATOR_SET_TYPE(INDICATOR_SOUND_TYPE) - -// GObject Boiler plate -static void indicator_sound_class_init (IndicatorSoundClass *klass); -static void indicator_sound_init (IndicatorSound *self); -static void indicator_sound_dispose (GObject *object); -static void indicator_sound_finalize (GObject *object); -G_DEFINE_TYPE (IndicatorSound, indicator_sound, INDICATOR_OBJECT_TYPE); - -//GTK+ items -static GtkLabel * get_label (IndicatorObject * io); -static GtkImage * get_icon (IndicatorObject * io); -static GtkMenu * get_menu (IndicatorObject * io); -static const gchar * get_accessible_desc (IndicatorObject * io); -static const gchar * get_name_hint (IndicatorObject * io); -static void indicator_sound_scroll (IndicatorObject * io, - IndicatorObjectEntry * entry, gint delta, - IndicatorScrollDirection direction); -static void indicator_sound_middle_click (IndicatorObject * io, - IndicatorObjectEntry * entry, - guint time, gpointer data); - -//key/moust event handlers -static gboolean key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data); -static gboolean key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data); - -//custom widget realisation methods -static gboolean new_volume_slider_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); -static gboolean new_voip_slider_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); -static gboolean new_transport_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); -static gboolean new_metadata_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); -static gboolean new_mute_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); - -// DBUS communication -static GDBusNodeInfo *node_info = NULL; -static GDBusInterfaceInfo *interface_info = NULL; -static void create_connection_to_service (GObject *source_object, - GAsyncResult *res, - gpointer user_data); -static void connection_changed (IndicatorServiceManager * sm, - gboolean connected, - gpointer userdata); - -// Visiblity -static void settings_init (IndicatorSound * self); - - -static void -indicator_sound_class_init (IndicatorSoundClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = indicator_sound_dispose; - object_class->finalize = indicator_sound_finalize; - - IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS(klass); - - g_type_class_add_private (klass, sizeof (IndicatorSoundPrivate)); - - io_class->get_label = get_label; - io_class->get_image = get_icon; - io_class->get_menu = get_menu; - io_class->get_accessible_desc = get_accessible_desc; - io_class->get_name_hint = get_name_hint; - io_class->entry_scrolled = indicator_sound_scroll; - io_class->secondary_activate = indicator_sound_middle_click; -} - -static void -indicator_sound_init (IndicatorSound *self) -{ - self->service = NULL; - self->service = indicator_service_manager_new_version(INDICATOR_SOUND_DBUS_NAME, - INDICATOR_SOUND_DBUS_VERSION); - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - priv->volume_widget = NULL; - priv->voip_widget = NULL; - priv->mute_widget = NULL; - priv->dbus_proxy = NULL; - GList* t_list = NULL; - priv->transport_widgets_list = t_list; - priv->state_manager = g_object_new (SOUND_TYPE_STATE_MANAGER, NULL); - priv->accessible_desc = NULL; - priv->settings = NULL; - - settings_init (self); - - g_signal_connect ( G_OBJECT(self->service), - INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, - G_CALLBACK(connection_changed), self ); -} - -static void -indicator_sound_dispose (GObject *object) -{ - IndicatorSound * self = INDICATOR_SOUND(object); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - - if (priv->settings != NULL) { - g_object_unref (G_OBJECT(priv->settings)); - priv->settings = NULL; - } - - if (self->service != NULL) { - g_object_unref(G_OBJECT(self->service)); - self->service = NULL; - } - g_list_free (priv->transport_widgets_list); - - G_OBJECT_CLASS (indicator_sound_parent_class)->dispose (object); -} - -static void -indicator_sound_finalize (GObject *object) -{ - IndicatorSound * self = INDICATOR_SOUND(object); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - - if (priv->accessible_desc) { - g_free (priv->accessible_desc); - priv->accessible_desc = NULL; - } - - G_OBJECT_CLASS (indicator_sound_parent_class)->finalize (object); -} - -static GtkLabel * -get_label (IndicatorObject * io) -{ - return NULL; -} - -static GtkImage * -get_icon (IndicatorObject * io) -{ - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - gtk_widget_show( GTK_WIDGET(sound_state_manager_get_current_icon (priv->state_manager)) ); - return sound_state_manager_get_current_icon (priv->state_manager); -} - -/* Indicator based function to get the menu for the whole - 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_MENU_DBUS_OBJECT_PATH); - - DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(menu); - g_object_set_data (G_OBJECT (client), "indicator", io); - dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), - DBUSMENU_VOLUME_MENUITEM_TYPE, - new_volume_slider_widget); - dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), - DBUSMENU_VOIP_INPUT_MENUITEM_TYPE, - new_voip_slider_widget); - dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), - DBUSMENU_TRANSPORT_MENUITEM_TYPE, - new_transport_widget); - dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), - DBUSMENU_METADATA_MENUITEM_TYPE, - new_metadata_widget); - dbusmenu_client_add_type_handler (DBUSMENU_CLIENT(client), - DBUSMENU_MUTE_MENUITEM_TYPE, - new_mute_widget); - // Note: Not ideal but all key handling needs to be managed here and then - // delegated to the appropriate widget. - g_signal_connect (menu, "key-press-event", G_CALLBACK(key_press_cb), io); - g_signal_connect (menu, "key-release-event", G_CALLBACK(key_release_cb), io); - - return GTK_MENU(menu); -} - -static const gchar * -get_accessible_desc (IndicatorObject * io) -{ - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(io); - return priv->accessible_desc; -} - -static const gchar *get_name_hint (IndicatorObject * io) -{ - return PACKAGE_NAME; -} - -static void -connection_changed (IndicatorServiceManager * sm, - gboolean connected, - gpointer user_data) -{ - IndicatorSound* indicator = INDICATOR_SOUND(user_data); - g_return_if_fail ( IS_INDICATOR_SOUND (indicator) ); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE (indicator); - GError *error = NULL; - - if (connected == FALSE){ - sound_state_manager_deal_with_disconnect (priv->state_manager); - return; - //TODO: Gracefully handle disconnection - // do a timeout to wait for reconnection - // for 5 seconds and then if no connection message - // is received put the state at 'sink not available' - } - // If the proxy is not null and connected is true => its a reconnect, - // we don't need to anything, gdbus takes care of the rest - bless. - // just fetch the state. - if (priv->dbus_proxy != NULL){ - g_dbus_proxy_call ( priv->dbus_proxy, - "GetSoundState", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback)sound_state_manager_get_state_cb, - priv->state_manager); - return; - } - - if ( node_info == NULL ){ - node_info = g_dbus_node_info_new_for_xml ( _sound_service, - &error ); - if (error != NULL) { - g_critical ( "Failed to get create interface info from xml: %s", - error->message ); - g_error_free(error); - return; - } - } - - if (interface_info == NULL) { - interface_info = g_dbus_node_info_lookup_interface (node_info, - INDICATOR_SOUND_DBUS_INTERFACE); - if (interface_info == NULL) { - g_critical ("Unable to find interface '" INDICATOR_SOUND_DBUS_INTERFACE "'"); - } - } - - g_dbus_proxy_new_for_bus( G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, - interface_info, - INDICATOR_SOUND_DBUS_NAME, - INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, - INDICATOR_SOUND_DBUS_INTERFACE, - NULL, - create_connection_to_service, - indicator ); -} - -static void create_connection_to_service (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - IndicatorSound *self = INDICATOR_SOUND(user_data); - GError *error = NULL; - - g_return_if_fail( IS_INDICATOR_SOUND(self) ); - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - - priv->dbus_proxy = g_dbus_proxy_new_finish(res, &error); - - if (error != NULL) { - g_critical ("Failed to get dbus proxy: %s", error->message); - g_error_free(error); - return; - } - sound_state_manager_connect_to_dbus (priv->state_manager, - priv->dbus_proxy); - -} - -static gboolean -new_transport_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_debug("indicator-sound: new_transport_bar() called "); - - GtkWidget* bar = NULL; - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - bar = transport_widget_new(newitem); - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - priv->transport_widgets_list = g_list_append ( priv->transport_widgets_list, bar ); - - GtkMenuItem *menu_transport_bar = GTK_MENU_ITEM(bar); - - gtk_widget_show_all(bar); - dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client), - newitem, - menu_transport_bar, - parent); - return TRUE; -} - -static gboolean -new_metadata_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_debug("indicator-sound: new_metadata_widget"); - - GtkWidget* metadata = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - - metadata = metadata_widget_new (newitem); - - g_debug ("%s (\"%s\")", __func__, - dbusmenu_menuitem_property_get(newitem, DBUSMENU_METADATA_MENUITEM_PLAYER_NAME)); - - GtkMenuItem *menu_metadata_widget = GTK_MENU_ITEM(metadata); - - gtk_widget_show_all(metadata); - dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client), - newitem, - menu_metadata_widget, - parent); - return TRUE; -} - -static gboolean -new_volume_slider_widget(DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_debug("indicator-sound: new_volume_slider_widget"); - - GtkWidget* volume_widget = NULL; - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - - if (priv->volume_widget != NULL){ - volume_widget_tidy_up (priv->volume_widget); - gtk_widget_destroy (priv->volume_widget); - priv->volume_widget = NULL; - } - volume_widget = volume_widget_new (newitem, io); - priv->volume_widget = volume_widget; - // Don't forget to set the accessible desc. - update_accessible_desc (io); - - - GtkWidget* ido_slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - - gtk_widget_show_all(ido_slider_widget); - // register the style callback on this widget with state manager's style change - // handler (needs to remake the blocking animation for each style). - g_signal_connect (ido_slider_widget, "style-set", - G_CALLBACK(sound_state_manager_style_changed_cb), - priv->state_manager); - - GtkMenuItem *menu_volume_item = GTK_MENU_ITEM(ido_slider_widget); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), - newitem, - menu_volume_item, - parent); - return TRUE; -} -/** - * new_voip_slider_widget - * Create the voip menu item widget, must of the time this widget will be hidden. - * @param newitem - * @param parent - * @param client - * @param user_data - * @return - */ -static gboolean -new_voip_slider_widget (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_debug("indicator-sound: new_voip_slider_widget"); - GtkWidget* voip_widget = NULL; - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - - if (priv->voip_widget != NULL){ - voip_input_widget_tidy_up (priv->voip_widget); - gtk_widget_destroy (priv->voip_widget); - priv->voip_widget = NULL; - } - - voip_widget = voip_input_widget_new (newitem); - priv->voip_widget = voip_widget; - - GtkWidget* ido_slider_widget = voip_input_widget_get_ido_slider(VOIP_INPUT_WIDGET(voip_widget)); - - gtk_widget_show_all(ido_slider_widget); - - GtkMenuItem *menu_volume_item = GTK_MENU_ITEM(ido_slider_widget); - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), - newitem, - menu_volume_item, - parent); - return TRUE; -} - -static gboolean -new_mute_widget(DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - IndicatorObject *io = NULL; - - g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); - - io = g_object_get_data (G_OBJECT (client), "indicator"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - - if (priv->mute_widget != NULL){ - g_object_unref (priv->mute_widget); - priv->mute_widget = NULL; - } - - priv->mute_widget = mute_widget_new(newitem); - GtkMenuItem *item = mute_widget_get_menu_item (priv->mute_widget); - - dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), - newitem, - item, - parent); - - return TRUE; -} - -/*******************************************************************/ -//UI callbacks -/******************************************************************/ - -static GtkWidget * -get_current_item (GtkContainer * container) -{ - GList *children = gtk_container_get_children (container); - GList *iter; - GtkWidget *rv = NULL; - - /* Suprisingly, GTK+ doesn't really let us query "what is the currently - selected item?". But it does note it internally by prelighting the - widget, so we watch for that. */ - for (iter = children; iter; iter = iter->next) { - if (gtk_widget_get_state (GTK_WIDGET (iter->data)) & GTK_STATE_PRELIGHT) { - rv = GTK_WIDGET (iter->data); - break; - } - } - - return rv; -} - -/** -key_press_cb: -**/ -static gboolean -key_press_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) -{ - gboolean digested = FALSE; - - g_return_val_if_fail(IS_INDICATOR_SOUND(data), FALSE); - - IndicatorSound *indicator = INDICATOR_SOUND (data); - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(indicator); - GtkWidget *menuitem; - menuitem = get_current_item (GTK_CONTAINER (widget)); - - if (IDO_IS_SCALE_MENU_ITEM(menuitem) == TRUE){ - gdouble current_value = 0; - gdouble new_value = 0; - const gdouble five_percent = 5; - gboolean is_voip_slider = FALSE; - - if (g_ascii_strcasecmp (ido_scale_menu_item_get_primary_label (IDO_SCALE_MENU_ITEM(menuitem)), "VOLUME") == 0) { - g_debug ("vOLUME SLIDER KEY PRESS"); - GtkWidget* slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)slider_widget); - GtkRange* range = (GtkRange*)slider; - g_return_val_if_fail(GTK_IS_RANGE(range), FALSE); - current_value = gtk_range_get_value(range); - new_value = current_value; - } - else if (g_ascii_strcasecmp (ido_scale_menu_item_get_primary_label (IDO_SCALE_MENU_ITEM(menuitem)), "VOIP") == 0) { - g_debug ("VOIP SLIDER KEY PRESS"); - GtkWidget* slider_widget = voip_input_widget_get_ido_slider(VOIP_INPUT_WIDGET(priv->voip_widget)); - GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)slider_widget); - GtkRange* range = (GtkRange*)slider; - g_return_val_if_fail(GTK_IS_RANGE(range), FALSE); - current_value = gtk_range_get_value(range); - new_value = current_value; - is_voip_slider = TRUE; - } - - switch (event->keyval) { - case GDK_KEY_Right: - digested = TRUE; - new_value = current_value + five_percent; - break; - case GDK_KEY_Left: - digested = TRUE; - new_value = current_value - five_percent; - break; - case GDK_KEY_plus: - digested = TRUE; - new_value = current_value + five_percent; - break; - case GDK_KEY_minus: - digested = TRUE; - new_value = current_value - five_percent; - break; - default: - break; - } - new_value = CLAMP(new_value, 0, 100); - if (new_value != current_value){ - if (is_voip_slider == TRUE){ - voip_input_widget_update (VOIP_INPUT_WIDGET(priv->voip_widget), new_value); - } - else{ - volume_widget_update (VOLUME_WIDGET(priv->volume_widget), new_value, "keypress-update"); - } - } - } - else if (IS_TRANSPORT_WIDGET(menuitem) == TRUE) { - TransportWidget* transport_widget = NULL; - GList* elem; - - for ( elem = priv->transport_widgets_list; elem; elem = elem->next ) { - transport_widget = TRANSPORT_WIDGET ( elem->data ); - if ( transport_widget_is_selected( transport_widget ) ) - break; - } - - switch (event->keyval) { - case GDK_KEY_Right: - transport_widget_react_to_key_press_event ( transport_widget, - TRANSPORT_ACTION_NEXT ); - digested = TRUE; - break; - case GDK_KEY_Left: - transport_widget_react_to_key_press_event ( transport_widget, - TRANSPORT_ACTION_PREVIOUS ); - digested = TRUE; - break; - case GDK_KEY_space: - transport_widget_react_to_key_press_event ( transport_widget, - TRANSPORT_ACTION_PLAY_PAUSE ); - digested = TRUE; - break; - case GDK_KEY_Up: - case GDK_KEY_Down: - digested = FALSE; - break; - default: - break; - } - } - return digested; -} - - -/** -key_release_cb: -**/ -static gboolean -key_release_cb(GtkWidget* widget, GdkEventKey* event, gpointer data) -{ - gboolean digested = FALSE; - - g_return_val_if_fail(IS_INDICATOR_SOUND(data), FALSE); - - IndicatorSound *indicator = INDICATOR_SOUND (data); - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(indicator); - - GtkWidget *menuitem; - - menuitem = get_current_item (GTK_CONTAINER (widget)); - if (IS_TRANSPORT_WIDGET(menuitem) == TRUE) { - TransportWidget* transport_widget = NULL; - GList* elem; - - for(elem = priv->transport_widgets_list; elem; elem = elem->next) { - transport_widget = TRANSPORT_WIDGET (elem->data); - if ( transport_widget_is_selected( transport_widget ) ) - break; - } - - switch (event->keyval) { - case GDK_KEY_Right: - transport_widget_react_to_key_release_event ( transport_widget, - TRANSPORT_ACTION_NEXT ); - digested = TRUE; - break; - case GDK_KEY_Left: - transport_widget_react_to_key_release_event ( transport_widget, - TRANSPORT_ACTION_PREVIOUS ); - digested = TRUE; - break; - case GDK_KEY_space: - transport_widget_react_to_key_release_event ( transport_widget, - TRANSPORT_ACTION_PLAY_PAUSE ); - digested = TRUE; - break; - case GDK_KEY_Up: - case GDK_KEY_Down: - digested = FALSE; - break; - default: - break; - } - } - return digested; -} - -static void -indicator_sound_scroll (IndicatorObject * io, IndicatorObjectEntry * entry, - gint delta, IndicatorScrollDirection direction) -{ - //g_debug("indicator-sound-scroll - current slider value"); - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(INDICATOR_SOUND (io)); - SoundState current_state = sound_state_manager_get_current_state (priv->state_manager); - - if (current_state == UNAVAILABLE || current_state == MUTED) - return; - - GtkWidget* slider_widget = volume_widget_get_ido_slider(VOLUME_WIDGET(priv->volume_widget)); - GtkWidget* slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)slider_widget); - GtkRange* range = (GtkRange*)slider; - g_return_if_fail(GTK_IS_RANGE(range)); - - gdouble value = gtk_range_get_value(range); - GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (slider)); - //g_debug("indicator-sound-scroll - current slider value %f", value); - if (direction == INDICATOR_OBJECT_SCROLL_UP) { - value += gtk_adjustment_get_step_increment (adj); - } else { - value -= gtk_adjustment_get_step_increment (adj); - } - //g_debug("indicator-sound-scroll - update slider with value %f", value); - volume_widget_update(VOLUME_WIDGET(priv->volume_widget), value, "scroll updates"); - - if (!gtk_widget_get_mapped(GTK_WIDGET (entry->menu))) - sound_state_manager_show_notification (priv->state_manager, value); -} - -static void -indicator_sound_middle_click (IndicatorObject * io, IndicatorObjectEntry * entry, - guint time, gpointer data) -{ - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(io); - g_return_if_fail (priv); - - mute_widget_toggle(priv->mute_widget); -} - -void -update_accessible_desc (IndicatorObject * io) -{ - GList *entries = indicator_object_get_entries(io); - if (!entries) - return; - IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data; - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(io); - gchar *old_desc = priv->accessible_desc; - - if (priv->volume_widget) { - priv->accessible_desc = g_strdup_printf(_("Volume (%'.0f%%)"), - volume_widget_get_current_volume (priv->volume_widget)); - } - else { - priv->accessible_desc = NULL; - } - - entry->accessible_desc = priv->accessible_desc; - g_free (old_desc); - g_signal_emit(G_OBJECT(io), - INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID, - 0, - entry, - TRUE); - g_list_free(entries); -} - -/*** -**** -***/ - -#define VISIBLE_KEY "visible" - -static void -on_visible_changed (GSettings * settings, gchar * key, gpointer user_data) -{ - g_return_if_fail (!g_strcmp0 (key, VISIBLE_KEY)); - - IndicatorObject * io = INDICATOR_OBJECT(user_data); - const gboolean visible = g_settings_get_boolean (settings, key); - indicator_object_set_visible (io, visible); - if (visible) - update_accessible_desc (io); // requires an entry -} - -static void -settings_init (IndicatorSound *self) -{ - const char * schema = SOUND_INDICATOR_GSETTINGS_SCHEMA_ID; - - gint i; - gboolean schema_exists = FALSE; - const char * const * schemas = g_settings_list_schemas (); - for (i=0; !schema_exists && schemas && schemas[i]; i++) - if (!g_strcmp0 (schema, schemas[i])) - schema_exists = TRUE; - - IndicatorSoundPrivate* priv = INDICATOR_SOUND_GET_PRIVATE(self); - if (schema_exists) { - priv->settings = g_settings_new (schema); - } else { - priv->settings = NULL; - } - - if (priv->settings != NULL) { - g_signal_connect (G_OBJECT(priv->settings), "changed::" VISIBLE_KEY, - G_CALLBACK(on_visible_changed), self); - const gboolean b = g_settings_get_boolean (priv->settings, VISIBLE_KEY); - g_object_set (G_OBJECT(self), - "indicator-object-default-visibility", b, - NULL); - } -} diff --git a/src/indicator-sound.h b/src/indicator-sound.h deleted file mode 100644 index c945efa..0000000 --- a/src/indicator-sound.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef __INCLUDE_INDICATOR_SOUND_H__ -#define __INCLUDE_INDICATOR_SOUND_H__ - -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2010 Canonical Ltd. - -Authors: - Conor Curran - Ted Gould - -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 "config.h" - -#include -#include -#include - -#define INDICATOR_SOUND_TYPE (indicator_sound_get_type ()) -#define INDICATOR_SOUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_SOUND_TYPE, IndicatorSound)) -#define INDICATOR_SOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_SOUND_TYPE, IndicatorSoundClass)) -#define IS_INDICATOR_SOUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_SOUND_TYPE)) -#define IS_INDICATOR_SOUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_SOUND_TYPE)) -#define INDICATOR_SOUND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_SOUND_TYPE, IndicatorSoundClass)) - -typedef struct _IndicatorSound IndicatorSound; -typedef struct _IndicatorSoundClass IndicatorSoundClass; - -//GObject class struct -struct _IndicatorSoundClass { - IndicatorObjectClass parent_class; -}; - -//GObject instance struct -struct _IndicatorSound { - IndicatorObject parent; - IndicatorServiceManager *service; -}; - -// GObject Boiler plate -GType indicator_sound_get_type (void); - -// Update the accessible description -void update_accessible_desc (IndicatorObject * io); - -#endif diff --git a/src/metadata-widget.c b/src/metadata-widget.c deleted file mode 100644 index 812f340..0000000 --- a/src/metadata-widget.c +++ /dev/null @@ -1,880 +0,0 @@ -/* -Copyright 2010 Canonical Ltd. - -Authors: - Conor Curran - Mirco Müller - -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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "metadata-widget.h" -#include "common-defs.h" -#include -#include -#include "transport-widget.h" -#include - -typedef struct _MetadataWidgetPrivate MetadataWidgetPrivate; - -struct _MetadataWidgetPrivate -{ - gboolean theme_change_occured; - GtkWidget* meta_data_h_box; - GtkWidget* meta_data_v_box; - GtkWidget* album_art; - GString* image_path; - GString* old_image_path; - GtkWidget* artist_label; - GtkWidget* piece_label; - GtkWidget* container_label; - GtkWidget* player_label; - GtkWidget* player_icon; - DbusmenuMenuitem* twin_item; - gint current_height; -}; - -#define METADATA_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), METADATA_WIDGET_TYPE, MetadataWidgetPrivate)) - -/* Prototypes */ -static void metadata_widget_class_init (MetadataWidgetClass *klass); -static void metadata_widget_init (MetadataWidget *self); -static void metadata_widget_dispose (GObject *object); -static void metadata_widget_finalize (GObject *object); -static void metadata_widget_set_style (GtkWidget* button, GtkStyle* style); -static void metadata_widget_set_twin_item (MetadataWidget* self, - DbusmenuMenuitem* twin_item); -// keyevent consumers -static gboolean metadata_widget_button_release_event (GtkWidget *menuitem, - GdkEventButton *event); -// Dbusmenuitem properties update callback -static void metadata_widget_property_update (DbusmenuMenuitem* item, - gchar* property, - GVariant* value, - gpointer userdata); -static void metadata_widget_style_labels ( MetadataWidget* self, - GtkLabel* label); -static void draw_album_art_placeholder (GtkWidget *metadata); - -static void draw_album_border (GtkWidget *metadata, gboolean selected); -static void metadata_widget_selection_received_event_callback( GtkWidget *widget, - GtkSelectionData *data, - guint time, - gpointer user_data); - - - -#if GTK_CHECK_VERSION(3, 0, 0) -static void metadata_widget_get_preferred_width (GtkWidget* self, - gint* minimum_width, - gint* natural_width); -static gboolean metadata_widget_icon_triangle_draw_cb_gtk_3 (GtkWidget *image, - cairo_t* cr, - gpointer user_data); -static gboolean metadata_image_expose_gtk_3 (GtkWidget *image, - cairo_t* cr, - gpointer user_data); -#else -static gboolean metadata_widget_icon_triangle_draw_cb (GtkWidget *image, - GdkEventExpose *event, - gpointer user_data); -static gboolean metadata_image_expose (GtkWidget *image, - GdkEventExpose *event, - gpointer user_data); -#endif - -static void metadata_widget_set_icon (MetadataWidget *self); -static void metadata_widget_handle_resizing (MetadataWidget* self); - - -G_DEFINE_TYPE (MetadataWidget, metadata_widget, GTK_TYPE_MENU_ITEM); - -static void -metadata_widget_class_init (MetadataWidgetClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - widget_class->button_release_event = metadata_widget_button_release_event; - #if GTK_CHECK_VERSION(3, 0, 0) - widget_class->get_preferred_width = metadata_widget_get_preferred_width; - #endif - g_type_class_add_private (klass, sizeof (MetadataWidgetPrivate)); - - gobject_class->dispose = metadata_widget_dispose; - gobject_class->finalize = metadata_widget_finalize; -} - -static void -metadata_widget_init (MetadataWidget *self) -{ - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(self); - GtkWidget *hbox; - GtkWidget *outer_v_box; - - #if GTK_CHECK_VERSION(3, 0, 0) - outer_v_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - #else - outer_v_box = gtk_vbox_new (FALSE, 0); - #endif - - #if GTK_CHECK_VERSION(3, 0, 0) - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - #else - hbox = gtk_hbox_new(FALSE, 0); - #endif - - priv->meta_data_h_box = hbox; - priv->current_height = 1; - - // image - priv->album_art = gtk_image_new(); - priv->image_path = g_string_new(""); - priv->old_image_path = g_string_new(""); - - #if GTK_CHECK_VERSION(3, 0, 0) - g_signal_connect(priv->album_art, "draw", - G_CALLBACK(metadata_image_expose_gtk_3), - GTK_WIDGET(self)); - - g_signal_connect_after (GTK_WIDGET(self), "draw", - G_CALLBACK(metadata_widget_icon_triangle_draw_cb_gtk_3), - GTK_WIDGET(self)); - #else - g_signal_connect(priv->album_art, "expose-event", - G_CALLBACK(metadata_image_expose), - GTK_WIDGET(self)); - - g_signal_connect_after (GTK_WIDGET(self), "expose-event", - G_CALLBACK(metadata_widget_icon_triangle_draw_cb), - GTK_WIDGET(self)); - #endif - gtk_box_pack_start (GTK_BOX (priv->meta_data_h_box), - priv->album_art, - FALSE, - FALSE, - 1); - priv->theme_change_occured = FALSE; - - #if GTK_CHECK_VERSION(3, 0, 0) - GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - #else - GtkWidget* vbox = gtk_vbox_new(FALSE, 0); - #endif - - // artist - GtkWidget* artist; - artist = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(artist), (gfloat)0, (gfloat)0); - gtk_misc_set_padding (GTK_MISC(artist), (gfloat)10, (gfloat)0); - gtk_widget_set_size_request (artist, 140, 15); - - gtk_label_set_ellipsize(GTK_LABEL(artist), PANGO_ELLIPSIZE_MIDDLE); - metadata_widget_style_labels(self, GTK_LABEL(artist)); - priv->artist_label = artist; - - // title - GtkWidget* piece; - piece = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(piece), (gfloat)0, (gfloat)0); - gtk_misc_set_padding (GTK_MISC(piece), (gfloat)10, (gfloat)-5); - - gtk_widget_set_size_request (piece, 140, 15); - gtk_label_set_ellipsize(GTK_LABEL(piece), PANGO_ELLIPSIZE_MIDDLE); - metadata_widget_style_labels(self, GTK_LABEL(piece)); - priv->piece_label = piece; - - // container - GtkWidget* container; - container = gtk_label_new(""); - gtk_misc_set_alignment(GTK_MISC(container), (gfloat)0, (gfloat)0); - gtk_misc_set_padding (GTK_MISC(container), (gfloat)10, (gfloat)0); - gtk_widget_set_size_request (container, 140, 15); - - gtk_label_set_ellipsize(GTK_LABEL(container), PANGO_ELLIPSIZE_MIDDLE); - metadata_widget_style_labels(self, GTK_LABEL(container)); - priv->container_label = container; - - gtk_box_pack_start (GTK_BOX (vbox), priv->piece_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox), priv->artist_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox), priv->container_label, FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (priv->meta_data_h_box), vbox, FALSE, FALSE, 0); - - g_signal_connect(self, "style-set", - G_CALLBACK(metadata_widget_set_style), GTK_WIDGET(self)); - g_signal_connect (self, "selection-received", - G_CALLBACK(metadata_widget_selection_received_event_callback), - GTK_WIDGET(self)); - - gint padding = 4; - gtk_widget_style_get(GTK_WIDGET(self), "toggle-spacing", &padding, NULL); - -#if GTK_CHECK_VERSION(3, 0, 0) - GtkWidget * tophbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding); -#else - GtkWidget * tophbox = gtk_hbox_new(FALSE, padding); -#endif - - GtkWidget *player_icon; - player_icon = gtk_image_new(); - priv->player_icon = player_icon; - - gtk_misc_set_alignment (GTK_MISC(priv->player_icon), 1.0 /* right aligned */, 0.5); - gtk_box_pack_start (GTK_BOX (tophbox), priv->player_icon, FALSE, FALSE, 0); - GtkWidget* spacer; - spacer = gtk_alignment_new (0,0,0,0); - gtk_container_add (GTK_CONTAINER (spacer), priv->meta_data_h_box); - gtk_alignment_set_padding (GTK_ALIGNMENT (spacer),5,0,0,0); - - // player label - GtkWidget* player_label; - player_label = gtk_label_new (""); - gtk_misc_set_alignment(GTK_MISC(player_label), (gfloat)0, 0.5); - priv->player_label = player_label; - gtk_box_pack_start (GTK_BOX (tophbox), priv->player_label, TRUE, TRUE, 0); - - gtk_box_pack_start (GTK_BOX(outer_v_box), tophbox, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX(outer_v_box), spacer, FALSE, FALSE, 0); - - gtk_container_add (GTK_CONTAINER (self), outer_v_box); - - gtk_widget_show_all (priv->meta_data_h_box); - gtk_widget_set_no_show_all (priv->meta_data_h_box, TRUE); - - gtk_widget_hide (priv->meta_data_h_box); -} - -static void -metadata_widget_dispose (GObject *object) -{ - G_OBJECT_CLASS (metadata_widget_parent_class)->dispose (object); -} - -static void -metadata_widget_finalize (GObject *object) -{ - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(METADATA_WIDGET(object)); - g_string_free (priv->image_path, TRUE); - g_string_free (priv->old_image_path, TRUE); - - G_OBJECT_CLASS (metadata_widget_parent_class)->finalize (object); -} - -/** -* Make sure to only clear the album art only when it is not empty -* Otherwise it will continuously call queue_draw after each empty call. -*/ -static void -clear_album_art (GtkImage* album_art) -{ - if (gtk_image_get_storage_type(album_art) != GTK_IMAGE_EMPTY){ - gtk_image_clear (album_art); - } -} - - -#if GTK_CHECK_VERSION(3, 0, 0) -static void -metadata_widget_get_preferred_width (GtkWidget* self, - gint* minimum_width, - gint* natural_width) -{ - *minimum_width = *natural_width = 200; -} - -/** - * We override the expose method to enable primitive drawing of the - * empty album art image. - */ -static gboolean -metadata_image_expose_gtk_3 (GtkWidget *metadata, - cairo_t* cr, - gpointer user_data) -{ - g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); - MetadataWidget* widget = METADATA_WIDGET(user_data); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widget); - - if ( TRUE == dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM(priv->twin_item), - DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS)) - { - return FALSE; - } - - if((guint)priv->image_path->len != 0){ - - if(g_string_equal (priv->image_path, priv->old_image_path) == FALSE || - priv->theme_change_occured == TRUE){ - priv->theme_change_occured = FALSE; - GdkPixbuf* pixbuf; - pixbuf = gdk_pixbuf_new_from_file_at_size(priv->image_path->str, 60, 60, NULL); - - if(GDK_IS_PIXBUF(pixbuf) == FALSE){ - clear_album_art (GTK_IMAGE(priv->album_art)); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); - draw_album_border (metadata, FALSE); - draw_album_art_placeholder(metadata); - return FALSE; - } - - gtk_image_set_from_pixbuf(GTK_IMAGE(priv->album_art), pixbuf); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), - gdk_pixbuf_get_width(pixbuf), - gdk_pixbuf_get_height(pixbuf)); - - draw_album_border (metadata, FALSE); - g_string_erase (priv->old_image_path, 0, -1); - g_string_overwrite (priv->old_image_path, 0, priv->image_path->str); - g_object_unref(pixbuf); - } - return FALSE; - } - clear_album_art (GTK_IMAGE(priv->album_art)); - g_string_erase (priv->old_image_path, 0, -1); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); - draw_album_border (metadata, FALSE); - draw_album_art_placeholder(metadata); - return FALSE; -} - -// Draw the triangle if the player is running ... -static gboolean -metadata_widget_icon_triangle_draw_cb_gtk_3 (GtkWidget *widget, - cairo_t* cr, - gpointer user_data) -{ - g_return_val_if_fail (IS_METADATA_WIDGET (user_data), FALSE); - MetadataWidget* meta = METADATA_WIDGET (user_data); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(meta); - - gboolean running = dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING); - - if (!running) - return FALSE; - - GtkStyle *style; - int x, y, arrow_width, arrow_height; - - arrow_width = 5; - arrow_height = 9; - - style = gtk_widget_get_style (widget); - - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - x = allocation.x; - y = 0; - - // Draw triangle but only if the player is running. - y += gtk_image_get_pixel_size (GTK_IMAGE (priv->player_icon)) / 3 + 5; - cairo_set_line_width (cr, 1.0); - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x, y + arrow_height); - cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0); - cairo_close_path (cr); - cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, - style->fg[gtk_widget_get_state(widget)].green/65535.0, - style->fg[gtk_widget_get_state(widget)].blue/65535.0); - cairo_fill (cr); - - return FALSE; -} - -// GTK 2 Expose handler -#else - -static gboolean -metadata_image_expose (GtkWidget *metadata, - GdkEventExpose *event, - gpointer user_data) -{ - g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); - MetadataWidget* widget = METADATA_WIDGET(user_data); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widget); - - if ( TRUE == dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM(priv->twin_item), - DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS)) - { - return FALSE; - } - - draw_album_border(metadata, FALSE); - - if(priv->image_path->len > 0){ - if(g_string_equal(priv->image_path, priv->old_image_path) == FALSE || - priv->theme_change_occured == TRUE){ - priv->theme_change_occured = FALSE; - GdkPixbuf* pixbuf; - pixbuf = gdk_pixbuf_new_from_file_at_size(priv->image_path->str, 60, 60, NULL); - - if(GDK_IS_PIXBUF(pixbuf) == FALSE){ - clear_album_art (GTK_IMAGE(priv->album_art)); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); - draw_album_art_placeholder(metadata); - return FALSE; - } - - gtk_image_set_from_pixbuf(GTK_IMAGE(priv->album_art), pixbuf); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), - gdk_pixbuf_get_width(pixbuf), - gdk_pixbuf_get_height(pixbuf)); - - g_string_erase (priv->old_image_path, 0, -1); - g_string_overwrite (priv->old_image_path, 0, priv->image_path->str); - g_object_unref(pixbuf); - } - return FALSE; - } - clear_album_art (GTK_IMAGE(priv->album_art)); - g_string_erase (priv->old_image_path, 0, -1); - gtk_widget_set_size_request(GTK_WIDGET(priv->album_art), 60, 60); - draw_album_art_placeholder(metadata); - return FALSE; -} - - -// Draw the triangle if the player is running ... -static gboolean -metadata_widget_icon_triangle_draw_cb (GtkWidget *widget, - GdkEventExpose *event, - gpointer user_data) -{ - g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE); - - MetadataWidget* meta = METADATA_WIDGET(user_data); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(meta); - - gboolean running = dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING); - - if (!running) - return FALSE; - - GtkStyle *style; - cairo_t *cr; - int x, y, arrow_width, arrow_height; - - arrow_width = 5; - arrow_height = 9; - - style = gtk_widget_get_style (widget); - - cr = (cairo_t*) gdk_cairo_create (gtk_widget_get_window (widget)); - - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - x = allocation.x; - y = allocation.y; - - // Draw triangle but only if the player is running. - y += allocation.height/2.0 - (double)arrow_height/2.0; - cairo_set_line_width (cr, 1.0); - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x, y + arrow_height); - cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0); - cairo_close_path (cr); - cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, - style->fg[gtk_widget_get_state(widget)].green/65535.0, - style->fg[gtk_widget_get_state(widget)].blue/65535.0); - cairo_fill (cr); - - cairo_destroy (cr); - return FALSE; -} -#endif - -static void -draw_album_border(GtkWidget *metadata, - gboolean selected) -{ - cairo_t *cr; - cr = gdk_cairo_create (gtk_widget_get_window (metadata)); - #if GTK_CHECK_VERSION(3, 0, 0) - gtk_style_context_add_class (gtk_widget_get_style_context (metadata), - "menu"); - #endif - - GtkStyle *style; - style = gtk_widget_get_style (metadata); - - GtkAllocation alloc; - gtk_widget_get_allocation (metadata, &alloc); - gint offset = 1; - - alloc.width = alloc.width + (offset * 2); - alloc.height = alloc.height + (offset * 2) - 7; - alloc.x = alloc.x - offset; - alloc.y = alloc.y - offset + 3; - - CairoColorRGB bg_normal, fg_normal; - - bg_normal.r = style->bg[0].red/65535.0; - bg_normal.g = style->bg[0].green/65535.0; - bg_normal.b = style->bg[0].blue/65535.0; - - const gint state = selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL; - - fg_normal.r = style->fg[state].red/65535.0; - fg_normal.g = style->fg[state].green/65535.0; - fg_normal.b = style->fg[state].blue/65535.0; - - CairoColorRGB dark_top_color; - CairoColorRGB light_bottom_color; - CairoColorRGB background_color; - - _color_shade ( &bg_normal, 0.93, &background_color ); - _color_shade ( &bg_normal, 0.23, &dark_top_color ); - _color_shade ( &fg_normal, 0.55, &light_bottom_color ); - - cairo_rectangle (cr, - alloc.x, alloc.y, - alloc.width, alloc.height); - - cairo_set_line_width (cr, 1.0); - - cairo_clip ( cr ); - - cairo_move_to (cr, alloc.x, alloc.y ); - cairo_line_to (cr, alloc.x + alloc.width, - alloc.y ); - cairo_line_to ( cr, alloc.x + alloc.width, - alloc.y + alloc.height ); - cairo_line_to ( cr, alloc.x, alloc.y + alloc.height ); - cairo_line_to ( cr, alloc.x, alloc.y); - cairo_close_path (cr); - - cairo_set_source_rgba ( cr, - background_color.r, - background_color.g, - background_color.b, - 1.0 ); - - cairo_fill ( cr ); - - cairo_move_to (cr, alloc.x, alloc.y ); - cairo_line_to (cr, alloc.x + alloc.width, - alloc.y ); - - cairo_close_path (cr); - cairo_set_source_rgba ( cr, - dark_top_color.r, - dark_top_color.g, - dark_top_color.b, - 1.0 ); - - cairo_stroke ( cr ); - - cairo_move_to ( cr, alloc.x + alloc.width, - alloc.y + alloc.height ); - cairo_line_to ( cr, alloc.x, alloc.y + alloc.height ); - - cairo_close_path (cr); - cairo_set_source_rgba ( cr, - light_bottom_color.r, - light_bottom_color.g, - light_bottom_color.b, - 1.0); - - cairo_stroke ( cr ); - cairo_destroy (cr); -} - -static void -draw_album_art_placeholder(GtkWidget *metadata) -{ - cairo_t *cr; - cr = gdk_cairo_create (gtk_widget_get_window (metadata)); - GtkStyle *style; - style = gtk_widget_get_style (metadata); - - GtkAllocation alloc; - gtk_widget_get_allocation (metadata, &alloc); - - PangoLayout *layout; - PangoFontDescription *desc; - layout = pango_cairo_create_layout(cr); - PangoContext* pcontext = pango_cairo_create_context(cr); - pango_cairo_context_set_resolution (pcontext, 96); - - GString* string = g_string_new(""); - gssize size = -1; - gunichar code = g_utf8_get_char_validated("\342\231\253", size); - g_string_append_unichar (string, code); - - pango_layout_set_text(layout, string->str, -1); - desc = pango_font_description_from_string("Sans Bold 30"); - pango_layout_set_font_description(layout, desc); - pango_font_description_free(desc); - - CairoColorRGB fg_normal, light_bottom_color; - - fg_normal.r = style->fg[0].red/65535.0; - fg_normal.g = style->fg[0].green/65535.0; - fg_normal.b = style->fg[0].blue/65535.0; - - _color_shade ( &fg_normal, 0.78, &light_bottom_color ); - - cairo_set_source_rgba (cr, - light_bottom_color.r, - light_bottom_color.g, - light_bottom_color.b, - 1.0); - - pango_cairo_update_layout(cr, layout); - cairo_move_to (cr, alloc.x + alloc.width/6, alloc.y + 3); - pango_cairo_show_layout(cr, layout); - - g_object_unref(layout); - g_object_unref(pcontext); - g_string_free (string, TRUE); - cairo_destroy (cr); -} - -static void -metadata_widget_selection_received_event_callback ( GtkWidget *widget, - GtkSelectionData *data, - guint time, - gpointer user_data ) - -{ - draw_album_border(widget, TRUE); -} - -/* Suppress/consume keyevents */ -static gboolean -metadata_widget_button_release_event (GtkWidget *menuitem, - GdkEventButton *event) -{ - g_return_val_if_fail (IS_METADATA_WIDGET (menuitem), FALSE); - MetadataWidgetPrivate* priv = METADATA_WIDGET_GET_PRIVATE(METADATA_WIDGET(menuitem)); - // For the left raise/launch the player - if (event->button == 1){ - GVariant* new_title_event = g_variant_new_boolean(TRUE); - dbusmenu_menuitem_handle_event (priv->twin_item, - "Title menu event", - new_title_event, - 0); - } - // For the right copy track details to clipboard only if the player is running - // and there is something there - else if (event->button == 3){ - gboolean running = dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_RUNNING); - gboolean hidden = dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS); - g_return_val_if_fail ( running, FALSE ); - - g_return_val_if_fail ( !hidden, FALSE ); - - GtkClipboard* board = gtk_clipboard_get (GDK_NONE); - gchar* contents = g_strdup_printf("artist: %s \ntitle: %s \nalbum: %s", - dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTIST), - dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_TITLE), - dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ALBUM)); - gtk_clipboard_set_text (board, contents, -1); - gtk_clipboard_store (board); - g_free(contents); - } - return FALSE; -} - -static void -metadata_widget_property_update(DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata) -{ - g_return_if_fail (IS_METADATA_WIDGET (userdata)); - - if(g_variant_is_of_type(value, G_VARIANT_TYPE_INT32) == TRUE && - g_variant_get_int32(value) == DBUSMENU_PROPERTY_EMPTY){ - GVariant* new_value = g_variant_new_string (""); - value = new_value; - } - - MetadataWidget* mitem = METADATA_WIDGET(userdata); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(mitem); - - if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_ARTIST, property) == 0){ - gtk_label_set_text(GTK_LABEL(priv->artist_label), g_variant_get_string(value, NULL)); - metadata_widget_style_labels(mitem, GTK_LABEL(priv->artist_label)); - } - else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_TITLE, property) == 0){ - gtk_label_set_text(GTK_LABEL(priv->piece_label), g_variant_get_string(value, NULL)); - metadata_widget_style_labels(mitem, GTK_LABEL(priv->piece_label)); - } - else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_ALBUM, property) == 0){ - gtk_label_set_text(GTK_LABEL(priv->container_label), g_variant_get_string(value, NULL)); - metadata_widget_style_labels(mitem, GTK_LABEL(priv->container_label)); - } - else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_ARTURL, property) == 0){ - g_string_erase(priv->image_path, 0, -1); - g_string_overwrite (priv->image_path, 0, g_variant_get_string (value, NULL)); - gtk_widget_queue_draw(GTK_WIDGET(mitem)); - } - else if (g_ascii_strcasecmp (DBUSMENU_METADATA_MENUITEM_PLAYER_NAME, property) == 0){ - gtk_label_set_label (GTK_LABEL (priv->player_label), - g_variant_get_string(value, NULL)); - } - else if (g_ascii_strcasecmp (DBUSMENU_METADATA_MENUITEM_PLAYER_ICON, property) == 0){ - metadata_widget_set_icon (mitem); - } - else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS, property) == 0){ - metadata_widget_handle_resizing (mitem); - } -} - -static void -metadata_widget_handle_resizing (MetadataWidget* self) -{ - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(self); - - if (dbusmenu_menuitem_property_get_bool (priv->twin_item, - DBUSMENU_METADATA_MENUITEM_HIDE_TRACK_DETAILS) == TRUE){ - gtk_widget_hide (priv->meta_data_h_box); - } - else{ - gtk_widget_show (priv->meta_data_h_box); - } - gtk_widget_queue_draw(GTK_WIDGET(self)); -} - -static void -metadata_widget_style_labels(MetadataWidget* self, GtkLabel* label) -{ - char* markup; - markup = g_markup_printf_escaped ("%s", - gtk_label_get_text(GTK_LABEL(label))); - gtk_label_set_markup (GTK_LABEL (label), markup); - g_free(markup); -} - -static void -metadata_widget_set_style(GtkWidget* metadata, GtkStyle* style) -{ - g_return_if_fail(IS_METADATA_WIDGET(metadata)); - MetadataWidget* widg = METADATA_WIDGET(metadata); - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widg); - priv->theme_change_occured = TRUE; - gtk_widget_queue_draw (GTK_WIDGET(metadata)); -} - -static void -metadata_widget_set_icon (MetadataWidget *self) -{ - MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(self); - - GString* banshee_string = g_string_new ( "banshee" ); - gchar * tmp = g_utf8_strdown (dbusmenu_menuitem_property_get(priv->twin_item, DBUSMENU_METADATA_MENUITEM_PLAYER_NAME), -1); - GString* app_panel = g_string_new (tmp); - g_free (tmp); - - // Banshee Special case! - // Not ideal but apparently we want the banshee icon to be the greyscale one - // and any others to be the icon from the desktop file => colour. - if ( g_string_equal ( banshee_string, app_panel ) == TRUE && - gtk_icon_theme_has_icon ( gtk_icon_theme_get_default(), app_panel->str ) ){ - g_string_append ( app_panel, "-panel" ); - } - else{ - // Otherwise use what is stored in the props - g_string_erase (app_panel, 0, -1); - g_string_overwrite (app_panel, - 0, - dbusmenu_menuitem_property_get ( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_ICON )); - } - - const GtkIconSize icon_size = GTK_ICON_SIZE_MENU; - if (g_path_is_absolute(app_panel->str) && g_file_test (app_panel->str, G_FILE_TEST_IS_REGULAR)){ - gint width, height; - gtk_icon_size_lookup (icon_size, &width, &height); - GdkPixbuf *pix = gdk_pixbuf_new_from_file_at_scale(app_panel->str, width, height, TRUE, NULL); - gtk_image_set_from_pixbuf (GTK_IMAGE (priv->player_icon), pix); - g_object_unref (pix); - } - else{ - gtk_image_set_from_icon_name(GTK_IMAGE (priv->player_icon), app_panel->str, icon_size); - } - - g_string_free ( app_panel, TRUE); - g_string_free ( banshee_string, TRUE); -} - -static void -metadata_widget_set_twin_item (MetadataWidget* self, - DbusmenuMenuitem* twin_item) -{ - MetadataWidgetPrivate* priv = METADATA_WIDGET_GET_PRIVATE(self); - priv->twin_item = twin_item; - g_signal_connect( G_OBJECT(priv->twin_item), "property-changed", - G_CALLBACK(metadata_widget_property_update), self); - gtk_label_set_text( GTK_LABEL(priv->container_label), - dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ALBUM)); - metadata_widget_style_labels( self, GTK_LABEL(priv->container_label)); - - gtk_label_set_text( GTK_LABEL(priv->piece_label), - dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_TITLE)); - metadata_widget_style_labels( self, GTK_LABEL(priv->piece_label)); - gtk_label_set_text( GTK_LABEL(priv->artist_label), - dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTIST)); - metadata_widget_style_labels( self, GTK_LABEL(priv->artist_label)); - - g_string_erase(priv->image_path, 0, -1); - const gchar *arturl = dbusmenu_menuitem_property_get( priv->twin_item, - DBUSMENU_METADATA_MENUITEM_ARTURL ); - - gtk_label_set_label (GTK_LABEL(priv->player_label), - dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_METADATA_MENUITEM_PLAYER_NAME)); - - metadata_widget_set_icon(self); - - if (arturl != NULL){ - g_string_overwrite( priv->image_path, - 0, - arturl); - // if its a remote image queue a redraw incase the download took too long - if (g_str_has_prefix (arturl, g_get_user_cache_dir())){ - gtk_widget_queue_draw(GTK_WIDGET(self)); - } - } - metadata_widget_handle_resizing (self); -} - - /** - * transport_new: - * @returns: a new #MetadataWidget. - **/ -GtkWidget* -metadata_widget_new(DbusmenuMenuitem *item) -{ - GtkWidget* widget = g_object_new(METADATA_WIDGET_TYPE, NULL); - metadata_widget_set_twin_item ( METADATA_WIDGET(widget), - item ); - return widget; -} - diff --git a/src/metadata-widget.h b/src/metadata-widget.h deleted file mode 100644 index fc6944e..0000000 --- a/src/metadata-widget.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -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 . -*/ -#ifndef __METADATA_WIDGET_H__ -#define __METADATA_WIDGET_H__ - -#include -#include - -G_BEGIN_DECLS - -#define METADATA_WIDGET_TYPE (metadata_widget_get_type ()) -#define METADATA_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), METADATA_WIDGET_TYPE, MetadataWidget)) -#define METADATA_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), METADATA_WIDGET_TYPE, MetadataWidgetClass)) -#define IS_METADATA_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), METADATA_WIDGET_TYPE)) -#define IS_METADATA_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), METADATA_WIDGET_TYPE)) -#define METADATA_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), METADATA_WIDGET_TYPE, MetadataWidgetClass)) - -typedef struct _MetadataWidget MetadataWidget; -typedef struct _MetadataWidgetClass MetadataWidgetClass; - -struct _MetadataWidgetClass { - GtkMenuItemClass parent_class; -}; - -struct _MetadataWidget { - GtkMenuItem parent; -}; - -GType metadata_widget_get_type (void); -GtkWidget* metadata_widget_new(DbusmenuMenuitem *twin_item); - -G_END_DECLS - -#endif - diff --git a/src/mute-widget.c b/src/mute-widget.c deleted file mode 100644 index 500f575..0000000 --- a/src/mute-widget.c +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Marco Trevisan (Treviño) - -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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "mute-widget.h" -#include "common-defs.h" -#include "indicator-sound.h" - -typedef struct _MuteWidgetPrivate MuteWidgetPrivate; - -struct _MuteWidgetPrivate -{ - DbusmenuMenuitem *item; - GtkMenuItem *gitem; -}; - -#define MUTE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUTE_WIDGET_TYPE, MuteWidgetPrivate)) - -/* Prototypes */ -static void mute_widget_class_init (MuteWidgetClass *klass); -static void mute_widget_init (MuteWidget *self); -static void mute_widget_dispose (GObject *object); -static void mute_widget_finalize (GObject *object); - -G_DEFINE_TYPE (MuteWidget, mute_widget, G_TYPE_OBJECT); - -static void -mute_widget_class_init (MuteWidgetClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - gobject_class->dispose = mute_widget_dispose; - gobject_class->finalize = mute_widget_finalize; - g_type_class_add_private (klass, sizeof (MuteWidgetPrivate)); -} - -static void -mute_widget_init (MuteWidget *self) -{ - MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); - priv->item = NULL; - priv->gitem = GTK_MENU_ITEM(gtk_menu_item_new ()); -} - -static void -mute_widget_dispose (GObject *object) -{ - G_OBJECT_CLASS (mute_widget_parent_class)->dispose (object); -} - -static void -mute_widget_finalize (GObject *object) -{ - MuteWidget *self = MUTE_WIDGET (object); - MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); - - g_object_unref (priv->item); - g_object_unref (G_OBJECT (priv->gitem)); - G_OBJECT_CLASS (mute_widget_parent_class)->finalize (object); -} - -GtkMenuItem * -mute_widget_get_menu_item(MuteWidget *self) -{ - MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); - return priv->gitem; -} - -MuteStatus -mute_widget_get_status (MuteWidget *self) -{ - g_return_val_if_fail(self, MUTE_STATUS_UNAVAILABLE); - MuteStatus status = MUTE_STATUS_UNAVAILABLE; - MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); - - GVariant *vstatus = dbusmenu_menuitem_property_get_variant(priv->item, - DBUSMENU_MUTE_MENUITEM_VALUE); - - if (g_variant_is_of_type (vstatus, G_VARIANT_TYPE_BOOLEAN)) - { - if (g_variant_get_boolean (vstatus)) - status = MUTE_STATUS_MUTED; - else - status = MUTE_STATUS_UNMUTED; - } - - return status; -} - -void mute_widget_toggle (MuteWidget *self) -{ - g_return_if_fail (self); - MuteWidgetPrivate *priv = MUTE_WIDGET_GET_PRIVATE(self); - gtk_menu_item_activate (priv->gitem); -} - -/** - * mute_widget_new: - * @returns: a new #MuteWidget. - **/ -MuteWidget * -mute_widget_new (DbusmenuMenuitem *item) -{ - MuteWidget* widget = g_object_new(MUTE_WIDGET_TYPE, NULL); - MuteWidgetPrivate* priv = MUTE_WIDGET_GET_PRIVATE(widget); - priv->item = g_object_ref(item); - - GVariant *label = dbusmenu_menuitem_property_get_variant(priv->item, - DBUSMENU_MENUITEM_PROP_LABEL); - - if (g_variant_is_of_type(label, G_VARIANT_TYPE_STRING)) - gtk_menu_item_set_label(priv->gitem, g_variant_get_string(label, NULL)); - - return widget; -} diff --git a/src/mute-widget.h b/src/mute-widget.h deleted file mode 100644 index 88ddd41..0000000 --- a/src/mute-widget.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Marco Trevisan (Treviño) - -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 . -*/ -#ifndef __MUTE_WIDGET_H__ -#define __MUTE_WIDGET_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define MUTE_WIDGET_TYPE (mute_widget_get_type ()) -#define MUTE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTE_WIDGET_TYPE, MuteWidget)) -#define MUTE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTE_WIDGET_TYPE, MuteWidgetClass)) -#define IS_MUTE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTE_WIDGET_TYPE)) -#define IS_MUTE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTE_WIDGET_TYPE)) -#define MUTE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTE_WIDGET_TYPE, MuteWidgetClass)) - -typedef struct _MuteWidget MuteWidget; -typedef struct _MuteWidgetClass MuteWidgetClass; - -struct _MuteWidgetClass { - GObjectClass parent_class; -}; - -struct _MuteWidget { - GObject parent; -}; - -typedef enum { - MUTE_STATUS_UNAVAILABLE, - MUTE_STATUS_MUTED, - MUTE_STATUS_UNMUTED -} MuteStatus; - -GType mute_widget_get_type (void) G_GNUC_CONST; -MuteWidget* mute_widget_new (DbusmenuMenuitem *item); -MuteStatus mute_widget_get_status (MuteWidget *self); -void mute_widget_toggle (MuteWidget *self); -GtkMenuItem *mute_widget_get_menu_item (MuteWidget *self); - -G_END_DECLS - -#endif - diff --git a/src/sound-state-manager.c b/src/sound-state-manager.c deleted file mode 100644 index c6b14ca..0000000 --- a/src/sound-state-manager.c +++ /dev/null @@ -1,480 +0,0 @@ -/* -Copyright 2011 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 "config.h" - -#include "sound-state-manager.h" -#include "dbus-shared-names.h" -#include "sound-state.h" - -typedef struct _SoundStateManagerPrivate SoundStateManagerPrivate; - -struct _SoundStateManagerPrivate -{ - GDBusProxy* dbus_proxy; - GHashTable* volume_states; - GList* blocked_animation_list; - SoundState current_state; - GtkImage* speaker_image; - NotifyNotification* notification; - GSettings *settings_manager; -}; - -#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; - -//Notifications -static void sound_state_manager_notification_init (SoundStateManager* self); - -//Animation/State related -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; - priv->notification = NULL; - priv->settings_manager = NULL; - - priv->settings_manager = g_settings_new("com.canonical.indicator.sound"); - - 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); - - if (priv->notification) { - notify_uninit(); - } - - g_object_unref(priv->settings_manager); - - 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); -} - -static void -sound_state_manager_notification_init (SoundStateManager* self) -{ - static gboolean initialized = FALSE; - - /* one-time lazy initialization */ - if (initialized) - return; - initialized = TRUE; - - SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); - - if (!notify_init(PACKAGE_NAME)) - return; - - GList* caps = notify_get_server_caps(); - gboolean has_notify_osd = FALSE; - - if (caps) { - if (g_list_find_custom(caps, "x-canonical-private-synchronous", - (GCompareFunc) g_strcmp0)) { - has_notify_osd = TRUE; - } - g_list_foreach(caps, (GFunc) g_free, NULL); - g_list_free(caps); - } - - if (has_notify_osd) { - priv->notification = notify_notification_new(PACKAGE_NAME, NULL, NULL); - notify_notification_set_hint_string(priv->notification, - "x-canonical-private-synchronous", PACKAGE_NAME); - } -} - -void -sound_state_manager_show_notification (SoundStateManager *self, - double value) -{ - SoundStateManagerPrivate* priv = SOUND_STATE_MANAGER_GET_PRIVATE(self); - - sound_state_manager_notification_init (self); - - if (priv->notification == NULL || - g_settings_get_boolean (priv->settings_manager, "show-notify-osd-on-scroll") == FALSE){ - return; - } - - char *icon; - const int notify_value = CLAMP((int)value, -1, 101); - - SoundState state = sound_state_get_from_volume ((int)value); - - if (state == ZERO_LEVEL) { - // Not available for all the themes - icon = "notification-audio-volume-off"; - } else if (state == LOW_LEVEL) { - icon = "notification-audio-volume-low"; - } else if (state == MEDIUM_LEVEL) { - icon = "notification-audio-volume-medium"; - } else if (state == HIGH_LEVEL) { - icon = "notification-audio-volume-high"; - } else { - icon = "notification-audio-volume-muted"; - } - - notify_notification_update(priv->notification, PACKAGE_NAME, NULL, icon); - notify_notification_set_hint_int32(priv->notification, "value", notify_value); - notify_notification_show(priv->notification, NULL); -} - - -/* -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 ); -} - diff --git a/src/sound-state-manager.h b/src/sound-state-manager.h deleted file mode 100644 index 9287897..0000000 --- a/src/sound-state-manager.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2011 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 . -*/ - -#ifndef _SOUND_STATE_MANAGER_H_ -#define _SOUND_STATE_MANAGER_H_ - -#include -#include "common-defs.h" - -G_BEGIN_DECLS - -#define SOUND_TYPE_STATE_MANAGER (sound_state_manager_get_type ()) -#define SOUND_STATE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUND_TYPE_STATE_MANAGER, SoundStateManager)) -#define SOUND_STATE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUND_TYPE_STATE_MANAGER, SoundStateManagerClass)) -#define SOUND_IS_STATE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUND_TYPE_STATE_MANAGER)) -#define SOUND_IS_STATE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUND_TYPE_STATE_MANAGER)) -#define SOUND_STATE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUND_TYPE_STATE_MANAGER, SoundStateManagerClass)) - -typedef struct _SoundStateManagerClass SoundStateManagerClass; -typedef struct _SoundStateManager SoundStateManager; - -struct _SoundStateManagerClass -{ - GObjectClass parent_class; -}; - -struct _SoundStateManager -{ - GObject parent_instance; -}; - -GType sound_state_manager_get_type (void) G_GNUC_CONST; - -void sound_state_manager_style_changed_cb (GtkWidget *widget, - GtkStyle *previous_style, - gpointer user_data); -GtkImage* sound_state_manager_get_current_icon (SoundStateManager* self); -SoundState sound_state_manager_get_current_state (SoundStateManager* self); -void sound_state_manager_connect_to_dbus (SoundStateManager* self, - GDBusProxy* proxy); -void sound_state_manager_deal_with_disconnect (SoundStateManager* self); -void sound_state_manager_get_state_cb (GObject *object, - GAsyncResult *res, - gpointer user_data); -void sound_state_manager_show_notification (SoundStateManager *self, - double value); - - -G_END_DECLS - -#endif /* _SOUND_STATE_MANAGER_H_ */ diff --git a/src/transport-widget.c b/src/transport-widget.c deleted file mode 100644 index ebe8282..0000000 --- a/src/transport-widget.c +++ /dev/null @@ -1,1886 +0,0 @@ -/* -Copyright 2010 Canonical Ltd. - -Authors: - Conor Curran - Mirco Müller - Andrea Cimitan - -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 . - -Uses code from ctk -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "transport-widget.h" - - -#define RECT_WIDTH 130.0f -#define Y 7.0f -#define X 70.0f -#define INNER_RADIUS 12.5 -#define MIDDLE_RADIUS 13.0f -#define OUTER_RADIUS 14.5f -#define CIRCLE_RADIUS 21.0f -#define PREV_WIDTH 25.0f -#define PREV_HEIGHT 17.0f -#define NEXT_WIDTH 25.0f //PREV_WIDTH -#define NEXT_HEIGHT 17.0f //PREV_HEIGHT -#define TRI_WIDTH 11.0f -#define TRI_HEIGHT 13.0f -#define TRI_OFFSET 6.0f -#define PREV_X 68.0f -#define PREV_Y 13.0f -#define NEXT_X 146.0f -#define NEXT_Y 13.0f //prev_y -#define PAUSE_WIDTH 21.0f -#define PAUSE_HEIGHT 27.0f -#define BAR_WIDTH 4.5f -#define BAR_HEIGHT 24.0f -#define BAR_OFFSET 10.0f -#define PAUSE_X 111.0f -#define PAUSE_Y 7.0f -#define PLAY_WIDTH 28.0f -#define PLAY_HEIGHT 29.0f -#define PLAY_PADDING 5.0f -#define INNER_START_SHADE 0.98 -#define INNER_END_SHADE 0.98 -#define MIDDLE_START_SHADE 1.0 -#define MIDDLE_END_SHADE 1.0 -#define OUTER_START_SHADE 0.75 -#define OUTER_END_SHADE 1.3 -#define SHADOW_BUTTON_SHADE 0.8 -#define OUTER_PLAY_START_SHADE 0.7 -#define OUTER_PLAY_END_SHADE 1.38 -#define BUTTON_START_SHADE 1.1 -#define BUTTON_END_SHADE 0.9 -#define BUTTON_SHADOW_SHADE 0.8 -#define INNER_COMPRESSED_START_SHADE 1.0 -#define INNER_COMPRESSED_END_SHADE 1.0 - -typedef struct _TransportWidgetPrivate TransportWidgetPrivate; - -struct _TransportWidgetPrivate -{ - TransportAction current_command; - TransportAction key_event; - TransportAction motion_event; - TransportState current_state; - GHashTable* command_coordinates; - DbusmenuMenuitem* twin_item; - gboolean has_focus; - gint hold_timer; - gint skip_frequency; -}; - -#if GTK_CHECK_VERSION(3, 0, 0) -static GList *transport_widget_list = NULL; -static GtkStyleContext *spinner_style_context = NULL; -static GtkWidgetPath *spinner_widget_path = NULL; -#endif - -// TODO refactor the UI handlers, consolidate functionality between key press /release -// and button press / release. -#define TRANSPORT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRANSPORT_WIDGET_TYPE, TransportWidgetPrivate)) - -/* Gobject boiler plate */ -static void transport_widget_class_init (TransportWidgetClass *klass); -static void transport_widget_init (TransportWidget *self); -static void transport_widget_dispose (GObject *object); -static void transport_widget_finalize (GObject *object); -G_DEFINE_TYPE (TransportWidget, transport_widget, GTK_TYPE_MENU_ITEM); - -/* essentials */ -static void transport_widget_set_twin_item ( TransportWidget* self, - DbusmenuMenuitem* twin_item); -#if ! GTK_CHECK_VERSION(3, 0, 0) -static gboolean transport_widget_expose ( GtkWidget *button, GdkEventExpose *event); -#endif -static gboolean draw (GtkWidget* button, cairo_t *cr); - -/* UI and dbusmenu callbacks */ -static gboolean transport_widget_button_press_event (GtkWidget *menuitem, - GdkEventButton *event); -static gboolean transport_widget_button_release_event (GtkWidget *menuitem, - GdkEventButton *event); -static gboolean transport_widget_motion_notify_event (GtkWidget *menuitem, - GdkEventMotion *event); -static gboolean transport_widget_leave_notify_event (GtkWidget *menuitem, - GdkEventCrossing *event); -static void transport_widget_property_update ( DbusmenuMenuitem* item, - gchar * property, - GVariant * value, - gpointer userdata ); -static void transport_widget_menu_hidden ( GtkWidget *menu, - TransportWidget *transport); -static void transport_widget_notify ( GObject *item, - GParamSpec *pspec, - gpointer user_data ); -static TransportAction transport_widget_determine_button_event ( TransportWidget* button, - GdkEventButton* event); -static TransportAction transport_widget_determine_motion_event ( TransportWidget* button, - GdkEventMotion* event); -static void transport_widget_react_to_button_release ( TransportWidget* button, - TransportAction command); -static void transport_widget_toggle_play_pause ( TransportWidget* button, - TransportState update); -static void transport_widget_select (GtkWidget* menu, gpointer Userdata); -static void transport_widget_deselect (GtkWidget* menu, gpointer Userdata); -static TransportAction transport_widget_collision_detection (gint x, gint y); -static void transport_widget_start_timing (TransportWidget* widget); -static gboolean transport_widget_trigger_seek (gpointer userdata); -static gboolean transport_widget_seek (gpointer userdata); - - -/// Init functions ////////////////////////////////////////////////////////// -static void -transport_widget_class_init (TransportWidgetClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); - - g_type_class_add_private (klass, sizeof (TransportWidgetPrivate)); - - widget_class->button_press_event = transport_widget_button_press_event; - widget_class->button_release_event = transport_widget_button_release_event; - widget_class->motion_notify_event = transport_widget_motion_notify_event; - widget_class->leave_notify_event = transport_widget_leave_notify_event; -#if GTK_CHECK_VERSION(3, 0, 0) - widget_class->draw = draw; -#else - widget_class->expose_event = transport_widget_expose; -#endif - - gobject_class->dispose = transport_widget_dispose; - gobject_class->finalize = transport_widget_finalize; -} - -static void -transport_widget_init (TransportWidget *self) -{ - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self); - #if GTK_CHECK_VERSION(3, 0, 0) - if (transport_widget_list == NULL){ - /* append the object to the static linked list. */ - transport_widget_list = g_list_append (transport_widget_list, self); - - /* create widget path */ - spinner_widget_path = gtk_widget_path_new(); - - gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU); - gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_MENU_ITEM); - gint pos = gtk_widget_path_append_type (spinner_widget_path, GTK_TYPE_SPINNER); - gtk_widget_path_iter_set_name (spinner_widget_path, pos, "IndicatorSoundSpinner"); - - /* create style context and append path */ - spinner_style_context = gtk_style_context_new(); - - gtk_style_context_set_path (spinner_style_context, spinner_widget_path); - gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENU); - gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_MENUITEM); - gtk_style_context_add_class (spinner_style_context, GTK_STYLE_CLASS_SPINNER); - } - #endif - priv->current_command = TRANSPORT_ACTION_NO_ACTION; - priv->current_state = TRANSPORT_STATE_PAUSED; - priv->key_event = TRANSPORT_ACTION_NO_ACTION; - priv->motion_event = TRANSPORT_ACTION_NO_ACTION; - priv->has_focus = FALSE; - priv->hold_timer = 0; - priv->skip_frequency = 0; - priv->command_coordinates = g_hash_table_new_full(g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify)g_list_free); - GList* previous_list = NULL; - previous_list = g_list_insert(previous_list, GINT_TO_POINTER(15), 0); - previous_list = g_list_insert(previous_list, GINT_TO_POINTER(5), 1); - previous_list = g_list_insert(previous_list, GINT_TO_POINTER(60), 2); - previous_list = g_list_insert(previous_list, GINT_TO_POINTER(34), 3); - g_hash_table_insert(priv->command_coordinates, - GINT_TO_POINTER(TRANSPORT_ACTION_PREVIOUS), - previous_list); - - GList* play_list = NULL; - play_list = g_list_insert(play_list, GINT_TO_POINTER(58), 0); - play_list = g_list_insert(play_list, GINT_TO_POINTER(0), 1); - play_list = g_list_insert(play_list, GINT_TO_POINTER(50), 2); - play_list = g_list_insert(play_list, GINT_TO_POINTER(43), 3); - - g_hash_table_insert(priv->command_coordinates, - GINT_TO_POINTER(TRANSPORT_ACTION_PLAY_PAUSE), - play_list); - - GList* next_list = NULL; - next_list = g_list_insert(next_list, GINT_TO_POINTER(100), 0); - next_list = g_list_insert(next_list, GINT_TO_POINTER(5), 1); - next_list = g_list_insert(next_list, GINT_TO_POINTER(60), 2); - next_list = g_list_insert(next_list, GINT_TO_POINTER(34), 3); - - g_hash_table_insert(priv->command_coordinates, - GINT_TO_POINTER(TRANSPORT_ACTION_NEXT), - next_list); - gtk_widget_set_size_request(GTK_WIDGET(self), 200, 43); - g_signal_connect (G_OBJECT(self), - "notify", - G_CALLBACK (transport_widget_notify), - NULL); - g_signal_connect (G_OBJECT(self), - "select", - G_CALLBACK (transport_widget_select), - NULL); - g_signal_connect (G_OBJECT(self), - "deselect", - G_CALLBACK (transport_widget_deselect), - NULL); - gtk_widget_realize ( GTK_WIDGET (self) ); - -} - -static void -transport_widget_dispose (GObject *object) -{ - #if GTK_CHECK_VERSION(3, 0, 0) - transport_widget_list = g_list_remove (transport_widget_list, object); - - if (transport_widget_list == NULL){ - if (spinner_widget_path != NULL){ - gtk_widget_path_free (spinner_widget_path); - spinner_widget_path = NULL; - } - - if (spinner_style_context != NULL){ - g_object_unref (spinner_style_context); - spinner_style_context = NULL; - } - } - #endif - - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(object); - if (priv->command_coordinates != NULL) { - g_hash_table_destroy (priv->command_coordinates); - priv->command_coordinates = NULL; - } - - G_OBJECT_CLASS (transport_widget_parent_class)->dispose (object); -} - -static void -transport_widget_finalize (GObject *object) -{ - - - G_OBJECT_CLASS (transport_widget_parent_class)->finalize (object); -} - -#if ! GTK_CHECK_VERSION(3, 0, 0) -static gboolean -transport_widget_expose (GtkWidget *button, GdkEventExpose *event) -{ - cairo_t *cr; - cr = gdk_cairo_create (gtk_widget_get_window (button)); - - cairo_rectangle (cr, - event->area.x, event->area.y, - event->area.width, event->area.height); - - cairo_clip(cr); - draw (button, cr); - - cairo_destroy (cr); - return FALSE; -} -#endif - -gboolean -transport_widget_is_selected ( TransportWidget* widget ) -{ - g_return_val_if_fail (IS_TRANSPORT_WIDGET (widget), FALSE); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(widget); - return priv->has_focus; -} - -static void -transport_widget_toggle_play_pause(TransportWidget* button, - TransportState update) -{ - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button); - priv->current_state = update; - gtk_widget_queue_draw (GTK_WIDGET(button)); -} - -static void -transport_widget_notify ( GObject *item, - GParamSpec *pspec, - gpointer user_data ) -{ - if (g_strcmp0 (pspec->name, "parent")){ - GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (item)); - if (parent){ - g_signal_connect ( parent, "hide", - G_CALLBACK (transport_widget_menu_hidden), - item ); - } - } -} - -static void -transport_widget_menu_hidden ( GtkWidget *menu, - TransportWidget *transport) -{ - g_return_if_fail(IS_TRANSPORT_WIDGET(transport)); - transport_widget_react_to_button_release(transport, TRANSPORT_ACTION_NO_ACTION); -} - -static gboolean -transport_widget_motion_notify_event (GtkWidget *menuitem, - GdkEventMotion *event) -{ - //g_debug("transport_widget_motion_notify_event()"); - - g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); - TransportAction result = transport_widget_determine_motion_event ( TRANSPORT_WIDGET(menuitem), - event); - priv->motion_event = result; - gtk_widget_queue_draw (menuitem); - if (priv->hold_timer != 0){ - g_source_remove (priv->hold_timer); - priv->hold_timer = 0; - } - if(priv->skip_frequency != 0){ - g_source_remove (priv->skip_frequency); - priv->skip_frequency = 0; - } - return TRUE; -} - -static gboolean -transport_widget_leave_notify_event (GtkWidget *menuitem, - GdkEventCrossing *event) -{ - //g_debug("transport_widget_leave_notify_event()"); - - g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); - - priv->motion_event = TRANSPORT_ACTION_NO_ACTION; - priv->current_command = TRANSPORT_ACTION_NO_ACTION; - gtk_widget_queue_draw (GTK_WIDGET(menuitem)); - - return TRUE; -} - -static gboolean -transport_widget_button_press_event (GtkWidget *menuitem, - GdkEventButton *event) -{ - //g_debug("transport_widget_button_press_event()"); - - g_return_val_if_fail ( IS_TRANSPORT_WIDGET(menuitem), FALSE ); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE ( TRANSPORT_WIDGET(menuitem) ); - TransportAction result = transport_widget_determine_button_event ( TRANSPORT_WIDGET(menuitem), - event); - if(result != TRANSPORT_ACTION_NO_ACTION){ - priv->current_command = result; - gtk_widget_queue_draw (GTK_WIDGET(menuitem)); - if (priv->current_command == TRANSPORT_ACTION_PREVIOUS || - priv->current_command == TRANSPORT_ACTION_NEXT){ - transport_widget_start_timing (TRANSPORT_WIDGET(menuitem)); - } - } - return TRUE; -} -/** - * TODO rename or merge - * @param widget - */ -static void -transport_widget_start_timing (TransportWidget* widget) -{ - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (widget); - if (priv->hold_timer == 0){ - priv->hold_timer = g_timeout_add (800, - transport_widget_trigger_seek, - widget); - } -} - -static gboolean -transport_widget_trigger_seek (gpointer userdata) -{ - g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE ); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata)); - if (priv->skip_frequency == 0){ - priv->skip_frequency = g_timeout_add (100, - transport_widget_seek, - userdata); - } - priv->hold_timer = 0; - return FALSE; -} - -/** - * This will be called repeatedly until a key/button release is received - * @param userdata - * @return - */ -static gboolean -transport_widget_seek (gpointer userdata) -{ - g_return_val_if_fail ( IS_TRANSPORT_WIDGET(userdata), FALSE ); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE (TRANSPORT_WIDGET(userdata)); - GVariant* new_transport_state; - if(priv->current_command == TRANSPORT_ACTION_NEXT){ - //g_debug ("we should be skipping forward"); - new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_FORWIND); - - dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", - new_transport_state, - 0 ); - - } - else if(priv->current_command == TRANSPORT_ACTION_PREVIOUS){ - //g_debug ("we should be skipping back"); - new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_REWIND); - - dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", - new_transport_state, - 0 ); - } - - return TRUE; -} - -static gboolean -transport_widget_button_release_event (GtkWidget *menuitem, - GdkEventButton *event) -{ - g_return_val_if_fail(IS_TRANSPORT_WIDGET(menuitem), FALSE); - TransportWidget* transport = TRANSPORT_WIDGET(menuitem); - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - TransportAction result = transport_widget_determine_button_event ( transport, - event ); - if (result != TRANSPORT_ACTION_NO_ACTION && - priv->current_command == result && - priv->skip_frequency == 0){ - GVariant* new_transport_state = g_variant_new_int32 ((int)result); - dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", - new_transport_state, - 0 ); - } - transport_widget_react_to_button_release ( transport, - result ); - return TRUE; -} - -static void -transport_widget_select (GtkWidget* item, gpointer Userdata) -{ - TransportWidget* transport = TRANSPORT_WIDGET(item); - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - priv->has_focus = TRUE; -} - -static void -transport_widget_deselect (GtkWidget* item, gpointer Userdata) -{ - TransportWidget* transport = TRANSPORT_WIDGET(item); - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - priv->has_focus = FALSE; -} - -void -transport_widget_react_to_key_press_event ( TransportWidget* transport, - TransportAction transport_event ) -{ - if(transport_event != TRANSPORT_ACTION_NO_ACTION){ - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - priv->current_command = transport_event; - priv->key_event = transport_event; - gtk_widget_realize ( GTK_WIDGET(transport) ); - gtk_widget_queue_draw (GTK_WIDGET(transport) ); - if (priv->current_command == TRANSPORT_ACTION_PREVIOUS || - priv->current_command == TRANSPORT_ACTION_NEXT){ - transport_widget_start_timing (transport); - } - } -} - -void -transport_widget_react_to_key_release_event ( TransportWidget* transport, - TransportAction transport_event ) -{ - if(transport_event != TRANSPORT_ACTION_NO_ACTION){ - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - GVariant* new_transport_event = g_variant_new_int32((int)transport_event); - if (priv->skip_frequency == 0){ - dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", - new_transport_event, - 0 ); - } - } - transport_widget_react_to_button_release ( transport, - transport_event ); -} - -void -transport_widget_focus_update ( TransportWidget* transport, gboolean focus ) -{ - TransportWidgetPrivate * priv = TRANSPORT_WIDGET_GET_PRIVATE ( transport ); - priv->has_focus = focus; -} - -static TransportAction -transport_widget_determine_button_event( TransportWidget* button, - GdkEventButton* event ) -{ - return transport_widget_collision_detection (event->x, event->y); -} - -static TransportAction -transport_widget_determine_motion_event( TransportWidget* button, - GdkEventMotion* event ) -{ - return transport_widget_collision_detection (event->x, event->y); -} - -static TransportAction -transport_widget_collision_detection ( gint x, - gint y ) -{ - TransportAction event = TRANSPORT_ACTION_NO_ACTION; - - if (x > 57 && x < 102 - && y > 12 && y < 40){ - event = TRANSPORT_ACTION_PREVIOUS; - } - else if (x > 101 && x < 143 - && y > 5 && y < 47){ - event = TRANSPORT_ACTION_PLAY_PAUSE; - } - else if (x > 142 && x < 187 - && y > 12 && y < 40){ - event = TRANSPORT_ACTION_NEXT; - } - return event; -} - -static void -transport_widget_react_to_button_release ( TransportWidget* button, - TransportAction command ) -{ - g_return_if_fail(IS_TRANSPORT_WIDGET(button)); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button); - - priv->current_command = TRANSPORT_ACTION_NO_ACTION; - priv->key_event = TRANSPORT_ACTION_NO_ACTION; - - gtk_widget_queue_draw (GTK_WIDGET(button)); - if (priv->hold_timer != 0){ - g_source_remove (priv->hold_timer); - priv->hold_timer = 0; - } - if(priv->skip_frequency != 0){ - g_source_remove (priv->skip_frequency); - priv->skip_frequency = 0; - } -} - -/// internal helper functions ////////////////////////////////////////////////// - -static void -draw_gradient (cairo_t* cr, - double x, - double y, - double w, - double r, - double* rgba_start, - double* rgba_end) -{ - cairo_pattern_t* pattern = NULL; - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x + w - 2.0f * r, y); - cairo_arc (cr, - x + w - 2.0f * r, - y + r, - r, - -90.0f * G_PI / 180.0f, - 90.0f * G_PI / 180.0f); - cairo_line_to (cr, x, y + 2.0f * r); - cairo_arc (cr, - x, - y + r, - r, - 90.0f * G_PI / 180.0f, - 270.0f * G_PI / 180.0f); - cairo_close_path (cr); - - pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r); - cairo_pattern_add_color_stop_rgba (pattern, - 0.0f, - rgba_start[0], - rgba_start[1], - rgba_start[2], - rgba_start[3]); - cairo_pattern_add_color_stop_rgba (pattern, - 1.0f, - rgba_end[0], - rgba_end[1], - rgba_end[2], - rgba_end[3]); - cairo_set_source (cr, pattern); - cairo_fill (cr); - cairo_pattern_destroy (pattern); -} - -static void -draw_circle (cairo_t* cr, - double x, - double y, - double r, - double* rgba_start, - double* rgba_end) -{ - cairo_pattern_t* pattern = NULL; - - cairo_move_to (cr, x, y); - cairo_arc (cr, - x + r, - y + r, - r, - 0.0f * G_PI / 180.0f, - 360.0f * G_PI / 180.0f); - - pattern = cairo_pattern_create_linear (x, y, x, y + 2.0f * r); - cairo_pattern_add_color_stop_rgba (pattern, - 0.0f, - rgba_start[0], - rgba_start[1], - rgba_start[2], - rgba_start[3]); - cairo_pattern_add_color_stop_rgba (pattern, - 1.0f, - rgba_end[0], - rgba_end[1], - rgba_end[2], - rgba_end[3]); - cairo_set_source (cr, pattern); - cairo_fill (cr); - cairo_pattern_destroy (pattern); -} - -static void -_setup (cairo_t** cr, - cairo_surface_t** surf, - gint width, - gint height) -{ - if (!cr || !surf) - return; - - *surf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - *cr = cairo_create (*surf); - cairo_scale (*cr, 1.0f, 1.0f); - cairo_set_operator (*cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (*cr); - cairo_set_operator (*cr, CAIRO_OPERATOR_OVER); -} - -static void -_mask_prev (cairo_t* cr, - double x, - double y, - double tri_width, - double tri_height, - double tri_offset) -{ - if (!cr) - return; - - cairo_move_to (cr, x, y + tri_height / 2.0f); - cairo_line_to (cr, x + tri_width, y); - cairo_line_to (cr, x + tri_width, y + tri_height); - x += tri_offset; - cairo_move_to (cr, x, y + tri_height / 2.0f); - cairo_line_to (cr, x + tri_width, y); - cairo_line_to (cr, x + tri_width, y + tri_height); - x -= tri_offset; - cairo_rectangle (cr, x, y, 2.5f, tri_height); - cairo_close_path (cr); -} - -static void -_mask_next (cairo_t* cr, - double x, - double y, - double tri_width, - double tri_height, - double tri_offset) -{ - if (!cr) - return; - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f); - cairo_line_to (cr, x, y + tri_height); - x += tri_offset; - cairo_move_to (cr, x, y); - cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f); - cairo_line_to (cr, x, y + tri_height); - x -= tri_offset; - x += 2.0f * tri_width - tri_offset - 1.0f; - cairo_rectangle (cr, x, y, 2.5f, tri_height); - - cairo_close_path (cr); -} - -static void -_mask_pause (cairo_t* cr, - double x, - double y, - double bar_width, - double bar_height, - double bar_offset) -{ - if (!cr) - return; - - cairo_set_line_width (cr, bar_width); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - - x += bar_width; - y += bar_width; - cairo_move_to (cr, x, y); - cairo_line_to (cr, x, y + bar_height); - cairo_move_to (cr, x + bar_offset, y); - cairo_line_to (cr, x + bar_offset, y + bar_height); - -} - -static void -_mask_play (cairo_t* cr, - double x, - double y, - double tri_width, - double tri_height) -{ - if (!cr) - return; - - cairo_move_to (cr, x, y); - cairo_line_to (cr, x + tri_width, y + tri_height / 2.0f); - cairo_line_to (cr, x, y + tri_height); - cairo_close_path (cr); - -} - -static void -_fill (cairo_t* cr, - double x_start, - double y_start, - double x_end, - double y_end, - double* rgba_start, - double* rgba_end, - gboolean stroke) -{ - cairo_pattern_t* pattern = NULL; - - if (!cr || !rgba_start || !rgba_end) - return; - - pattern = cairo_pattern_create_linear (x_start, y_start, x_end, y_end); - cairo_pattern_add_color_stop_rgba (pattern, - 0.0f, - rgba_start[0], - rgba_start[1], - rgba_start[2], - rgba_start[3]); - cairo_pattern_add_color_stop_rgba (pattern, - 1.0f, - rgba_end[0], - rgba_end[1], - rgba_end[2], - rgba_end[3]); - cairo_set_source (cr, pattern); - if (stroke) - cairo_stroke (cr); - else - cairo_fill (cr); - cairo_pattern_destroy (pattern); -} - -static void -_finalize (cairo_t* cr, - cairo_t** cr_surf, - cairo_surface_t** surf, - double x, - double y) -{ - if (!cr || !cr_surf || !surf) - return; - - cairo_set_source_surface (cr, *surf, x, y); - cairo_paint (cr); - cairo_surface_destroy (*surf); - cairo_destroy (*cr_surf); -} - -static void -_finalize_repaint (cairo_t* cr, - cairo_t** cr_surf, - cairo_surface_t** surf, - double x, - double y, - int repaints) -{ - if (!cr || !cr_surf || !surf) - return; - - while (repaints > 0) - { - cairo_set_source_surface (cr, *surf, x, y); - cairo_paint (cr); - repaints--; - } - - cairo_surface_destroy (*surf); - cairo_destroy (*cr_surf); -} - -static void -_color_rgb_to_hls (gdouble *r, - gdouble *g, - gdouble *b) -{ - gdouble min; - gdouble max; - gdouble red; - gdouble green; - gdouble blue; - gdouble h = 0; - gdouble l; - gdouble s; - gdouble delta; - - red = *r; - green = *g; - blue = *b; - - if (red > green) - { - if (red > blue) - max = red; - else - max = blue; - - if (green < blue) - min = green; - else - min = blue; - } - else - { - if (green > blue) - max = green; - else - max = blue; - - if (red < blue) - min = red; - else - min = blue; - } - l = (max+min)/2; - if (fabs (max-min) < 0.0001) - { - h = 0; - s = 0; - } - else - { - if (l <= 0.5) - s = (max-min)/(max+min); - else - s = (max-min)/(2-max-min); - - delta = (max -min) != 0 ? (max -min) : 1; - - if(delta == 0) - delta = 1; - if (red == max) - h = (green-blue)/delta; - else if (green == max) - h = 2+(blue-red)/delta; - else if (blue == max) - h = 4+(red-green)/delta; - - h *= 60; - if (h < 0.0) - h += 360; - } - - *r = h; - *g = l; - *b = s; -} - -static void -_color_hls_to_rgb (gdouble *h, - gdouble *l, - gdouble *s) -{ - gdouble hue; - gdouble lightness; - gdouble saturation; - gdouble m1, m2; - gdouble r, g, b; - - lightness = *l; - saturation = *s; - - if (lightness <= 0.5) - m2 = lightness*(1+saturation); - else - m2 = lightness+saturation-lightness*saturation; - - m1 = 2*lightness-m2; - - if (saturation == 0) - { - *h = lightness; - *l = lightness; - *s = lightness; - } - else - { - hue = *h+120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - r = m1+(m2-m1)*hue/60; - else if (hue < 180) - r = m2; - else if (hue < 240) - r = m1+(m2-m1)*(240-hue)/60; - else - r = m1; - - hue = *h; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - g = m1+(m2-m1)*hue/60; - else if (hue < 180) - g = m2; - else if (hue < 240) - g = m1+(m2-m1)*(240-hue)/60; - else - g = m1; - - hue = *h-120; - while (hue > 360) - hue -= 360; - while (hue < 0) - hue += 360; - - if (hue < 60) - b = m1+(m2-m1)*hue/60; - else if (hue < 180) - b = m2; - else if (hue < 240) - b = m1+(m2-m1)*(240-hue)/60; - else - b = m1; - - *h = r; - *l = g; - *s = b; - } -} - -void -_color_shade (const CairoColorRGB *a, float k, CairoColorRGB *b) -{ - double red; - double green; - double blue; - - red = a->r; - green = a->g; - blue = a->b; - - if (k == 1.0) - { - b->r = red; - b->g = green; - b->b = blue; - return; - } - - _color_rgb_to_hls (&red, &green, &blue); - - green *= k; - if (green > 1.0) - green = 1.0; - else if (green < 0.0) - green = 0.0; - - blue *= k; - if (blue > 1.0) - blue = 1.0; - else if (blue < 0.0) - blue = 0.0; - - _color_hls_to_rgb (&red, &green, &blue); - - b->r = red; - b->g = green; - b->b = blue; -} - -static inline void -_blurinner (guchar* pixel, - gint* zR, - gint* zG, - gint* zB, - gint* zA, - gint alpha, - gint aprec, - gint zprec) -{ - gint R; - gint G; - gint B; - guchar A; - - R = *pixel; - G = *(pixel + 1); - B = *(pixel + 2); - A = *(pixel + 3); - - *zR += (alpha * ((R << zprec) - *zR)) >> aprec; - *zG += (alpha * ((G << zprec) - *zG)) >> aprec; - *zB += (alpha * ((B << zprec) - *zB)) >> aprec; - *zA += (alpha * ((A << zprec) - *zA)) >> aprec; - - *pixel = *zR >> zprec; - *(pixel + 1) = *zG >> zprec; - *(pixel + 2) = *zB >> zprec; - *(pixel + 3) = *zA >> zprec; -} - -static inline void -_blurrow (guchar* pixels, - gint width, - gint height, - gint channels, - gint line, - gint alpha, - gint aprec, - gint zprec) -{ - gint zR; - gint zG; - gint zB; - gint zA; - gint index; - guchar* scanline; - - scanline = &(pixels[line * width * channels]); - - zR = *scanline << zprec; - zG = *(scanline + 1) << zprec; - zB = *(scanline + 2) << zprec; - zA = *(scanline + 3) << zprec; - - for (index = 0; index < width; index ++) - _blurinner (&scanline[index * channels], - &zR, - &zG, - &zB, - &zA, - alpha, - aprec, - zprec); - - for (index = width - 2; index >= 0; index--) - _blurinner (&scanline[index * channels], - &zR, - &zG, - &zB, - &zA, - alpha, - aprec, - zprec); -} - -static inline void -_blurcol (guchar* pixels, - gint width, - gint height, - gint channels, - gint x, - gint alpha, - gint aprec, - gint zprec) -{ - gint zR; - gint zG; - gint zB; - gint zA; - gint index; - guchar* ptr; - - ptr = pixels; - - ptr += x * channels; - - zR = *((guchar*) ptr ) << zprec; - zG = *((guchar*) ptr + 1) << zprec; - zB = *((guchar*) ptr + 2) << zprec; - zA = *((guchar*) ptr + 3) << zprec; - - for (index = width; index < (height - 1) * width; index += width) - _blurinner ((guchar*) &ptr[index * channels], - &zR, - &zG, - &zB, - &zA, - alpha, - aprec, - zprec); - - for (index = (height - 2) * width; index >= 0; index -= width) - _blurinner ((guchar*) &ptr[index * channels], - &zR, - &zG, - &zB, - &zA, - alpha, - aprec, - zprec); -} - -void -_expblur (guchar* pixels, - gint width, - gint height, - gint channels, - gint radius, - gint aprec, - gint zprec) -{ - gint alpha; - gint row = 0; - gint col = 0; - - if (radius < 1) - return; - - // calculate the alpha such that 90% of - // the kernel is within the radius. - // (Kernel extends to infinity) - alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f)))); - - for (; row < height; row++) - _blurrow (pixels, - width, - height, - channels, - row, - alpha, - aprec, - zprec); - - for(; col < width; col++) - _blurcol (pixels, - width, - height, - channels, - col, - alpha, - aprec, - zprec); - - return; -} - -void -_surface_blur (cairo_surface_t* surface, - guint radius) -{ - guchar* pixels; - guint width; - guint height; - cairo_format_t format; - - // before we mess with the surface execute any pending drawing - cairo_surface_flush (surface); - - pixels = cairo_image_surface_get_data (surface); - width = cairo_image_surface_get_width (surface); - height = cairo_image_surface_get_height (surface); - format = cairo_image_surface_get_format (surface); - - switch (format) - { - case CAIRO_FORMAT_ARGB32: - _expblur (pixels, width, height, 4, radius, 16, 7); - break; - - case CAIRO_FORMAT_RGB24: - _expblur (pixels, width, height, 3, radius, 16, 7); - break; - - case CAIRO_FORMAT_A8: - _expblur (pixels, width, height, 1, radius, 16, 7); - break; - - default : - // do nothing - break; - } - - // inform cairo we altered the surfaces contents - cairo_surface_mark_dirty (surface); -} - -static gboolean -draw (GtkWidget* button, cairo_t *cr) -{ - g_return_val_if_fail(IS_TRANSPORT_WIDGET(button), FALSE); - g_return_val_if_fail(cr != NULL, FALSE); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(button); - - //g_debug("transport-widget draw()"); - - cairo_surface_t* surf = NULL; - cairo_t* cr_surf = NULL; - -#if ! GTK_CHECK_VERSION(3, 0, 0) - GtkAllocation allocation; - gtk_widget_get_allocation (button, &allocation); - cairo_translate (cr, allocation.x, allocation.y); -#endif - - GtkStyle *style; - -#if GTK_CHECK_VERSION(3, 0, 0) - gtk_style_context_add_class (gtk_widget_get_style_context (button), - GTK_STYLE_CLASS_MENU); -#endif - CairoColorRGB bg_color, fg_color, bg_selected, bg_prelight; - CairoColorRGB color_middle[2], color_middle_prelight[2], color_outer[2], color_outer_prelight[2], - color_play_outer[2], color_play_outer_prelight[2], - color_button[4], color_button_shadow, color_inner[2], color_inner_compressed[2]; - - /* Use the menu's style instead of that of the menuitem ('button' is a - * menuitem that is packed in a menu directly). The menuitem's style - * can't be used due to a change in light-themes (lp #1130183). - * Menuitems now have a transparent background, which confuses - * GtkStyle. - * - * This is a workaround until this code gets refactored to use - * GtkStyleContext. - */ - style = gtk_widget_get_style (gtk_widget_get_parent (button)); - - bg_color.r = style->bg[0].red/65535.0; - bg_color.g = style->bg[0].green/65535.0; - bg_color.b = style->bg[0].blue/65535.0; - - bg_prelight.r = style->bg[GTK_STATE_PRELIGHT].red/65535.0; - bg_prelight.g = style->bg[GTK_STATE_PRELIGHT].green/65535.0; - bg_prelight.b = style->bg[GTK_STATE_PRELIGHT].blue/65535.0; - - bg_selected.r = style->bg[GTK_STATE_SELECTED].red/65535.0; - bg_selected.g = style->bg[GTK_STATE_SELECTED].green/65535.0; - bg_selected.b = style->bg[GTK_STATE_SELECTED].blue/65535.0; - - fg_color.r = style->fg[0].red/65535.0; - fg_color.g = style->fg[0].green/65535.0; - fg_color.b = style->fg[0].blue/65535.0; - - _color_shade (&bg_color, MIDDLE_START_SHADE, &color_middle[0]); - _color_shade (&bg_color, MIDDLE_END_SHADE, &color_middle[1]); - _color_shade (&bg_prelight, MIDDLE_START_SHADE, &color_middle_prelight[0]); - _color_shade (&bg_prelight, MIDDLE_END_SHADE, &color_middle_prelight[1]); - _color_shade (&bg_color, OUTER_START_SHADE, &color_outer[0]); - _color_shade (&bg_color, OUTER_END_SHADE, &color_outer[1]); - _color_shade (&bg_prelight, OUTER_START_SHADE, &color_outer_prelight[0]); - _color_shade (&bg_prelight, OUTER_END_SHADE, &color_outer_prelight[1]); - _color_shade (&bg_color, OUTER_PLAY_START_SHADE, &color_play_outer[0]); - _color_shade (&bg_color, OUTER_PLAY_END_SHADE, &color_play_outer[1]); - _color_shade (&bg_prelight, OUTER_PLAY_START_SHADE, &color_play_outer_prelight[0]); - _color_shade (&bg_prelight, OUTER_PLAY_END_SHADE, &color_play_outer_prelight[1]); - _color_shade (&bg_color, INNER_START_SHADE, &color_inner[0]); - _color_shade (&bg_color, INNER_END_SHADE, &color_inner[1]); - _color_shade (&fg_color, BUTTON_START_SHADE, &color_button[0]); - _color_shade (&fg_color, BUTTON_END_SHADE, &color_button[1]); - _color_shade (&bg_color, BUTTON_SHADOW_SHADE, &color_button[2]); - _color_shade (&bg_color, SHADOW_BUTTON_SHADE, &color_button_shadow); - _color_shade (&bg_selected, 1.0, &color_button[3]); - _color_shade (&bg_color, INNER_COMPRESSED_START_SHADE, &color_inner_compressed[0]); - _color_shade (&bg_color, INNER_COMPRESSED_END_SHADE, &color_inner_compressed[1]); - - double MIDDLE_END[] = {color_middle[0].r, color_middle[0].g, color_middle[0].b, 1.0f}; - double MIDDLE_START[] = {color_middle[1].r, color_middle[1].g, color_middle[1].b, 1.0f}; - double MIDDLE_END_PRELIGHT[] = {color_middle_prelight[0].r, color_middle_prelight[0].g, color_middle_prelight[0].b, 1.0f}; - double MIDDLE_START_PRELIGHT[] = {color_middle_prelight[1].r, color_middle_prelight[1].g, color_middle_prelight[1].b, 1.0f}; - double OUTER_END[] = {color_outer[0].r, color_outer[0].g, color_outer[0].b, 1.0f}; - double OUTER_START[] = {color_outer[1].r, color_outer[1].g, color_outer[1].b, 1.0f}; - double OUTER_END_PRELIGHT[] = {color_outer_prelight[0].r, color_outer_prelight[0].g, color_outer_prelight[0].b, 1.0f}; - double OUTER_START_PRELIGHT[] = {color_outer_prelight[1].r, color_outer_prelight[1].g, color_outer_prelight[1].b, 1.0f}; - double SHADOW_BUTTON[] = {color_button_shadow.r, color_button_shadow.g, color_button_shadow.b, 0.3f}; - double OUTER_PLAY_END[] = {color_play_outer[0].r, color_play_outer[0].g, color_play_outer[0].b, 1.0f}; - double OUTER_PLAY_START[] = {color_play_outer[1].r, color_play_outer[1].g, color_play_outer[1].b, 1.0f}; - double OUTER_PLAY_END_PRELIGHT[] = {color_play_outer_prelight[0].r, color_play_outer_prelight[0].g, color_play_outer_prelight[0].b, 1.0f}; - double OUTER_PLAY_START_PRELIGHT[] = {color_play_outer_prelight[1].r, color_play_outer_prelight[1].g, color_play_outer_prelight[1].b, 1.0f}; - double BUTTON_END[] = {color_button[0].r, color_button[0].g, color_button[0].b, 1.0f}; - double BUTTON_START[] = {color_button[1].r, color_button[1].g, color_button[1].b, 1.0f}; - double BUTTON_SHADOW[] = {color_button[2].r, color_button[2].g, color_button[2].b, 0.75f}; - double BUTTON_SHADOW_FOCUS[] = {color_button[3].r, color_button[3].g, color_button[3].b, 1.0f}; - double INNER_COMPRESSED_END[] = {color_inner_compressed[1].r, color_inner_compressed[1].g, color_inner_compressed[1].b, 1.0f}; - double INNER_COMPRESSED_START[] = {color_inner_compressed[0].r, color_inner_compressed[0].g, color_inner_compressed[0].b, 1.0f}; - - - draw_gradient (cr, - X, - Y, - RECT_WIDTH, - OUTER_RADIUS, - OUTER_START, - OUTER_END); - - draw_gradient (cr, - X, - Y + 1, - RECT_WIDTH - 2, - MIDDLE_RADIUS, - MIDDLE_START, - MIDDLE_END); - - draw_gradient (cr, - X, - Y + 2, - RECT_WIDTH - 4, - MIDDLE_RADIUS, - MIDDLE_START, - MIDDLE_END); - - //prev/next button - if(priv->current_command == TRANSPORT_ACTION_PREVIOUS) - { - draw_gradient (cr, - X, - Y, - RECT_WIDTH/2, - OUTER_RADIUS, - OUTER_END, - OUTER_START); - - draw_gradient (cr, - X, - Y + 1, - RECT_WIDTH/2, - MIDDLE_RADIUS, - INNER_COMPRESSED_START, - INNER_COMPRESSED_END); - - draw_gradient (cr, - X, - Y + 2, - RECT_WIDTH/2, - MIDDLE_RADIUS, - INNER_COMPRESSED_START, - INNER_COMPRESSED_END); - } - else if(priv->current_command == TRANSPORT_ACTION_NEXT) - { - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y, - RECT_WIDTH/2, - OUTER_RADIUS, - OUTER_END, - OUTER_START); - - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y + 1, - (RECT_WIDTH - 4.5)/2, - MIDDLE_RADIUS, - INNER_COMPRESSED_START, - INNER_COMPRESSED_END); - - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y + 2, - (RECT_WIDTH - 7)/2, - MIDDLE_RADIUS, - INNER_COMPRESSED_START, - INNER_COMPRESSED_END); - } - else if (priv->motion_event == TRANSPORT_ACTION_PREVIOUS) - { - draw_gradient (cr, - X, - Y, - RECT_WIDTH/2, - OUTER_RADIUS, - OUTER_START_PRELIGHT, - OUTER_END_PRELIGHT); - - draw_gradient (cr, - X, - Y + 1, - RECT_WIDTH/2, - MIDDLE_RADIUS, - MIDDLE_START_PRELIGHT, - MIDDLE_END_PRELIGHT); - - draw_gradient (cr, - X, - Y + 2, - RECT_WIDTH/2, - MIDDLE_RADIUS, - MIDDLE_START_PRELIGHT, - MIDDLE_END_PRELIGHT); - } - else if (priv->motion_event == TRANSPORT_ACTION_NEXT) - { - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y, - RECT_WIDTH/2, - OUTER_RADIUS, - OUTER_START_PRELIGHT, - OUTER_END_PRELIGHT); - - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y + 1, - (RECT_WIDTH - 4.5)/2, - MIDDLE_RADIUS, - MIDDLE_START_PRELIGHT, - MIDDLE_END_PRELIGHT); - - draw_gradient (cr, - RECT_WIDTH / 2 + X, - Y + 2, - (RECT_WIDTH - 7)/2, - MIDDLE_RADIUS, - MIDDLE_START_PRELIGHT, - MIDDLE_END_PRELIGHT); - } - - // play/pause shadow - if(priv->current_command != TRANSPORT_ACTION_PLAY_PAUSE) - { - cairo_save (cr); - cairo_rectangle (cr, X, Y, RECT_WIDTH, MIDDLE_RADIUS*2); - cairo_clip (cr); - - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f - 1.0f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) - 1.0f, - CIRCLE_RADIUS + 1.0f, - SHADOW_BUTTON, - SHADOW_BUTTON); - - cairo_restore (cr); - } - - // play/pause button - if(priv->current_command == TRANSPORT_ACTION_PLAY_PAUSE) - { - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) , - CIRCLE_RADIUS, - OUTER_PLAY_END, - OUTER_PLAY_START); - - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f, - CIRCLE_RADIUS - 1.25, - INNER_COMPRESSED_START, - INNER_COMPRESSED_END); - } - else if (priv->motion_event == TRANSPORT_ACTION_PLAY_PAUSE) - { - /* this subtle offset is to fix alpha borders, should be removed once this draw routine will be refactored */ - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 0.1, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 0.1, - CIRCLE_RADIUS - 0.1, - OUTER_PLAY_START_PRELIGHT, - OUTER_PLAY_END_PRELIGHT); - - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f, - CIRCLE_RADIUS - 1.25, - MIDDLE_START_PRELIGHT, - MIDDLE_END_PRELIGHT); - } - else - { - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)), - CIRCLE_RADIUS, - OUTER_PLAY_START, - OUTER_PLAY_END); - - draw_circle (cr, - X + RECT_WIDTH / 2.0f - 2.0f * OUTER_RADIUS - 5.5f + 1.25f, - Y - ((CIRCLE_RADIUS - OUTER_RADIUS)) + 1.25f, - CIRCLE_RADIUS - 1.25, - MIDDLE_START, - MIDDLE_END); - } - - // draw previous-button drop-shadow - if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_PREVIOUS) - { - _setup (&cr_surf, &surf, PREV_WIDTH+6, PREV_HEIGHT+6); - _mask_prev (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_SHADOW_FOCUS, - BUTTON_SHADOW_FOCUS, - FALSE); - _surface_blur (surf, 3); - _finalize_repaint (cr, &cr_surf, &surf, PREV_X, PREV_Y + 0.5f, 3); - } - else - { - _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT); - _mask_prev (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_SHADOW, - BUTTON_SHADOW, - FALSE); - _surface_blur (surf, 1); - _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y + 1.0f); - } - - // draw previous-button - _setup (&cr_surf, &surf, PREV_WIDTH, PREV_HEIGHT); - _mask_prev (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (PREV_HEIGHT - TRI_HEIGHT) / 2.0f, - (PREV_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_START, - BUTTON_END, - FALSE); - _finalize (cr, &cr_surf, &surf, PREV_X, PREV_Y); - - // draw next-button drop-shadow - if (priv->has_focus && priv->key_event == TRANSPORT_ACTION_NEXT) - { - _setup (&cr_surf, &surf, NEXT_WIDTH+6, NEXT_HEIGHT+6); - _mask_next (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_SHADOW_FOCUS, - BUTTON_SHADOW_FOCUS, - FALSE); - _surface_blur (surf, 3); - _finalize_repaint (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 0.5f, 3); - } - else - { - _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT); - _mask_next (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_SHADOW, - BUTTON_SHADOW, - FALSE); - _surface_blur (surf, 1); - _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y + 1.0f); - } - - // draw next-button - _setup (&cr_surf, &surf, NEXT_WIDTH, NEXT_HEIGHT); - _mask_next (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - TRI_WIDTH, - TRI_HEIGHT, - TRI_OFFSET); - _fill (cr_surf, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (NEXT_HEIGHT - TRI_HEIGHT) / 2.0f, - (NEXT_WIDTH - (2.0f * TRI_WIDTH - TRI_OFFSET)) / 2.0f, - (double) TRI_HEIGHT, - BUTTON_START, - BUTTON_END, - FALSE); - _finalize (cr, &cr_surf, &surf, NEXT_X, NEXT_Y); - - // draw pause-button drop-shadow - if(priv->current_state == TRANSPORT_STATE_PLAYING) - { - if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION || - priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE)) - { - _setup (&cr_surf, &surf, PAUSE_WIDTH+6, PAUSE_HEIGHT+6); - _mask_pause (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - BAR_WIDTH, - BAR_HEIGHT - 2.0f * BAR_WIDTH, - BAR_OFFSET); - _fill (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (double) BAR_HEIGHT, - BUTTON_SHADOW_FOCUS, - BUTTON_SHADOW_FOCUS, - TRUE); - _surface_blur (surf, 3); - _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 0.5f, 3); - } - else - { - _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT); - _mask_pause (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - BAR_WIDTH, - BAR_HEIGHT - 2.0f * BAR_WIDTH, - BAR_OFFSET); - _fill (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (double) BAR_HEIGHT, - BUTTON_SHADOW, - BUTTON_SHADOW, - TRUE); - _surface_blur (surf, 1); - _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y + 1.0f); - } - - // draw pause-button - _setup (&cr_surf, &surf, PAUSE_WIDTH, PAUSE_HEIGHT); - _mask_pause (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - BAR_WIDTH, - BAR_HEIGHT - 2.0f * BAR_WIDTH, - BAR_OFFSET); - _fill (cr_surf, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (PAUSE_HEIGHT - BAR_HEIGHT) / 2.0f, - (PAUSE_WIDTH - (2.0f * BAR_WIDTH + BAR_OFFSET)) / 2.0f, - (double) BAR_HEIGHT, - BUTTON_START, - BUTTON_END, - TRUE); - _finalize (cr, &cr_surf, &surf, PAUSE_X, PAUSE_Y); - } - else if(priv->current_state == TRANSPORT_STATE_PAUSED) - { - if (priv->has_focus && (priv->key_event == TRANSPORT_ACTION_NO_ACTION || - priv->key_event == TRANSPORT_ACTION_PLAY_PAUSE)) - { - _setup (&cr_surf, &surf, PLAY_WIDTH+6, PLAY_HEIGHT+6); - _mask_play (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING)); - _fill (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING), - BUTTON_SHADOW_FOCUS, - BUTTON_SHADOW_FOCUS, - FALSE); - _surface_blur (surf, 3); - _finalize_repaint (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y + 0.5f, 3); - } - else - { - _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT); - _mask_play (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING)); - _fill (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING), - BUTTON_SHADOW, - BUTTON_SHADOW, - FALSE); - _surface_blur (surf, 1); - _finalize (cr, &cr_surf, &surf, PAUSE_X-0.75f, PAUSE_Y + 1.0f); - } - - // draw play-button - _setup (&cr_surf, &surf, PLAY_WIDTH, PLAY_HEIGHT); - cairo_set_line_width (cr, 10.5); - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); - _mask_play (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING)); - _fill (cr_surf, - PLAY_PADDING, - PLAY_PADDING, - PLAY_WIDTH - (2*PLAY_PADDING), - PLAY_HEIGHT - (2*PLAY_PADDING), - BUTTON_START, - BUTTON_END, - FALSE); - _finalize (cr, &cr_surf, &surf, PAUSE_X-0.5f, PAUSE_Y); - } - #if GTK_CHECK_VERSION(3, 0, 0) - else if(priv->current_state == TRANSPORT_STATE_LAUNCHING) - { - // the spinner is not aligned, why? because the play button has odd width/height numbers - gtk_render_activity (spinner_style_context, cr, 106, 6, 30, 30); - } - #endif - return FALSE; -} - -static void -transport_widget_set_twin_item(TransportWidget* self, - DbusmenuMenuitem* twin_item) -{ - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(self); - priv->twin_item = twin_item; - g_signal_connect(G_OBJECT(priv->twin_item), "property-changed", - G_CALLBACK(transport_widget_property_update), self); - gint initial_state = dbusmenu_menuitem_property_get_int (twin_item, - DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE ); - //g_debug("TRANSPORT WIDGET - INITIAL UPDATE = %i", initial_state); - transport_widget_toggle_play_pause (self, - (TransportState)initial_state); -} - -/** -* transport_widget_update_state() -* Callback for updates from the other side of dbus -**/ -static void -transport_widget_property_update(DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata) -{ - //g_debug("transport_widget_update_state - with property %s", property); - TransportWidget* bar = (TransportWidget*)userdata; - g_return_if_fail(IS_TRANSPORT_WIDGET(bar)); - TransportWidgetPrivate* priv = TRANSPORT_WIDGET_GET_PRIVATE(bar); - - if(g_ascii_strcasecmp(DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE, property) == 0) - { - TransportState new_state = (TransportState)g_variant_get_int32(value); - //g_debug("transport_widget_update_state - with value %i", new_state); - if (new_state == TRANSPORT_STATE_LAUNCHING){ - #if GTK_CHECK_VERSION(3, 0, 0) - gtk_style_context_set_state (spinner_style_context, GTK_STATE_FLAG_ACTIVE); - #endif - - priv->current_state = TRANSPORT_STATE_LAUNCHING; - g_debug("TransportWidget::toggle play state : %i", priv->current_state); - } - else{ - transport_widget_toggle_play_pause(bar, new_state); - } - } -} - - -/** -* transport_widget_new: -* @returns: a new #TransportWidget. -**/ -GtkWidget* -transport_widget_new ( DbusmenuMenuitem *item ) -{ - GtkWidget* widget = g_object_new(TRANSPORT_WIDGET_TYPE, NULL); - gtk_widget_set_app_paintable (widget, TRUE); - transport_widget_set_twin_item((TransportWidget*)widget, item); - return widget; -} - diff --git a/src/transport-widget.h b/src/transport-widget.h deleted file mode 100644 index b68845f..0000000 --- a/src/transport-widget.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 . -*/ -#ifndef __TRANSPORT_WIDGET_H__ -#define __TRANSPORT_WIDGET_H__ - -#include -#include - -#include "common-defs.h" - -G_BEGIN_DECLS - -#define TRANSPORT_WIDGET_TYPE (transport_widget_get_type ()) -#define TRANSPORT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRANSPORT_WIDGET_TYPE, TransportWidget)) -#define TRANSPORT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TRANSPORT_WIDGET_TYPE, TransportWidgetClass)) -#define IS_TRANSPORT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRANSPORT_WIDGET_TYPE)) -#define IS_TRANSPORT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TRANSPORT_WIDGET_TYPE)) -#define TRANSPORT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TRANSPORT_WIDGET_TYPE, TransportWidgetClass)) - -typedef struct _TransportWidget TransportWidget; -typedef struct _TransportWidgetClass TransportWidgetClass; - - -struct _TransportWidgetClass { - GtkMenuItemClass parent_class; -}; - -struct _TransportWidget { - GtkMenuItem parent; -}; - -typedef struct -{ - double r; - double g; - double b; -} CairoColorRGB; - - -void _color_shade (const CairoColorRGB *a, float k, CairoColorRGB *b); -GType transport_widget_get_type (void); -GtkWidget* transport_widget_new (DbusmenuMenuitem *item); -void transport_widget_react_to_key_press_event (TransportWidget* widget, - TransportAction transport_event); -void transport_widget_react_to_key_release_event (TransportWidget* widget, - TransportAction transport_event); -gboolean transport_widget_is_selected (TransportWidget* widget); -G_END_DECLS - -#endif - diff --git a/src/voip-input-widget.c b/src/voip-input-widget.c deleted file mode 100644 index 03879e7..0000000 --- a/src/voip-input-widget.c +++ /dev/null @@ -1,284 +0,0 @@ - -/* -Copyright 2011 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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include "voip-input-widget.h" -#include "common-defs.h" -#include - -typedef struct _VoipInputWidgetPrivate VoipInputWidgetPrivate; - -struct _VoipInputWidgetPrivate -{ - DbusmenuMenuitem* twin_item; - GtkWidget* ido_voip_input_slider; - gboolean grabbed; -}; - -#define VOIP_INPUT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetPrivate)) - -/* Prototypes */ -static void voip_input_widget_class_init (VoipInputWidgetClass *klass); -static void voip_input_widget_init (VoipInputWidget *self); -static void voip_input_widget_dispose (GObject *object); -static void voip_input_widget_finalize (GObject *object); -static void voip_input_widget_set_twin_item( VoipInputWidget* self, - DbusmenuMenuitem* twin_item); -static void voip_input_widget_property_update( DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata ); - -static gboolean voip_input_widget_change_value_cb (GtkRange *range, - GtkScrollType scroll, - gdouble value, - gpointer user_data); -static gboolean voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data); -static void voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data); -static void voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data); -static void voip_input_widget_parent_changed (GtkWidget *widget, gpointer user_data); - -G_DEFINE_TYPE (VoipInputWidget, voip_input_widget, G_TYPE_OBJECT); - - -static void -voip_input_widget_class_init (VoipInputWidgetClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (VoipInputWidgetPrivate)); - - gobject_class->dispose = voip_input_widget_dispose; - gobject_class->finalize = voip_input_widget_finalize; -} - -static void -voip_input_widget_init (VoipInputWidget *self) -{ - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self); - - priv->ido_voip_input_slider = ido_scale_menu_item_new_with_range ("VOLUME", IDO_RANGE_STYLE_DEFAULT, 0, 0, 100, 1); - g_object_ref (priv->ido_voip_input_slider); - ido_scale_menu_item_set_primary_label (IDO_SCALE_MENU_ITEM(priv->ido_voip_input_slider), "VOIP"); - - ido_scale_menu_item_set_style (IDO_SCALE_MENU_ITEM (priv->ido_voip_input_slider), IDO_SCALE_MENU_ITEM_STYLE_IMAGE); - g_object_set(priv->ido_voip_input_slider, "reverse-scroll-events", TRUE, NULL); - - g_signal_connect (priv->ido_voip_input_slider, - "notify::parent", G_CALLBACK (voip_input_widget_parent_changed), - NULL); - - GtkWidget* voip_input_widget = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider); - - g_signal_connect(voip_input_widget, "change-value", G_CALLBACK(voip_input_widget_change_value_cb), self); - g_signal_connect(voip_input_widget, "value-changed", G_CALLBACK(voip_input_widget_value_changed_cb), self); - g_signal_connect(priv->ido_voip_input_slider, "slider-grabbed", G_CALLBACK(voip_input_widget_slider_grabbed), self); - g_signal_connect(priv->ido_voip_input_slider, "slider-released", G_CALLBACK(voip_input_widget_slider_released), self); - - GtkWidget* primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider); - GIcon * primary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone-low-zero-panel"); - gtk_image_set_from_gicon(GTK_IMAGE(primary_image), primary_gicon, GTK_ICON_SIZE_MENU); - g_object_unref(primary_gicon); - - GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)priv->ido_voip_input_slider); - GIcon * secondary_gicon = g_themed_icon_new_with_default_fallbacks("audio-input-microphone-high-panel"); - gtk_image_set_from_gicon(GTK_IMAGE(secondary_image), secondary_gicon, GTK_ICON_SIZE_MENU); - g_object_unref(secondary_gicon); - - GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (voip_input_widget)); - gtk_adjustment_set_step_increment(adj, 4); -} - -static void -voip_input_widget_dispose (GObject *object) -{ - G_OBJECT_CLASS (voip_input_widget_parent_class)->dispose (object); -} - -static void -voip_input_widget_finalize (GObject *object) -{ - G_OBJECT_CLASS (voip_input_widget_parent_class)->finalize (object); -} - -static void -voip_input_widget_property_update (DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata) -{ - g_return_if_fail (IS_VOIP_INPUT_WIDGET (userdata)); - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(userdata); - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem); - if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL, property) == 0){ - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)); - if (priv->grabbed == FALSE){ - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider); - GtkRange *range = (GtkRange*)slider; - gdouble update = g_variant_get_double (value); - //g_debug("volume-widget - update level with value %f", update); - gtk_range_set_value(range, update); - } - } - if(g_ascii_strcasecmp(DBUSMENU_VOIP_INPUT_MENUITEM_MUTE, property) == 0){ - if(priv->grabbed == FALSE){ - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)); - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider); - GtkRange *range = (GtkRange*)slider; - gint update = g_variant_get_int32 (value); - gdouble level; - if (update == 1){ - level = 0; - } - else{ - GVariant* variant = dbusmenu_menuitem_property_get_variant (priv->twin_item, - DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL); - g_return_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE)); - level = g_variant_get_double (variant); - } - gtk_range_set_value(range, level); - - g_debug ("voip-item-widget - update mute with value %i", update); - } - } -} - -static void -voip_input_widget_set_twin_item (VoipInputWidget* self, - DbusmenuMenuitem* twin_item) -{ - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self); - priv->twin_item = twin_item; - g_object_ref(priv->twin_item); - g_signal_connect(G_OBJECT(twin_item), "property-changed", - G_CALLBACK(voip_input_widget_property_update), self); - gdouble initial_level = g_variant_get_double (dbusmenu_menuitem_property_get_variant(twin_item, - DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL)); - //g_debug("voip_input_widget_set_twin_item initial level = %f", initial_level); - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider); - GtkRange *range = (GtkRange*)slider; - gtk_range_set_value(range, initial_level); - - gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item, - DBUSMENU_VOIP_INPUT_MENUITEM_MUTE)); - if (mute == 1){ - gtk_range_set_value (range, 0.0); - } -} - -static gboolean -voip_input_widget_change_value_cb (GtkRange *range, - GtkScrollType scroll, - gdouble new_value, - gpointer user_data) -{ - g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE); - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data); - voip_input_widget_update(mitem, new_value); - return FALSE; -} - - -/** - * We only want this callback to catch mouse icon press events which set the - * slider to 0 or 100. Ignore all other events including the new Mute behaviour - * (slider to go to 0 on mute without setting the level to 0 and return to - * previous level on unmute) - **/ -static gboolean -voip_input_widget_value_changed_cb(GtkRange *range, gpointer user_data) -{ - g_return_val_if_fail (IS_VOIP_INPUT_WIDGET (user_data), FALSE); - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data); - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem); - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_voip_input_slider); - gdouble current_value = CLAMP(gtk_range_get_value(GTK_RANGE(slider)), 0, 100); - - gint mute = g_variant_get_int32 (dbusmenu_menuitem_property_get_variant (priv->twin_item, - DBUSMENU_VOIP_INPUT_MENUITEM_MUTE)); - if ((current_value == 0 && mute != 1) || current_value == 100 ){ - voip_input_widget_update(mitem, current_value); - } - return FALSE; -} - -void -voip_input_widget_update(VoipInputWidget* self, gdouble update) -{ - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self); - gdouble clamped = CLAMP(update, 0, 100); - GVariant* new_volume = g_variant_new_double(clamped); - dbusmenu_menuitem_handle_event (priv->twin_item, "update", new_volume, 0); -} - -GtkWidget* -voip_input_widget_get_ido_slider(VoipInputWidget* self) -{ - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(self); - return priv->ido_voip_input_slider; -} - -static void -voip_input_widget_parent_changed (GtkWidget *widget, - gpointer user_data) -{ - gtk_widget_set_size_request (widget, 200, -1); - //g_debug("voip_input_widget_parent_changed"); -} - -static void -voip_input_widget_slider_grabbed(GtkWidget *widget, gpointer user_data) -{ - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data); - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem); - priv->grabbed = TRUE; -} - -static void -voip_input_widget_slider_released(GtkWidget *widget, gpointer user_data) -{ - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(user_data); - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem); - priv->grabbed = FALSE; -} - -void -voip_input_widget_tidy_up (GtkWidget *widget) -{ - VoipInputWidget* mitem = VOIP_INPUT_WIDGET(widget); - VoipInputWidgetPrivate * priv = VOIP_INPUT_WIDGET_GET_PRIVATE(mitem); - gtk_widget_destroy (priv->ido_voip_input_slider); -} - -/** - * voip_input_widget_new: - * @returns: a new #VoipInputWidget. - **/ -GtkWidget* -voip_input_widget_new(DbusmenuMenuitem *item) -{ - GtkWidget* widget = g_object_new(VOIP_INPUT_WIDGET_TYPE, NULL); - voip_input_widget_set_twin_item((VoipInputWidget*)widget, item); - return widget; -} - - diff --git a/src/voip-input-widget.h b/src/voip-input-widget.h deleted file mode 100644 index 72da80c..0000000 --- a/src/voip-input-widget.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2011 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 . -*/ -#ifndef __VOIP_INPUT_WIDGET_H__ -#define __VOIP_INPUT_WIDGET_H__ - -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define VOIP_INPUT_WIDGET_TYPE (voip_input_widget_get_type ()) -#define VOIP_INPUT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VOIP_INPUT_WIDGET_TYPE, VoipInputWidget)) -#define VOIP_INPUT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetClass)) -#define IS_VOIP_INPUT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VOIP_INPUT_WIDGET_TYPE)) -#define IS_VOIP_INPUT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VOIP_INPUT_WIDGET_TYPE)) -#define VOIP_INPUT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VOIP_INPUT_WIDGET_TYPE, VoipInputWidgetClass)) - -typedef struct _VoipInputWidget VoipInputWidget; -typedef struct _VoipInputWidgetClass VoipInputWidgetClass; - -struct _VoipInputWidgetClass { - GObjectClass parent_class; -}; - -struct _VoipInputWidget { - GObject parent; -}; - -GType voip_input_widget_get_type (void) G_GNUC_CONST; -GtkWidget* voip_input_widget_new(DbusmenuMenuitem* twin_item); -GtkWidget* voip_input_widget_get_ido_slider(VoipInputWidget* self); -void voip_input_widget_update(VoipInputWidget* self, gdouble update); -void voip_input_widget_tidy_up (GtkWidget *widget); - -G_END_DECLS - -#endif - diff --git a/src/volume-widget.c b/src/volume-widget.c deleted file mode 100644 index 1258c20..0000000 --- a/src/volume-widget.c +++ /dev/null @@ -1,338 +0,0 @@ - -/* -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 . -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include "volume-widget.h" -#include "common-defs.h" -#include -#include "indicator-sound.h" - -typedef struct _VolumeWidgetPrivate VolumeWidgetPrivate; - -struct _VolumeWidgetPrivate -{ - DbusmenuMenuitem* twin_item; - GtkWidget* ido_volume_slider; - gboolean grabbed; - IndicatorObject* indicator; -}; - -#define VOLUME_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOLUME_WIDGET_TYPE, VolumeWidgetPrivate)) - -/* Prototypes */ -static void volume_widget_class_init (VolumeWidgetClass *klass); -static void volume_widget_init (VolumeWidget *self); -static void volume_widget_dispose (GObject *object); -static void volume_widget_finalize (GObject *object); -static void volume_widget_set_twin_item( VolumeWidget* self, - DbusmenuMenuitem* twin_item); -static void volume_widget_property_update( DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata ); - -static gboolean volume_widget_change_value_cb (GtkRange *range, - GtkScrollType scroll, - gdouble value, - gpointer user_data); -static void volume_widget_primary_clicked(GtkWidget *widget, gpointer user_data); -static void volume_widget_secondary_clicked(GtkWidget *widget, gpointer user_data); -static void volume_widget_slider_grabbed(GtkWidget *widget, gpointer user_data); -static void volume_widget_slider_released(GtkWidget *widget, gpointer user_data); -static void volume_widget_parent_changed (GtkWidget *widget, gpointer user_data); - -G_DEFINE_TYPE (VolumeWidget, volume_widget, G_TYPE_OBJECT); - - -static void -volume_widget_class_init (VolumeWidgetClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (VolumeWidgetPrivate)); - - gobject_class->dispose = volume_widget_dispose; - gobject_class->finalize = volume_widget_finalize; -} - -static void -volume_widget_init (VolumeWidget *self) -{ - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(self); - - priv->ido_volume_slider = ido_scale_menu_item_new_with_range ("VOLUME", IDO_RANGE_STYLE_DEFAULT, 0, 0, 100, 1); - g_object_ref (priv->ido_volume_slider); - ido_scale_menu_item_set_primary_label (IDO_SCALE_MENU_ITEM(priv->ido_volume_slider), "VOLUME"); - ido_scale_menu_item_set_style (IDO_SCALE_MENU_ITEM (priv->ido_volume_slider), IDO_SCALE_MENU_ITEM_STYLE_IMAGE); - g_object_set(priv->ido_volume_slider, "reverse-scroll-events", TRUE, NULL); - - g_signal_connect (priv->ido_volume_slider, - "notify::parent", G_CALLBACK (volume_widget_parent_changed), - NULL); - - GtkWidget* volume_widget = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); - - g_signal_connect(volume_widget, "change-value", G_CALLBACK(volume_widget_change_value_cb), self); - g_signal_connect(priv->ido_volume_slider, "primary-clicked", G_CALLBACK(volume_widget_primary_clicked), self); - g_signal_connect(priv->ido_volume_slider, "secondary-clicked", G_CALLBACK(volume_widget_secondary_clicked), self); - g_signal_connect(priv->ido_volume_slider, "slider-grabbed", G_CALLBACK(volume_widget_slider_grabbed), self); - g_signal_connect(priv->ido_volume_slider, "slider-released", G_CALLBACK(volume_widget_slider_released), self); - - GtkWidget* primary_image = ido_scale_menu_item_get_primary_image((IdoScaleMenuItem*)priv->ido_volume_slider); - GIcon * primary_gicon = g_themed_icon_new_with_default_fallbacks("audio-volume-low-zero-panel"); - gtk_image_set_from_gicon(GTK_IMAGE(primary_image), primary_gicon, GTK_ICON_SIZE_MENU); - g_object_unref(primary_gicon); - - GtkWidget* secondary_image = ido_scale_menu_item_get_secondary_image((IdoScaleMenuItem*)priv->ido_volume_slider); - GIcon * secondary_gicon = g_themed_icon_new_with_default_fallbacks("audio-volume-high-panel"); - gtk_image_set_from_gicon(GTK_IMAGE(secondary_image), secondary_gicon, GTK_ICON_SIZE_MENU); - g_object_unref(secondary_gicon); - - GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (volume_widget)); - gtk_adjustment_set_step_increment(adj, 4); -} - -static void -volume_widget_dispose (GObject *object) -{ - G_OBJECT_CLASS (volume_widget_parent_class)->dispose (object); -} - -static void -volume_widget_finalize (GObject *object) -{ - G_OBJECT_CLASS (volume_widget_parent_class)->finalize (object); -} - -static void -volume_widget_property_update( DbusmenuMenuitem* item, gchar* property, - GVariant* value, gpointer userdata) -{ - g_return_if_fail (IS_VOLUME_WIDGET(userdata)); - VolumeWidget* mitem = VOLUME_WIDGET(userdata); - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(mitem); - - if(g_ascii_strcasecmp(DBUSMENU_VOLUME_MENUITEM_LEVEL, property) == 0){ - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE) ); - gdouble update = g_variant_get_double (value); - - if(priv->grabbed == FALSE){ - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); - GtkRange *range = (GtkRange*)slider; - gtk_range_set_value(range, update); -/* - g_debug ("volume-widget::volume_widget_property_update - volume - value %f", update); - AtkObject* atk_object; - atk_object = gtk_widget_get_accessible (priv->ido_volume_slider); - if (atk_object != NULL){ - atk_object_set_name (atk_object, desc); - - }*/ - } - gchar* desc = g_strdup_printf(_("Volume (%'.0f%%)"), - update); - dbusmenu_menuitem_property_set (priv->twin_item, - DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC, - desc); - g_free (desc); - update_accessible_desc(priv->indicator); - } - else if(g_ascii_strcasecmp(DBUSMENU_VOLUME_MENUITEM_MUTE, property) == 0){ - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)); - if(priv->grabbed == FALSE){ - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); - GtkRange *range = (GtkRange*)slider; - gboolean update = g_variant_get_boolean (value); - gdouble level; - - if (update == TRUE){ - level = 0; - } - else{ - GVariant* variant = dbusmenu_menuitem_property_get_variant (priv->twin_item, - DBUSMENU_VOLUME_MENUITEM_LEVEL); -/* - g_debug ("variant for the volume - is it null = %i", variant == NULL); -*/ - g_return_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE) ); - - level = g_variant_get_double (variant); - } -/* - g_debug ("volume-widget::volume_widget_property_update - mute - value %i and level = %f", update, level); -*/ - gtk_range_set_value(range, level); - } - } -} - -static void -volume_widget_set_twin_item(VolumeWidget* self, - DbusmenuMenuitem* twin_item) -{ - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(self); - priv->twin_item = twin_item; - g_object_ref(priv->twin_item); - g_signal_connect(G_OBJECT(twin_item), "property-changed", - G_CALLBACK(volume_widget_property_update), self); - gdouble initial_level = g_variant_get_double (dbusmenu_menuitem_property_get_variant(twin_item, - DBUSMENU_VOLUME_MENUITEM_LEVEL)); - gboolean initial_mute = g_variant_get_boolean (dbusmenu_menuitem_property_get_variant(twin_item, - DBUSMENU_VOLUME_MENUITEM_MUTE)); - - //g_debug("volume_widget_set_twin_item initial level = %f", initial_level); - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); - GtkRange *range = (GtkRange*)slider; - if(initial_mute == TRUE){ - initial_level = 0; - } - gtk_range_set_value(range, initial_level); - gchar* desc = g_strdup_printf(_("Volume (%'.0f%%)"), - initial_level); - dbusmenu_menuitem_property_set (priv->twin_item, - DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC, - desc); - g_free (desc); - -} - -static gboolean -volume_widget_change_value_cb (GtkRange *range, - GtkScrollType scroll, - gdouble new_value, - gpointer user_data) -{ - g_return_val_if_fail (IS_VOLUME_WIDGET (user_data), FALSE); - VolumeWidget* mitem = VOLUME_WIDGET(user_data); - -/* - g_debug ("changed value %f", new_value); -*/ - - volume_widget_update(mitem, new_value, "change-value"); - return FALSE; -} - -void -volume_widget_update(VolumeWidget* self, gdouble update, gchar* label) -{ - gchar* source = NULL; - source = label; - if (label == NULL){ - source = "v widget update"; - } - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(self); - gdouble clamped = CLAMP(update, 0, 100); - GVariant* new_volume = g_variant_new_double(clamped); - dbusmenu_menuitem_handle_event (priv->twin_item, source, new_volume, 0); -} - -static void -volume_widget_update_from_scale (VolumeWidget *self) -{ - g_return_if_fail (IS_VOLUME_WIDGET (self)); - - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(self); - GtkWidget *slider = ido_scale_menu_item_get_scale((IdoScaleMenuItem*)priv->ido_volume_slider); - const gdouble current_value = CLAMP(gtk_range_get_value(GTK_RANGE(slider)), 0, 100); - g_debug ("%s - setting value to %.0f", G_STRFUNC, current_value); - volume_widget_update (self, current_value, "value-changed"); -} - -static void -volume_widget_primary_clicked (GtkWidget *widget G_GNUC_UNUSED, gpointer user_data) -{ - volume_widget_update_from_scale (VOLUME_WIDGET(user_data)); -} - -static void -volume_widget_secondary_clicked(GtkWidget *widget, gpointer user_data) -{ - volume_widget_update_from_scale (VOLUME_WIDGET(user_data)); -} - -GtkWidget* -volume_widget_get_ido_slider(VolumeWidget* self) -{ - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(self); - return priv->ido_volume_slider; -} - -static void -volume_widget_parent_changed (GtkWidget *widget, - gpointer user_data) -{ - gtk_widget_set_size_request (widget, 200, -1); - //g_debug("volume_widget_parent_changed"); -} - -static void -volume_widget_slider_grabbed(GtkWidget *widget, gpointer user_data) -{ - VolumeWidget* mitem = VOLUME_WIDGET(user_data); - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(mitem); - priv->grabbed = TRUE; -} - -static void -volume_widget_slider_released(GtkWidget *widget, gpointer user_data) -{ - VolumeWidget* mitem = VOLUME_WIDGET(user_data); - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(mitem); - priv->grabbed = FALSE; -} - -void -volume_widget_tidy_up (GtkWidget *widget) -{ - VolumeWidget* mitem = VOLUME_WIDGET(widget); - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(mitem); - gtk_widget_destroy (priv->ido_volume_slider); -} - -gdouble -volume_widget_get_current_volume ( GtkWidget *widget ) -{ - VolumeWidget* mitem = VOLUME_WIDGET(widget); - VolumeWidgetPrivate * priv = VOLUME_WIDGET_GET_PRIVATE(mitem); - gdouble vol = g_variant_get_double( dbusmenu_menuitem_property_get_variant( priv->twin_item, - DBUSMENU_VOLUME_MENUITEM_LEVEL)); - return vol; -} - -/** - * volume_widget_new: - * @returns: a new #VolumeWidget. - **/ -GtkWidget* -volume_widget_new(DbusmenuMenuitem *item, IndicatorObject* io) -{ - GtkWidget* widget = g_object_new(VOLUME_WIDGET_TYPE, NULL); - VolumeWidgetPrivate* priv = VOLUME_WIDGET_GET_PRIVATE(VOLUME_WIDGET(widget)); - priv->indicator = io; - volume_widget_set_twin_item((VolumeWidget*)widget, item); - return widget; -} - - diff --git a/src/volume-widget.h b/src/volume-widget.h deleted file mode 100644 index 665f39b..0000000 --- a/src/volume-widget.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -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 . -*/ -#ifndef __VOLUME_WIDGET_H__ -#define __VOLUME_WIDGET_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define VOLUME_WIDGET_TYPE (volume_widget_get_type ()) -#define VOLUME_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VOLUME_WIDGET_TYPE, VolumeWidget)) -#define VOLUME_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VOLUME_WIDGET_TYPE, VolumeWidgetClass)) -#define IS_VOLUME_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VOLUME_WIDGET_TYPE)) -#define IS_VOLUME_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VOLUME_WIDGET_TYPE)) -#define VOLUME_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VOLUME_WIDGET_TYPE, VolumeWidgetClass)) - -typedef struct _VolumeWidget VolumeWidget; -typedef struct _VolumeWidgetClass VolumeWidgetClass; - -struct _VolumeWidgetClass { - GObjectClass parent_class; -}; - -struct _VolumeWidget { - GObject parent; -}; - -GType volume_widget_get_type (void) G_GNUC_CONST; -GtkWidget* volume_widget_new(DbusmenuMenuitem *item, IndicatorObject* io); -GtkWidget* volume_widget_get_ido_slider(VolumeWidget* self); -void volume_widget_update(VolumeWidget* self, gdouble update, gchar* label); -void volume_widget_tidy_up (GtkWidget *widget); -gdouble volume_widget_get_current_volume ( GtkWidget *widget ); - -G_END_DECLS - -#endif - -- cgit v1.2.3 From 92c98f341c6b5600d4b5fae8753326c866e40860 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 22 Mar 2013 16:24:06 -0400 Subject: Add service that adheres to the new indicator protocol This is the one that gets built now. It doesn't do anything interesting yet, though. --- src/Makefile.am | 8 ++-- src/com.canonical.sound.indicator | 4 ++ src/main.vala | 5 +++ src/service.vala | 84 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/com.canonical.sound.indicator create mode 100644 src/main.vala create mode 100644 src/service.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a56f82a..39ec6bf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,8 @@ glib_marshal_prefix = _sound_service_marshal # Sound service vala ##################### music_bridge_VALASOURCES = \ + service.vala \ + main.vala \ music-player-bridge.vala \ transport-menu-item.vala \ specific-items-manager.vala \ @@ -44,7 +46,9 @@ music_bridge_VALAFLAGS = \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ --pkg gdk-pixbuf-2.0 \ - --pkg libxml-2.0 + --pkg libxml-2.0 \ + --pkg libpulse \ + --target-glib=2.36 $(MAINTAINER_VALAFLAGS) @@ -60,8 +64,6 @@ music_bridge_vala.stamp $(music_bridge_APIFILES): $(music_bridge_VALASOURCES) ############################### indicator_sound_service_SOURCES = \ common-defs.h \ - sound-service.h \ - sound-service.c \ sound-state.c \ sound-state.h \ pulseaudio-mgr.h \ diff --git a/src/com.canonical.sound.indicator b/src/com.canonical.sound.indicator new file mode 100644 index 0000000..e2f26b0 --- /dev/null +++ b/src/com.canonical.sound.indicator @@ -0,0 +1,4 @@ +[Indicator Service] +Name=indicator-sound +BusName=com.canonical.indicator.sound +ObjectPath=/com/canonical/indicator/sound diff --git a/src/main.vala b/src/main.vala new file mode 100644 index 0000000..1ec7d2c --- /dev/null +++ b/src/main.vala @@ -0,0 +1,5 @@ + +static int main (string[] args) { + var service = new IndicatorSound.Service (); + return service.run (); +} diff --git a/src/service.vala b/src/service.vala new file mode 100644 index 0000000..fc35dd7 --- /dev/null +++ b/src/service.vala @@ -0,0 +1,84 @@ + +public class IndicatorSound.Service { + public Service () { + } + + public int run () { + if (this.loop != null) { + warning ("service is already running"); + return 1; + } + + Bus.own_name (BusType.SESSION, "com.canonical.indicator.sound", BusNameOwnerFlags.NONE, + this.bus_acquired, null, this.name_lost); + + this.loop = new MainLoop (null, false); + this.loop.run (); + + return 0; + } + + const ActionEntry[] action_entries = { + { "root", null, null, "('', 'audio-volume-high-panel', '', true)", null }, + { "mute", activate_mute, null, "false", null }, + { "volume", null, null, "0", volume_changed }, + { "settings", activate_settings, null, null, null } + }; + + MainLoop loop; + SimpleActionGroup actions; + Menu menu; + + void activate_mute (SimpleAction action, Variant? param) { + bool muted = action.get_state ().get_boolean (); + } + + void volume_changed (SimpleAction action, Variant val) { + } + + void activate_settings (SimpleAction action, Variant? param) { + try { + Process.spawn_command_line_async ("gnome-control-center sound"); + } catch (Error e) { + warning ("unable to launch sound settings: %s", e.message); + } + } + + static Menu create_base_menu () { + var submenu = new Menu (); + submenu.append ("Mute", "indicator.mute"); + + var slider = new MenuItem ("null", "indicator.volume"); + slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); + submenu.append_item (slider); + + submenu.append ("Sound Settings…", "indicator.settings"); + + var root = new MenuItem (null, "indicator.root"); + root.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); + root.set_submenu (submenu); + + var menu = new Menu (); + menu.append_item (root); + + return menu; + } + + void bus_acquired (DBusConnection connection, string name) { + this.actions = new SimpleActionGroup (); + this.actions.add_entries (action_entries, this); + + this.menu = create_base_menu (); + + try { + connection.export_action_group ("/com/canonical/indicator/sound", this.actions); + connection.export_menu_model ("/com/canonical/indicator/sound/desktop", this.menu); + } catch (Error e) { + critical ("%s", e.message); + } + } + + void name_lost (DBusConnection connection, string name) { + this.loop.quit (); + } +} -- cgit v1.2.3 From c3d504bc66bbd360ca6c8d5742a121441dacbfe2 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Mar 2013 13:06:26 -0400 Subject: Add VolumeControl and hook up mute and volume actions to it The VolumeControl class originated in the phablet branch of this indicator. --- src/Makefile.am | 3 + src/service.vala | 46 +++++++++--- src/volume-control.vala | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 src/volume-control.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 39ec6bf..2421683 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,7 @@ glib_marshal_prefix = _sound_service_marshal music_bridge_VALASOURCES = \ service.vala \ main.vala \ + volume-control.vala \ music-player-bridge.vala \ transport-menu-item.vala \ specific-items-manager.vala \ @@ -48,6 +49,8 @@ music_bridge_VALAFLAGS = \ --pkg gdk-pixbuf-2.0 \ --pkg libxml-2.0 \ --pkg libpulse \ + --pkg libpulse-mainloop-glib \ + --pkg pulseaudio-mgr \ --target-glib=2.36 $(MAINTAINER_VALAFLAGS) diff --git a/src/service.vala b/src/service.vala index fc35dd7..cdb5b15 100644 --- a/src/service.vala +++ b/src/service.vala @@ -1,6 +1,7 @@ public class IndicatorSound.Service { public Service () { + this.volume_control = new VolumeControl (); } public int run () { @@ -20,21 +21,13 @@ public class IndicatorSound.Service { const ActionEntry[] action_entries = { { "root", null, null, "('', 'audio-volume-high-panel', '', true)", null }, - { "mute", activate_mute, null, "false", null }, - { "volume", null, null, "0", volume_changed }, { "settings", activate_settings, null, null, null } }; MainLoop loop; SimpleActionGroup actions; Menu menu; - - void activate_mute (SimpleAction action, Variant? param) { - bool muted = action.get_state ().get_boolean (); - } - - void volume_changed (SimpleAction action, Variant val) { - } + VolumeControl volume_control; void activate_settings (SimpleAction action, Variant? param) { try { @@ -64,10 +57,45 @@ public class IndicatorSound.Service { return menu; } + Action create_mute_action () { + var mute_action = new SimpleAction.stateful ("mute", null, this.volume_control.mute); + + mute_action.activate.connect ( (action, param) => { + action.change_state (!action.get_state ().get_boolean ()); + }); + + mute_action.change_state.connect ( (action, val) => { + volume_control.set_mute (val.get_boolean ()); + }); + + this.volume_control.notify["mute"].connect ( () => { + mute_action.set_state (this.volume_control.mute); + }); + + return mute_action; + } + + Action create_volume_action () { + var volume_action = new SimpleAction.stateful ("volume", null, this.volume_control.get_volume ()); + + volume_action.change_state.connect ( (action, val) => { + volume_control.set_volume (val.get_double ()); + }); + + this.volume_control.volume_changed.connect ( (volume) => { + volume_action.set_state (volume); + }); + + return volume_action; + } + void bus_acquired (DBusConnection connection, string name) { this.actions = new SimpleActionGroup (); this.actions.add_entries (action_entries, this); + this.actions.add_action (this.create_mute_action ()); + this.actions.add_action (this.create_volume_action ()); + this.menu = create_base_menu (); try { diff --git a/src/volume-control.vala b/src/volume-control.vala new file mode 100644 index 0000000..8c88c92 --- /dev/null +++ b/src/volume-control.vala @@ -0,0 +1,192 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Alberto Ruiz + */ + +using PulseAudio; + +[CCode(cname="pa_cvolume_set", cheader_filename = "pulse/volume.h")] +extern unowned PulseAudio.CVolume? vol_set (PulseAudio.CVolume? cv, uint channels, PulseAudio.Volume v); + +public class VolumeControl : Object +{ + /* this is static to ensure it being freed after @context (loop does not have ref counting) */ + private static PulseAudio.GLibMainLoop loop; + + private PulseAudio.Context context; + private bool _mute = true; + private double _volume = 0.0; + + public signal void ready (); + public signal void volume_changed (double v); + + public VolumeControl () + { + if (loop == null) + loop = new PulseAudio.GLibMainLoop (); + + var props = new Proplist (); + props.sets (Proplist.PROP_APPLICATION_NAME, "Ubuntu Audio Settings"); + props.sets (Proplist.PROP_APPLICATION_ID, "com.canonical.settings.sound"); + props.sets (Proplist.PROP_APPLICATION_ICON_NAME, "multimedia-volume-control"); + props.sets (Proplist.PROP_APPLICATION_VERSION, "0.1"); + + context = new PulseAudio.Context (loop.get_api(), null, props); + + context.set_state_callback (notify_cb); + + if (context.connect(null, Context.Flags.NOFAIL, null) < 0) + { + warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno())); + return; + } + } + + /* PulseAudio logic*/ + private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) + { + if ((t & Context.SubscriptionEventType.FACILITY_MASK) == Context.SubscriptionEventType.SINK) + { + get_properties (); + } + } + + private void sink_info_cb_for_props (Context c, SinkInfo? i, int eol) + { + if (i == null) + return; + + if (_mute != (bool)i.mute) + { + _mute = (bool)i.mute; + this.notify_property ("mute"); + } + + if (_volume != volume_to_double (i.volume.values[0])) + { + _volume = volume_to_double (i.volume.values[0]); + volume_changed (_volume); + } + } + + private void server_info_cb_for_props (Context c, ServerInfo? i) + { + if (i == null) + return; + context.get_sink_info_by_name (i.default_sink_name, sink_info_cb_for_props); + } + + private void get_properties () + { + context.get_server_info (server_info_cb_for_props); + } + + private void notify_cb (Context c) + { + if (c.get_state () == Context.State.READY) + { + c.subscribe (PulseAudio.Context.SubscriptionMask.SINK); + c.set_subscribe_callback (context_events_cb); + get_properties (); + ready (); + } + } + + /* Mute operations */ + public void set_mute (bool mute) + { + if (context.get_state () != Context.State.READY) + { + warning ("Could not mute: PulseAudio server connection is not ready."); + return; + } + + context.get_sink_info_list ((context, sink, eol) => { + if (sink != null) + context.set_sink_mute_by_index (sink.index, mute, null); + }); + } + + public void toggle_mute () + { + this.set_mute (!this._mute); + } + + public bool mute + { + get + { + return this._mute; + } + } + + /* Volume operations */ + private static PulseAudio.Volume double_to_volume (double vol) + { + double tmp = (double)(PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED) * vol; + return (PulseAudio.Volume)tmp + PulseAudio.Volume.MUTED; + } + + private static double volume_to_double (PulseAudio.Volume vol) + { + double tmp = (double)(vol - PulseAudio.Volume.MUTED); + return tmp / (double)(PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED); + } + + private void set_volume_success_cb (Context c, int success) + { + if ((bool)success) + volume_changed (_volume); + } + + private void sink_info_set_volume_cb (Context c, SinkInfo? i, int eol) + { + if (i == null) + return; + + unowned CVolume cvol = vol_set (i.volume, 1, double_to_volume (_volume)); + c.set_sink_volume_by_index (i.index, cvol, set_volume_success_cb); + } + + private void server_info_cb_for_set_volume (Context c, ServerInfo? i) + { + if (i == null) + { + warning ("Could not get PulseAudio server info"); + return; + } + + context.get_sink_info_by_name (i.default_sink_name, sink_info_set_volume_cb); + } + + public void set_volume (double volume) + { + if (context.get_state () != Context.State.READY) + { + warning ("Could not change volume: PulseAudio server connection is not ready."); + return; + } + _volume = volume; + + context.get_server_info (server_info_cb_for_set_volume); + } + + public double get_volume () + { + return _volume; + } +} -- cgit v1.2.3 From 0386c87d0176c3d539f6cac6f5d52ba1adf9d761 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Mar 2013 14:32:06 -0400 Subject: VolumeControl: turn "ready" into a property And bind it to the "enabled" properties of the volume and mute actions. --- src/service.vala | 2 ++ src/volume-control.vala | 25 +++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index cdb5b15..aa6664e 100644 --- a/src/service.vala +++ b/src/service.vala @@ -86,6 +86,8 @@ public class IndicatorSound.Service { volume_action.set_state (volume); }); + this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE); + return volume_action; } diff --git a/src/volume-control.vala b/src/volume-control.vala index 8c88c92..5ce5a05 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -31,9 +31,11 @@ public class VolumeControl : Object private bool _mute = true; private double _volume = 0.0; - public signal void ready (); public signal void volume_changed (double v); + /** true when connected to the pulse server */ + public bool ready { get; set; } + public VolumeControl () { if (loop == null) @@ -47,7 +49,7 @@ public class VolumeControl : Object context = new PulseAudio.Context (loop.get_api(), null, props); - context.set_state_callback (notify_cb); + context.set_state_callback (context_state_callback); if (context.connect(null, Context.Flags.NOFAIL, null) < 0) { @@ -95,25 +97,23 @@ public class VolumeControl : Object context.get_server_info (server_info_cb_for_props); } - private void notify_cb (Context c) + private void context_state_callback (Context c) { if (c.get_state () == Context.State.READY) { c.subscribe (PulseAudio.Context.SubscriptionMask.SINK); c.set_subscribe_callback (context_events_cb); get_properties (); - ready (); + this.ready = true; } + else + this.ready = false; } /* Mute operations */ public void set_mute (bool mute) { - if (context.get_state () != Context.State.READY) - { - warning ("Could not mute: PulseAudio server connection is not ready."); - return; - } + return_if_fail (context.get_state () == Context.State.READY); context.get_sink_info_list ((context, sink, eol) => { if (sink != null) @@ -175,11 +175,8 @@ public class VolumeControl : Object public void set_volume (double volume) { - if (context.get_state () != Context.State.READY) - { - warning ("Could not change volume: PulseAudio server connection is not ready."); - return; - } + return_if_fail (context.get_state () == Context.State.READY); + _volume = volume; context.get_server_info (server_info_cb_for_set_volume); -- cgit v1.2.3 From 1419ef39b81be9d8a42b915ad9f56e81ab4eb03c Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 28 Mar 2013 12:34:35 -0400 Subject: Show running media players in the menu Each player has its own action with a dictionary state. Right now, this state only contains one key "running", which signifies whether an instance of the player is currently running. It does not yet show non-running players on startup, and ignores the blacklist. --- src/Makefile.am | 2 + src/media-player-list.vala | 70 +++++++++++++++++++++++++ src/media-player.vala | 124 +++++++++++++++++++++++++++++++++++++++++++++ src/service.vala | 40 ++++++++++++++- 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 src/media-player-list.vala create mode 100644 src/media-player.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 2421683..1de360c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ music_bridge_VALASOURCES = \ service.vala \ main.vala \ volume-control.vala \ + media-player.vala \ + media-player-list.vala \ music-player-bridge.vala \ transport-menu-item.vala \ specific-items-manager.vala \ diff --git a/src/media-player-list.vala b/src/media-player-list.vala new file mode 100644 index 0000000..0c0a212 --- /dev/null +++ b/src/media-player-list.vala @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +/** + * MediaPlayerList is a list of media players that should appear in the sound menu. Its main responsibility is + * to listen for MPRIS players on the bus and attach them to the corresponding %Player objects. + */ +public class MediaPlayerList { + + public MediaPlayerList () { + this._players = new HashTable (str_hash, str_equal); + + this.mpris_watcher = new Mpris2Watcher (); + this.mpris_watcher.client_appeared.connect (this.player_appeared); + this.mpris_watcher.client_disappeared.connect (this.player_disappeared); + } + + public List players { + owned get { + return this._players.get_values (); + } + } + + public signal void player_added (MediaPlayer player); + + HashTable _players; + Mpris2Watcher mpris_watcher; + + void player_appeared (string desktop_id, string dbus_name, bool use_playlists) { + var appinfo = new DesktopAppInfo (desktop_id + ".desktop"); + if (appinfo == null) { + warning ("unable to find application '%s'", desktop_id); + return; + } + + MediaPlayer? player = this._players.lookup (desktop_id); + if (player == null) { + player = new MediaPlayer (appinfo); + this._players.insert (player.id, player); + this.player_added (player); + } + + player.attach (dbus_name); + } + + void player_disappeared (string dbus_name) { + MediaPlayer? player = this._players.find ( (name, player) => { + return player.dbus_name == dbus_name; + }); + + if (player != null) + player.detach (); + } +} diff --git a/src/media-player.vala b/src/media-player.vala new file mode 100644 index 0000000..8037ddb --- /dev/null +++ b/src/media-player.vala @@ -0,0 +1,124 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +/** + * MediaPlayer represents an MRPIS-capable media player. + */ +public class MediaPlayer: Object { + + public MediaPlayer (DesktopAppInfo appinfo) { + this.appinfo = appinfo; + } + + /** Desktop id of the player */ + public string id { + get { + return this.appinfo.get_id (); + } + } + + /** Display name of the player */ + public string name { + get { + return this.appinfo.get_name (); + } + } + + /** Application icon of the player */ + public Icon icon { + get { + return this.appinfo.get_icon (); + } + } + + /** + * True if an instance of the player is currently running. + * + * See also: attach(), detach() + */ + public bool is_running { + get { + return this.proxy != null; + } + } + + /** Name of the player on the bus, if an instance is currently running */ + public string dbus_name { + get { + return this._dbus_name; + } + } + + /** + * Attach this object to a process of the associated media player. The player must own @dbus_name and + * implement the org.mpris.MediaPlayer2.Player interface. + * + * Only one player can be attached at any given time. Use detach() to detach a player. + * + * This method does not block. If it is successful, "is-running" will be set to %TRUE. + */ + public void attach (string dbus_name) { + return_if_fail (this._dbus_name == null && this.proxy == null); + + this._dbus_name = dbus_name; + Bus.get_proxy.begin (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", + DBusProxyFlags.NONE, null, got_proxy); + } + + /** + * Detach this object from a process running the associated media player. + * + * See also: attach() + */ + public void detach () { + this.proxy = null; + this._dbus_name = null; + this.notify_property ("is-running"); + } + + /** + * Launch the associated media player. + * + * Note: this will _not_ call attach(), because it doesn't know on which dbus-name the player will appear. + * Use attach() to attach this object to a running instance of the player. + */ + public void launch () { + try { + this.appinfo.launch (null, null); + } + catch (Error e) { + warning ("unable to launch %s: %s", appinfo.get_name (), e.message); + } + } + + DesktopAppInfo appinfo; + MprisPlayer? proxy; + string _dbus_name; + + void got_proxy (Object? obj, AsyncResult res) { + try { + this.proxy = Bus.get_proxy.end (res); + this.notify_property ("is-running"); + } + catch (Error e) { + this._dbus_name = null; + warning ("unable to attach to media player: %s", e.message); + } + } +} diff --git a/src/service.vala b/src/service.vala index aa6664e..4ea4a95 100644 --- a/src/service.vala +++ b/src/service.vala @@ -2,6 +2,9 @@ public class IndicatorSound.Service { public Service () { this.volume_control = new VolumeControl (); + + this.players = new MediaPlayerList (); + this.players.player_added.connect (player_added); } public int run () { @@ -28,6 +31,8 @@ public class IndicatorSound.Service { SimpleActionGroup actions; Menu menu; VolumeControl volume_control; + MediaPlayerList players; + uint player_action_update_id; void activate_settings (SimpleAction action, Variant? param) { try { @@ -41,7 +46,7 @@ public class IndicatorSound.Service { var submenu = new Menu (); submenu.append ("Mute", "indicator.mute"); - var slider = new MenuItem ("null", "indicator.volume"); + var slider = new MenuItem (null, "indicator.volume"); slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); submenu.append_item (slider); @@ -111,4 +116,37 @@ public class IndicatorSound.Service { void name_lost (DBusConnection connection, string name) { this.loop.quit (); } + + bool update_player_action (MediaPlayer player) { + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "running", new Variant ("b", player.is_running)); + var state = builder.end (); + + SimpleAction? action = this.actions.lookup (player.id) as SimpleAction; + if (action == null) { + action = new SimpleAction.stateful (player.id, null, state); + action.activate.connect ( () => { player.launch (); }); + this.actions.insert (action); + } + else { + action.set_state (state); + } + + this.player_action_update_id = 0; + return false; + } + + void eventually_update_player_action (MediaPlayer player) { + if (player_action_update_id == 0) + this.player_action_update_id = Idle.add ( () => this.update_player_action (player) ); + } + + void player_added (MediaPlayer player) { + var item = new MenuItem (player.name, player.id); + item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); + this.menu.insert_item (this.menu.get_n_items () -1, item); + + eventually_update_player_action (player); + player.notify.connect ( () => eventually_update_player_action (player) ); + } } -- cgit v1.2.3 From b5d24eeed9528525861d7a7c185327a5e19c7bb7 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 19:07:14 -0400 Subject: Sync list of media players with gsettings --- src/media-player-list.vala | 73 +++++++++++++++++++++++++++++++++++++--------- src/service.vala | 25 ++++++++++------ 2 files changed, 76 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 0c0a212..3fa08f3 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -37,26 +37,73 @@ public class MediaPlayerList { } } - public signal void player_added (MediaPlayer player); - - HashTable _players; - Mpris2Watcher mpris_watcher; - - void player_appeared (string desktop_id, string dbus_name, bool use_playlists) { - var appinfo = new DesktopAppInfo (desktop_id + ".desktop"); - if (appinfo == null) { - warning ("unable to find application '%s'", desktop_id); - return; - } - + /** + * Adds the player associated with @desktop_id. Does nothing if such a player already exists. + */ + public MediaPlayer? insert (string desktop_id) { + message ("inserting %s", desktop_id); MediaPlayer? player = this._players.lookup (desktop_id); + if (player == null) { + message (" Really."); + var appinfo = new DesktopAppInfo (desktop_id + ".desktop"); + if (appinfo == null) { + warning ("unable to find application '%s'", desktop_id); + return null; + } + player = new MediaPlayer (appinfo); this._players.insert (player.id, player); this.player_added (player); } - player.attach (dbus_name); + return player; + } + + /** + * Removes the player associated with @desktop_id, unless it is currently running. + */ + public void remove (string desktop_id) { + MediaPlayer? player = this._players.lookup (desktop_id); + + if (player != null && !player.is_running) { + this._players.remove (desktop_id); + this.player_removed (player); + } + } + + /** + * Synchronizes the player list with @desktop_ids. After this call, this list will only contain the players + * in @desktop_ids. Players that were running but are not in @desktop_ids will remain in the list. + */ + public void sync (string[] desktop_ids) { + + /* hash desktop_ids for faster lookup */ + var hash = new HashTable (str_hash, str_equal); + foreach (var id in desktop_ids) + hash.add (id); + + /* remove players that are not desktop_ids */ + foreach (var id in this._players.get_keys ()) { + if (!hash.contains (id)) + this.remove (id); + } + + /* insert all players (insert() takes care of not adding a player twice */ + foreach (var id in desktop_ids) + this.insert (id); + } + + public signal void player_added (MediaPlayer player); + public signal void player_removed (MediaPlayer player); + + HashTable _players; + Mpris2Watcher mpris_watcher; + + void player_appeared (string desktop_id, string dbus_name, bool use_playlists) { + var player = this.insert (desktop_id); + if (player != null) + player.attach (dbus_name); } void player_disappeared (string dbus_name) { diff --git a/src/service.vala b/src/service.vala index 4ea4a95..8f5280e 100644 --- a/src/service.vala +++ b/src/service.vala @@ -1,10 +1,24 @@ public class IndicatorSound.Service { public Service () { + this.settings = new Settings ("com.canonical.indicator.sound"); + this.volume_control = new VolumeControl (); this.players = new MediaPlayerList (); - this.players.player_added.connect (player_added); + this.players.player_added.connect (this.player_added); + + this.actions = new SimpleActionGroup (); + this.actions.add_entries (action_entries, this); + this.actions.add_action (this.create_mute_action ()); + this.actions.add_action (this.create_volume_action ()); + + this.menu = create_base_menu (); + + this.players.sync (settings.get_strv ("preferred-media-players")); + this.settings.changed["preferred-media-players"].connect ( () => { + this.players.sync (settings.get_strv ("preferred-media-players")); + }); } public int run () { @@ -30,6 +44,7 @@ public class IndicatorSound.Service { MainLoop loop; SimpleActionGroup actions; Menu menu; + Settings settings; VolumeControl volume_control; MediaPlayerList players; uint player_action_update_id; @@ -97,14 +112,6 @@ public class IndicatorSound.Service { } void bus_acquired (DBusConnection connection, string name) { - this.actions = new SimpleActionGroup (); - this.actions.add_entries (action_entries, this); - - this.actions.add_action (this.create_mute_action ()); - this.actions.add_action (this.create_volume_action ()); - - this.menu = create_base_menu (); - try { connection.export_action_group ("/com/canonical/indicator/sound", this.actions); connection.export_menu_model ("/com/canonical/indicator/sound/desktop", this.menu); -- cgit v1.2.3 From e341f4f758fc704ecbc26cd30b96782afa5d54e6 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:16:01 -0400 Subject: Append ".desktop" to the desktop entries reported from MPRIS This is the format that GDesktopAppInfo expects. This means that the apps will be reffered to as app.desktop in gsettings, which is consistent with how others are doing it (indicator-messages, unity, gnome shell). Backwards compatibility with old settings keys is maintained. --- src/media-player-list.vala | 2 +- src/mpris2-watcher.vala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 3fa08f3..73f21ce 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -46,7 +46,7 @@ public class MediaPlayerList { if (player == null) { message (" Really."); - var appinfo = new DesktopAppInfo (desktop_id + ".desktop"); + var appinfo = new DesktopAppInfo (desktop_id.has_suffix (".desktop") ? desktop_id : desktop_id + ".desktop"); if (appinfo == null) { warning ("unable to find application '%s'", desktop_id); return null; diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 06ccb6e..105c797 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -66,7 +66,7 @@ public class Mpris2Watcher : GLib.Object MprisRoot? mpris2_root = this.create_mpris_root(address); if (mpris2_root == null) return; bool use_playlists = this.supports_playlists ( address ); - client_appeared (mpris2_root.DesktopEntry, address, use_playlists); + client_appeared (mpris2_root.DesktopEntry + ".desktop", address, use_playlists); } } } @@ -87,7 +87,7 @@ public class Mpris2Watcher : GLib.Object else if (previous_owner == "" && current_owner != "") { debug ("Client '%s' has appeared", name); bool use_playlists = this.supports_playlists ( name ); - client_appeared (mpris2_root.DesktopEntry, name, use_playlists); + client_appeared (mpris2_root.DesktopEntry + ".desktop", name, use_playlists); } } -- cgit v1.2.3 From 9f030ed47653f96bf88a6f7b746555b2581701cf Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:20:18 -0400 Subject: Make MediaPlayerList iteratable instead of providing the "players" property --- src/media-player-list.vala | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 73f21ce..893ec64 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -31,10 +31,26 @@ public class MediaPlayerList { this.mpris_watcher.client_disappeared.connect (this.player_disappeared); } - public List players { - owned get { - return this._players.get_values (); + /* only valid while the list is not changed */ + public class Iterator { + HashTableIter iter; + + public Iterator (MediaPlayerList list) { + this.iter = HashTableIter (list._players); } + + public MediaPlayer? next_value () { + MediaPlayer? player; + + if (this.iter.next (null, out player)) + return player; + else + return null; + } + } + + public Iterator iterator () { + return new Iterator (this); } /** -- cgit v1.2.3 From e1d7602cdeabfd4267a6279ec59971905e615a43 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:23:56 -0400 Subject: MediaPlayerList: remove debug messages --- src/media-player-list.vala | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 893ec64..203c69e 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -57,11 +57,9 @@ public class MediaPlayerList { * Adds the player associated with @desktop_id. Does nothing if such a player already exists. */ public MediaPlayer? insert (string desktop_id) { - message ("inserting %s", desktop_id); MediaPlayer? player = this._players.lookup (desktop_id); if (player == null) { - message (" Really."); var appinfo = new DesktopAppInfo (desktop_id.has_suffix (".desktop") ? desktop_id : desktop_id + ".desktop"); if (appinfo == null) { warning ("unable to find application '%s'", desktop_id); -- cgit v1.2.3 From 7519d2eb92c06b5a88d79a9a8f28b53c33b96c3b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:30:03 -0400 Subject: Always update all player actions This fixes a bug: only the first player that changed (or was added) in a single main context iteration was updated in update_player_action(). That's because only one source id was used for all idles. Another fix to this would be to keep track of which actions need to be updated. This patch is a bit more brute-force, but that will probably not matter much, as the amount of players is generally small. --- src/service.vala | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 8f5280e..1590844 100644 --- a/src/service.vala +++ b/src/service.vala @@ -124,28 +124,30 @@ public class IndicatorSound.Service { this.loop.quit (); } - bool update_player_action (MediaPlayer player) { - var builder = new VariantBuilder (new VariantType ("a{sv}")); - builder.add ("{sv}", "running", new Variant ("b", player.is_running)); - var state = builder.end (); - - SimpleAction? action = this.actions.lookup (player.id) as SimpleAction; - if (action == null) { - action = new SimpleAction.stateful (player.id, null, state); - action.activate.connect ( () => { player.launch (); }); - this.actions.insert (action); - } - else { - action.set_state (state); + bool update_player_actions () { + foreach (var player in this.players) { + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "running", new Variant ("b", player.is_running)); + var state = builder.end (); + + SimpleAction? action = this.actions.lookup (player.id) as SimpleAction; + if (action == null) { + action = new SimpleAction.stateful (player.id, null, state); + action.activate.connect ( () => { player.launch (); }); + this.actions.insert (action); + } + else { + action.set_state (state); + } } this.player_action_update_id = 0; return false; } - void eventually_update_player_action (MediaPlayer player) { + void eventually_update_player_actions () { if (player_action_update_id == 0) - this.player_action_update_id = Idle.add ( () => this.update_player_action (player) ); + this.player_action_update_id = Idle.add (this.update_player_actions); } void player_added (MediaPlayer player) { @@ -153,7 +155,7 @@ public class IndicatorSound.Service { item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); this.menu.insert_item (this.menu.get_n_items () -1, item); - eventually_update_player_action (player); - player.notify.connect ( () => eventually_update_player_action (player) ); + eventually_update_player_actions (); + player.notify.connect (this.eventually_update_player_actions); } } -- cgit v1.2.3 From 83c37bd14bcca1b2774bdb9f73c3742da65a65a3 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:43:37 -0400 Subject: Remove players when settings key changes --- src/service.vala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 1590844..d966de9 100644 --- a/src/service.vala +++ b/src/service.vala @@ -7,6 +7,7 @@ public class IndicatorSound.Service { this.players = new MediaPlayerList (); this.players.player_added.connect (this.player_added); + this.players.player_removed.connect (this.player_removed); this.actions = new SimpleActionGroup (); this.actions.add_entries (action_entries, this); @@ -158,4 +159,18 @@ public class IndicatorSound.Service { eventually_update_player_actions (); player.notify.connect (this.eventually_update_player_actions); } + + void player_removed (MediaPlayer player) { + this.actions.remove (player.id); + + int n = this.menu.get_n_items (); + for (int i = 0; i < n; i++) { + string action; + this.menu.get_item_attribute (i, "action", "s", out action); + if (action == player.id) { + this.menu.remove (i); + break; + } + } + } } -- cgit v1.2.3 From deb90ed324d668aa3815baac9e279bedac0b14ef Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:44:54 -0400 Subject: MediaPlayerList: use appname.desktop also for lookups --- src/media-player-list.vala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 203c69e..6d37b4d 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -57,12 +57,13 @@ public class MediaPlayerList { * Adds the player associated with @desktop_id. Does nothing if such a player already exists. */ public MediaPlayer? insert (string desktop_id) { - MediaPlayer? player = this._players.lookup (desktop_id); + var id = desktop_id.has_suffix (".desktop") ? desktop_id : desktop_id + ".desktop"; + MediaPlayer? player = this._players.lookup (id); if (player == null) { - var appinfo = new DesktopAppInfo (desktop_id.has_suffix (".desktop") ? desktop_id : desktop_id + ".desktop"); + var appinfo = new DesktopAppInfo (id); if (appinfo == null) { - warning ("unable to find application '%s'", desktop_id); + warning ("unable to find application '%s'", id); return null; } -- cgit v1.2.3 From eb24b0dbee584427991ae49cbcb94893bdbd47c2 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 3 Apr 2013 22:48:19 -0400 Subject: service.vala: add license header --- src/service.vala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index d966de9..03b72a6 100644 --- a/src/service.vala +++ b/src/service.vala @@ -1,3 +1,21 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ public class IndicatorSound.Service { public Service () { -- cgit v1.2.3 From 838d4366a4c5e159ef7cde4caef4d908730df3a7 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 05:51:55 +0200 Subject: Don't build unused files and remove superseded files --- src/Makefile.am | 56 +----- src/device.c | 276 -------------------------- src/device.h | 84 -------- src/sound-service-dbus.c | 489 ----------------------------------------------- src/sound-service-dbus.h | 66 ------- src/sound-service.c | 127 ------------ src/sound-service.h | 35 ---- src/sound-service.xml | 34 ---- 8 files changed, 1 insertion(+), 1166 deletions(-) delete mode 100644 src/device.c delete mode 100644 src/device.h delete mode 100644 src/sound-service-dbus.c delete mode 100644 src/sound-service-dbus.h delete mode 100644 src/sound-service.c delete mode 100644 src/sound-service.h delete mode 100644 src/sound-service.xml (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 1de360c..993b0d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,5 @@ libexec_PROGRAMS = indicator-sound-service -checkxml: $(srcdir)/sound-service.xml - @xmllint -valid -noout $< - @echo $< checks out ok - - #################################################################### # Sound Service #################################################################### @@ -22,19 +17,9 @@ music_bridge_VALASOURCES = \ volume-control.vala \ media-player.vala \ media-player-list.vala \ - music-player-bridge.vala \ - transport-menu-item.vala \ - specific-items-manager.vala \ - metadata-menu-item.vala \ - player-controller.vala \ mpris2-interfaces.vala \ mpris2-watcher.vala \ - mpris2-controller.vala \ - player-item.vala \ - settings-manager.vala \ - playlists-menu-item.vala \ - freedesktop-interfaces.vala \ - fetch-file.vala + freedesktop-interfaces.vala music_bridge_VALAFLAGS = \ --ccode \ @@ -52,7 +37,6 @@ music_bridge_VALAFLAGS = \ --pkg libxml-2.0 \ --pkg libpulse \ --pkg libpulse-mainloop-glib \ - --pkg pulseaudio-mgr \ --target-glib=2.36 $(MAINTAINER_VALAFLAGS) @@ -69,24 +53,6 @@ music_bridge_vala.stamp $(music_bridge_APIFILES): $(music_bridge_VALASOURCES) ############################### indicator_sound_service_SOURCES = \ common-defs.h \ - sound-state.c \ - sound-state.h \ - pulseaudio-mgr.h \ - pulseaudio-mgr.c \ - device.c \ - device.h \ - sound-service-dbus.h \ - sound-service-dbus.c \ - slider-menu-item.h \ - slider-menu-item.c \ - voip-input-menu-item.h \ - voip-input-menu-item.c \ - mute-menu-item.h \ - mute-menu-item.c \ - gen-sound-service.xml.h \ - gen-sound-service.xml.c \ - sound-service-marshal.c \ - sound-service-marshal.h \ $(music_bridge_VALASOURCES:.vala=.c) indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ @@ -97,26 +63,8 @@ indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS) indicator_sound_service_LDFLAGS = $(COVERAGE_LDFLAGS) -######################### -# Service xml compilation -######################### -DBUS_SPECS = \ - sound-service.xml - -gen-%.xml.h: %.xml - @echo "Building $@ from $<" - @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@ - -gen-%.xml.c: %.xml - @echo "Building $@ from $<" - @echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@ - @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@ - @echo ";" >> $@ - - EXTRA_DIST = \ $(DBUS_SPECS) \ - sound-service.xml \ $(music_bridge_APIFILES) \ $(music_bridge_VALASOURCES) @@ -126,8 +74,6 @@ EXTRA_DIST = \ BUILT_SOURCES = \ music_bridge_vala.stamp \ $(music_bridge_APIFILES) \ - gen-sound-service.xml.h \ - gen-sound-service.xml.c \ $(music_bridge_VALASOURCES:.vala=.c) CLEANFILES = \ diff --git a/src/device.c b/src/device.c deleted file mode 100644 index 84db596..0000000 --- a/src/device.c +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright 2011 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 "device.h" -#include "slider-menu-item.h" -#include "mute-menu-item.h" -#include "voip-input-menu-item.h" -#include "pulseaudio-mgr.h" -#include "sound-state.h" - -typedef struct _DevicePrivate DevicePrivate; - -struct _DevicePrivate -{ - SliderMenuItem* volume_slider_menuitem; - MuteMenuItem* mute_menuitem; - VoipInputMenuItem* voip_input_menu_item; - SoundState current_sound_state; - SoundServiceDbus* service; -}; - -#define DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVICE_TYPE, DevicePrivate)) - -/* Prototypes */ -static void device_class_init (DeviceClass *klass); -static void device_init (Device *self); -static void device_dispose (GObject *object); -static void device_finalize (GObject *object); - -static SoundState device_get_state_from_volume (Device* self); -static void device_mute_update (Device* self, gboolean muted); - -G_DEFINE_TYPE (Device, device, G_TYPE_OBJECT); - -static void -device_class_init (DeviceClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (DevicePrivate)); - - gobject_class->dispose = device_dispose; - gobject_class->finalize = device_finalize; -} - -static void -device_init (Device *self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - priv->mute_menuitem = NULL; - priv->volume_slider_menuitem = NULL; - priv->voip_input_menu_item = NULL; - priv->current_sound_state = UNAVAILABLE; - priv->service = NULL; - - // Init our menu items. - priv->mute_menuitem = g_object_new (MUTE_MENU_ITEM_TYPE, NULL); - priv->voip_input_menu_item = g_object_new (VOIP_INPUT_MENU_ITEM_TYPE, NULL);; - priv->volume_slider_menuitem = slider_menu_item_new (self); - mute_menu_item_enable (priv->mute_menuitem, FALSE); - slider_menu_item_enable (priv->volume_slider_menuitem, FALSE); -} - -static void -device_dispose (GObject *object) -{ - G_OBJECT_CLASS (device_parent_class)->dispose (object); -} - -static void -device_finalize (GObject *object) -{ - G_OBJECT_CLASS (device_parent_class)->finalize (object); -} - -void -device_sink_populate (Device* self, - const pa_sink_info* update) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE(self); - mute_menu_item_enable (priv->mute_menuitem, TRUE); - slider_menu_item_populate (priv->volume_slider_menuitem, update); - SoundState state = device_get_state_from_volume (self); - if (priv->current_sound_state != state){ - priv->current_sound_state = state; - sound_service_dbus_update_sound_state (priv->service, - priv->current_sound_state); - } - device_mute_update (self, update->mute); -} - -void -device_sink_update (Device* self, - const pa_sink_info* update) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - slider_menu_item_update (priv->volume_slider_menuitem, update); - - SoundState state = device_get_state_from_volume (self); - if (priv->current_sound_state != state){ - priv->current_sound_state = state; - sound_service_dbus_update_sound_state (priv->service, - priv->current_sound_state); - } - - device_mute_update (self, update->mute); -} - -gint -device_get_voip_source_output_index (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return voip_input_menu_item_get_source_output_index (priv->voip_input_menu_item); -} - -static void -device_mute_update (Device* self, gboolean muted) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - mute_menu_item_update (priv->mute_menuitem, muted); - SoundState state = device_get_state_from_volume (self); - - if (muted == TRUE){ - state = MUTED; - } - // Only send signals if something has changed - if (priv->current_sound_state != state){ - priv->current_sound_state = state; - sound_service_dbus_update_sound_state (priv->service, state); - } -} - -void -device_ensure_sink_is_unmuted (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - if (mute_menu_item_is_muted (priv->mute_menuitem)){ - pm_update_mute (FALSE); - } -} - - -static SoundState -device_get_state_from_volume (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - GVariant* v = dbusmenu_menuitem_property_get_variant (DBUSMENU_MENUITEM(priv->volume_slider_menuitem), - DBUSMENU_VOLUME_MENUITEM_LEVEL); - gdouble volume_percent = g_variant_get_double (v); - - return sound_state_get_from_volume ((int)volume_percent); -} - -void -device_determine_blocking_state (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - if (mute_menu_item_is_muted (priv->mute_menuitem)){ - /** - We don't want to set the current state to blocking - as this is a fire and forget event. - */ - sound_service_dbus_update_sound_state (priv->service, - BLOCKED); - } -} - -gint -device_get_sink_index (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return slider_menu_item_get_sink_index (priv->volume_slider_menuitem); -} - -gboolean -device_is_sink_populated (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM (priv->volume_slider_menuitem), - DBUSMENU_MENUITEM_PROP_ENABLED); -} - -void -device_activate_voip_item (Device* self, gint source_output_index, gint client_index) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - if (voip_input_menu_item_is_interested (priv->voip_input_menu_item, - source_output_index, - client_index)){ - voip_input_menu_item_enable (priv->voip_input_menu_item, TRUE); - } -} - -void -device_deactivate_voip_source (Device* self, gboolean visible) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - visible &= voip_input_menu_item_is_active (priv->voip_input_menu_item); - voip_input_menu_item_deactivate_source (priv->voip_input_menu_item, visible); -} - -void -device_deactivate_voip_client (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - voip_input_menu_item_deactivate_voip_client (priv->voip_input_menu_item); -} - -void -device_sink_deactivated (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - priv->current_sound_state = UNAVAILABLE; - sound_service_dbus_update_sound_state (priv->service, - priv->current_sound_state); - mute_menu_item_enable (priv->mute_menuitem, FALSE); - slider_menu_item_enable (priv->volume_slider_menuitem, FALSE); -} - -SoundState -device_get_state (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return priv->current_sound_state; -} - -void -device_update_voip_input_source (Device* self, const pa_source_info* update) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - voip_input_menu_item_update (priv->voip_input_menu_item, update); -} - -gboolean -device_is_voip_source_populated (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return voip_input_menu_item_is_populated (priv->voip_input_menu_item); -} - -gint device_get_source_index (Device* self) -{ - DevicePrivate* priv = DEVICE_GET_PRIVATE (self); - return voip_input_menu_item_get_index (priv->voip_input_menu_item); -} - -Device* -device_new (SoundServiceDbus* service) -{ - Device* sink = g_object_new (DEVICE_TYPE, NULL); - DevicePrivate* priv = DEVICE_GET_PRIVATE (sink); - priv->service = service; - sound_service_dbus_build_sound_menu (service, - mute_menu_item_get_button (priv->mute_menuitem), - DBUSMENU_MENUITEM (priv->volume_slider_menuitem), - DBUSMENU_MENUITEM (priv->voip_input_menu_item)); - pm_establish_pulse_connection (sink); - return sink; -} diff --git a/src/device.h b/src/device.h deleted file mode 100644 index ccaf4ea..0000000 --- a/src/device.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 . - */ - -#ifndef __DEVICE_H__ -#define __DEVICE_H__ - -#include -#include - -#include "common-defs.h" -#include "sound-service-dbus.h" - -#include - -G_BEGIN_DECLS - -#define DEVICE_TYPE (device_get_type ()) -#define DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVICE_TYPE, Device)) -#define DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), DEVICE_TYPE, DeviceClass)) -#define IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVICE_TYPE)) -#define IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DEVICE_TYPE)) -#define DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVICE_TYPE, DeviceClass)) - -typedef struct _Device Device; -typedef struct _DeviceClass DeviceClass; - -struct _Device { - GObject parent; -}; - -struct _DeviceClass { - GObjectClass parent_class; -}; - -GType device_get_type (void) G_GNUC_CONST; - -/** - * TODO - * Refactor this to become a device manager obj basically acting as wrapper for - * the communication between pulseaudio-mgr and the individual items. - * First steps collapse slider/volume related stuff into slider-menu-item. - */ - -// Sink related -void device_sink_populate (Device* sink, const pa_sink_info* update); -void device_sink_update (Device* sink, const pa_sink_info* update); -gboolean device_is_sink_populated (Device* sink); -gint device_get_sink_index (Device* self); -void device_sink_deactivated (Device* self); -void device_update_mute (Device* self, gboolean mute_update); -void device_ensure_sink_is_unmuted (Device* self); - -// source and sinkinput/client related for VOIP functionality -void device_update_voip_input_source (Device* sink, const pa_source_info* update); -void device_activate_voip_item (Device* sink, gint source_output_index, gint client_index); -gint device_get_voip_source_output_index (Device* sink); -gboolean device_is_voip_source_populated (Device* sink); -gint device_get_source_index (Device* self); -void device_determine_blocking_state (Device* self); -void device_deactivate_voip_source (Device* self, gboolean visible); -void device_deactivate_voip_client (Device* self); -SoundState device_get_state (Device* self); - -Device* device_new (SoundServiceDbus* service); - -G_END_DECLS - -#endif diff --git a/src/sound-service-dbus.c b/src/sound-service-dbus.c deleted file mode 100644 index 5e004cb..0000000 --- a/src/sound-service-dbus.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - * 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 . - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include - -#include "sound-service-dbus.h" -#include "device.h" -#include "gen-sound-service.xml.h" -#include "dbus-shared-names.h" -#include "sound-service-marshal.h" - -// DBUS methods -static void bus_method_call (GDBusConnection * connection, - const gchar * sender, - const gchar * path, - const gchar * interface, - const gchar * method, - GVariant * params, - GDBusMethodInvocation * invocation, - gpointer user_data); - -static GDBusInterfaceVTable interface_table = { - method_call: bus_method_call, - get_property: NULL, /* No properties */ - set_property: NULL /* No properties */ -}; - - -typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; - -struct _SoundServiceDbusPrivate { - GDBusConnection* connection; - DbusmenuMenuitem* root_menuitem; - Device* device; - gboolean greeter_mode; - guint registration_id; -}; - -enum { - TRACK_SPECIFIC_ITEM, - PLAYER_SPECIFIC_ITEM, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * interface_info = NULL; - -#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); - -static void show_sound_settings_dialog (DbusmenuMenuitem *mi, - gpointer user_data); -static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self, - const gchar* player_name, - gboolean blacklist); - -static gboolean sound_service_dbus_is_blacklisted (SoundServiceDbus* self, - const gchar* player_name); - -G_DEFINE_TYPE (SoundServiceDbus, sound_service_dbus, G_TYPE_OBJECT); - -static void -sound_service_dbus_class_init (SoundServiceDbusClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof(SoundServiceDbusPrivate)); - - object_class->dispose = sound_service_dbus_dispose; - object_class->finalize = sound_service_dbus_finalize; - - g_assert(klass != NULL); - - if (node_info == NULL) { - GError * error = NULL; - - node_info = g_dbus_node_info_new_for_xml(_sound_service, &error); - if (error != NULL) { - g_critical ("Unable to parse Indicator Service Interface description: %s", - error->message); - g_error_free(error); - } - } - - if (interface_info == NULL) { - interface_info = g_dbus_node_info_lookup_interface (node_info, - INDICATOR_SOUND_DBUS_INTERFACE); - - if (interface_info == NULL) { - g_critical("Unable to find interface '" INDICATOR_SOUND_DBUS_INTERFACE "'"); - } - } - signals[TRACK_SPECIFIC_ITEM] = g_signal_new("track-specific-item-requested", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - _sound_service_marshal_VOID__STRING_STRING, - G_TYPE_NONE, 2, G_TYPE_STRING, - G_TYPE_STRING); - signals[PLAYER_SPECIFIC_ITEM] = g_signal_new("player-specific-item-requested", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - _sound_service_marshal_VOID__STRING_STRING, - G_TYPE_NONE, 2, G_TYPE_STRING, - G_TYPE_STRING); -} - -static void -sound_service_dbus_init (SoundServiceDbus *self) -{ - GError *error = NULL; - SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); - - priv->connection = NULL; - - /* Fetch the session bus */ - priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - - if (error != NULL) { - g_critical ("sound-service-dbus:Unable to connect to the session bus when creating indicator sound service : %s", error->message); - g_error_free (error); - return; - } - /* register the service on it */ - priv->registration_id = g_dbus_connection_register_object (priv->connection, - INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, - interface_info, - &interface_table, - self, - NULL, - &error); - if (error != NULL) { - g_critical ("Unable to register the sound service on DBus: %s", error->message); - g_error_free (error); - } -} - -DbusmenuMenuitem* -sound_service_dbus_create_root_item (SoundServiceDbus* self, gboolean greeter_mode) -{ - SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); - priv->greeter_mode = greeter_mode; - priv->root_menuitem = dbusmenu_menuitem_new(); - DbusmenuServer *server = dbusmenu_server_new (INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH); - dbusmenu_server_set_root (server, priv->root_menuitem); - g_object_unref (priv->root_menuitem); - priv->device = device_new (self); - return priv->root_menuitem; -} - -void -sound_service_dbus_build_sound_menu ( SoundServiceDbus* self, - DbusmenuMenuitem* mute_item, - DbusmenuMenuitem* slider_item, - DbusmenuMenuitem* voip_input_menu_item) -{ - SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); - - // Mute, Volume and Voip widgets - dbusmenu_menuitem_child_add_position (priv->root_menuitem, mute_item, 0); - dbusmenu_menuitem_child_add_position (priv->root_menuitem, slider_item, 1); - dbusmenu_menuitem_child_add_position (priv->root_menuitem, voip_input_menu_item, 2); - - if (!priv->greeter_mode) { - // Separator - DbusmenuMenuitem* separator = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set (separator, - DBUSMENU_MENUITEM_PROP_TYPE, - DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_add_position (priv->root_menuitem, separator, 3); - g_object_unref (separator); - - // Sound preferences dialog - DbusmenuMenuitem* settings_mi = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set( settings_mi, - DBUSMENU_MENUITEM_PROP_LABEL, - _("Sound Settings...")); - dbusmenu_menuitem_child_append(priv->root_menuitem, settings_mi); - g_object_unref (settings_mi); - g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(show_sound_settings_dialog), NULL); - } -} - -/** -show_sound_settings_dialog: -Bring up the gnome volume preferences dialog -**/ -static void -show_sound_settings_dialog (DbusmenuMenuitem *mi, - gpointer user_data) -{ - GError * error = NULL; - if (!g_spawn_command_line_async("gnome-volume-control --page=applications", &error) && - !g_spawn_command_line_async("gnome-control-center sound", &error) && - !g_spawn_command_line_async("xfce4-mixer", &error)) - { - g_warning("Unable to show dialog: %s", error->message); - g_error_free(error); - } -} - -static void -sound_service_dbus_dispose (GObject *object) -{ - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (object); - - if (priv->connection && priv->registration_id) { - g_dbus_connection_unregister_object (priv->connection, priv->registration_id); - priv->registration_id = 0; - } - - g_clear_object(&priv->connection); - - G_OBJECT_CLASS (sound_service_dbus_parent_class)->dispose (object); - //TODO dispose of the active sink instance ! - return; -} - -static void -sound_service_dbus_finalize (GObject *object) -{ - G_OBJECT_CLASS (sound_service_dbus_parent_class)->finalize (object); - return; -} - - -// EMIT STATE SIGNAL -void -sound_service_dbus_update_sound_state (SoundServiceDbus* self, - SoundState new_state) -{ - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); - - GVariant* v_output = g_variant_new("(i)", (int)new_state); - - GError * error = NULL; - - if (priv->connection == NULL || - g_dbus_connection_is_closed (priv->connection) == TRUE){ - g_critical ("sound_service_dbus_update_sound_state - dbus connection is %s !!", - priv->connection == NULL? "NULL" : "closed"); - return; - } - - //g_debug ("emitting state signal with value %i", (int)new_state); - g_dbus_connection_emit_signal( priv->connection, - NULL, - INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, - INDICATOR_SOUND_DBUS_INTERFACE, - INDICATOR_SOUND_SIGNAL_STATE_UPDATE, - v_output, - &error ); - if (error != NULL) { - g_critical ("Unable to emit signal because : %s", error->message); - g_error_free(error); - } -} - -//HANDLE DBUS METHOD CALLS -static void -bus_method_call (GDBusConnection * connection, - const gchar * sender, - const gchar * path, - const gchar * interface, - const gchar * method, - GVariant * params, - GDBusMethodInvocation * invocation, - gpointer user_data) -{ - SoundServiceDbus* service = SOUND_SERVICE_DBUS(user_data); - g_return_if_fail ( IS_SOUND_SERVICE_DBUS(service) ); - GVariant * retval = NULL; - SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (service); - - if (g_strcmp0(method, "GetSoundState") == 0) { - g_debug("Get state - %i", device_get_state (priv->device)); - retval = g_variant_new ( "(i)", device_get_state (priv->device)); - } - else if (g_strcmp0(method, "BlacklistMediaPlayer") == 0) { - gboolean blacklist; - const gchar* player_name; - g_variant_get (params, "(&sb)", &player_name, &blacklist); - - g_debug ("BlacklistMediaPlayer - bool %i", blacklist); - g_debug ("BlacklistMediaPlayer - name %s", player_name); - gboolean result = sound_service_dbus_blacklist_player (service, - player_name, - blacklist); - retval = g_variant_new ("(b)", result); - } - else if (g_strcmp0(method, "IsBlacklisted") == 0) { - const gchar* player_name; - g_variant_get (params, "(&s)", &player_name); - - g_debug ("IsBlacklisted - name %s", player_name); - gboolean result = sound_service_dbus_is_blacklisted (service, - player_name); - retval = g_variant_new ("(b)", result); - } - else if (g_strcmp0(method, "EnableTrackSpecificItems") == 0) { - g_debug ("EnableTrackSpecificItems"); - gchar* player_object_path; - gchar* player_id; - g_variant_get (params, "(os)", &player_object_path, &player_id); - //g_debug ("object path = %s and id = %s", player_object_path, player_id); - g_signal_emit (service, - signals[TRACK_SPECIFIC_ITEM], - 0, - player_object_path, - player_id); - g_free (player_object_path); - g_free (player_id); - - } - else if (g_strcmp0(method, "EnablePlayerSpecificItems") == 0) { - gchar* player_object_path; - gchar* player_id; - g_variant_get (params, "(os)", &player_object_path, &player_id); - g_debug ("PLayer specific item - object path = %s and id = %s", - player_object_path, - player_id); - g_signal_emit (service, - signals[PLAYER_SPECIFIC_ITEM], - 0, - player_object_path, - player_id); - g_free (player_object_path); - g_free (player_id); - } - else { - g_warning("Calling method '%s' on the sound service but it's unknown", method); - } - g_dbus_method_invocation_return_value (invocation, retval); -} - -/** - TODO - Works nicely but refactor into at least two different methods -**/ -static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self, - const gchar* player_name, - gboolean blacklist) -{ - g_return_val_if_fail (player_name != NULL, FALSE); - g_return_val_if_fail (IS_SOUND_SERVICE_DBUS (self), FALSE); - - GVariant* the_black_list; - gboolean result = FALSE; - GSettings* our_settings; - GVariantIter iter; - gchar *str; - GVariantBuilder builder; - - our_settings = g_settings_new ("com.canonical.indicator.sound"); - the_black_list = g_settings_get_value (our_settings, - "blacklisted-media-players"); - g_variant_iter_init (&iter, the_black_list); - g_variant_builder_init(&builder, G_VARIANT_TYPE_STRING_ARRAY); - - while (g_variant_iter_loop (&iter, "s", &str)){ - g_variant_builder_add (&builder, "s", str); - } - g_variant_iter_init (&iter, the_black_list); - - if (blacklist == TRUE){ - while (g_variant_iter_loop (&iter, "s", &str)){ - g_print ("first pass to check if %s is present\n", str); - if (g_strcmp0 (player_name, str) == 0){ - // Return if its already there - g_debug ("we have this already blacklisted, no need to do anything"); - g_variant_builder_clear (&builder); - g_object_unref (our_settings); - g_variant_unref (the_black_list); - return result; - } - } - // Otherwise blacklist it ! - g_debug ("about to blacklist %s", player_name); - g_variant_builder_add (&builder, "s", player_name); - } - else{ - gboolean present = FALSE; - g_variant_iter_init (&iter, the_black_list); - g_debug ("attempting to UN-blacklist %s", player_name); - - while (g_variant_iter_loop (&iter, "s", &str)){ - if (g_strcmp0 (player_name, str) == 0){ - present = TRUE; - } - } - // It was not there anyway, return false - if (present == FALSE){ - g_debug ("it was not blacklisted ?, no need to do anything"); - g_variant_builder_clear (&builder); - g_object_unref (our_settings); - g_variant_unref (the_black_list); - return result; - } - - // Otherwise free the builder and reconstruct ensuring no duplicates. - g_variant_builder_clear (&builder); - g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY); - - g_variant_iter_init (&iter, the_black_list); - - while (g_variant_iter_loop (&iter, "s", &str)){ - if (g_strcmp0 (player_name, str) != 0){ - g_variant_builder_add (&builder, "s", str); - } - } - } - GVariant* value = g_variant_builder_end (&builder); - result = g_settings_set_value (our_settings, - "blacklisted-media-players", - value); - - g_object_unref (our_settings); - g_variant_unref (the_black_list); - - return result; -} - -static gboolean sound_service_dbus_is_blacklisted (SoundServiceDbus *self, - const gchar *player_name) -{ - GSettings *our_settings; - GVariant *the_black_list; - GVariantIter iter; - gchar *str; - gboolean result = FALSE; - - g_return_val_if_fail (player_name != NULL, FALSE); - g_return_val_if_fail (IS_SOUND_SERVICE_DBUS (self), FALSE); - - our_settings = g_settings_new ("com.canonical.indicator.sound"); - the_black_list = g_settings_get_value (our_settings, - "blacklisted-media-players"); - g_variant_iter_init (&iter, the_black_list); - while (g_variant_iter_next (&iter, "s", &str)){ - if (g_strcmp0 (player_name, str) == 0) { - result = TRUE; - g_free (str); - break; - } - g_free (str); - } - - g_object_unref (our_settings); - g_variant_unref (the_black_list); - - return result; -} - diff --git a/src/sound-service-dbus.h b/src/sound-service-dbus.h deleted file mode 100644 index 1c15fc7..0000000 --- a/src/sound-service-dbus.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 . - */ - -#ifndef __SOUND_SERVICE_DBUS_H__ -#define __SOUND_SERVICE_DBUS_H__ - -#include -#include -#include -#include "common-defs.h" - - -G_BEGIN_DECLS - -#define SOUND_SERVICE_DBUS_TYPE (sound_service_dbus_get_type ()) -#define SOUND_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbus)) -#define SOUND_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusClass)) -#define IS_SOUND_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUND_SERVICE_DBUS_TYPE)) -#define IS_SOUND_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SOUND_SERVICE_DBUS_TYPE)) -#define SOUND_SERVICE_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusClass)) - -typedef struct _SoundServiceDbus SoundServiceDbus; -typedef struct _SoundServiceDbusClass SoundServiceDbusClass; -typedef struct _SoundData SoundData; - -struct _SoundData { - SoundServiceDbus *service; -}; - -struct _SoundServiceDbus { - GObject parent; -}; - -struct _SoundServiceDbusClass { - GObjectClass parent_class; -}; - -GType sound_service_dbus_get_type (void) G_GNUC_CONST; - -DbusmenuMenuitem* sound_service_dbus_create_root_item (SoundServiceDbus* self, gboolean greeter_mode); -void sound_service_dbus_update_sound_state (SoundServiceDbus* self, SoundState new_state); -void sound_service_dbus_build_sound_menu ( SoundServiceDbus* self, - DbusmenuMenuitem* mute_item, - DbusmenuMenuitem* slider_item, - DbusmenuMenuitem* voip_input_menu_item); - - -G_END_DECLS - -#endif diff --git a/src/sound-service.c b/src/sound-service.c deleted file mode 100644 index 0170f81..0000000 --- a/src/sound-service.c +++ /dev/null @@ -1,127 +0,0 @@ -/* -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 "sound-service.h" -#include "pulseaudio-mgr.h" -#include "sound-service-dbus.h" -#include "music-player-bridge.h" - -static GMainLoop *mainloop = NULL; -static MusicPlayerBridge* player_bridge = NULL; -/***********************************************************************************************************/ -// Init and exit functions -/**********************************************************************************************************************/ -/** -service_shutdown: -When the service interface starts to shutdown, we -should follow it. -**/ - -void -service_shutdown (IndicatorService *service, gpointer user_data) -{ - if (mainloop != NULL) { - g_debug("Service shutdown !"); - close_pulse_activites(); - g_main_loop_quit(mainloop); - } - return; -} - -static gboolean -get_greeter_mode (void) -{ - const gchar *var; - var = g_getenv("INDICATOR_GREETER_MODE"); - return (g_strcmp0(var, "1") == 0); -} - -void -on_player_specific_item_requested (SoundServiceDbus* sound_service, - const gchar* desktop_id, - const gchar* player_object_path, - gpointer userdata) -{ - if (player_bridge != NULL){ - music_player_bridge_enable_player_specific_items_for_client (player_bridge, - desktop_id, - player_object_path); - } -} - -void -on_track_specific_item_requested (SoundServiceDbus* sound_service, - const gchar* desktop_id, - const gchar* player_object_path, - gpointer userdata) -{ - if (player_bridge != NULL){ - music_player_bridge_enable_track_specific_items_for_client (player_bridge, - desktop_id, - player_object_path); - } -} - -/** -main: -**/ -int -main (int argc, char ** argv) -{ - gboolean greeter_mode; - - g_type_init(); - textdomain (GETTEXT_PACKAGE); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - setlocale (LC_ALL, ""); - - IndicatorService *service = indicator_service_new_version (INDICATOR_SOUND_DBUS_NAME, - INDICATOR_SOUND_DBUS_VERSION); - g_signal_connect(G_OBJECT(service), - INDICATOR_SERVICE_SIGNAL_SHUTDOWN, - G_CALLBACK(service_shutdown), NULL); - - SoundServiceDbus* sound_service = g_object_new(SOUND_SERVICE_DBUS_TYPE, NULL); - g_signal_connect(G_OBJECT(sound_service), - "track-specific-item-requested", - G_CALLBACK(on_track_specific_item_requested), NULL); - g_signal_connect(G_OBJECT(sound_service), - "player-specific-item-requested", - G_CALLBACK(on_player_specific_item_requested), NULL); - - greeter_mode = get_greeter_mode(); - - DbusmenuMenuitem* root_menuitem = sound_service_dbus_create_root_item(sound_service, greeter_mode); - if (!greeter_mode) { - player_bridge = music_player_bridge_new(); - music_player_bridge_set_root_menu_item(player_bridge, root_menuitem); - } - - // Run the loop - mainloop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(mainloop); - - return 0; -} - - - - diff --git a/src/sound-service.h b/src/sound-service.h deleted file mode 100644 index 7c5d0c3..0000000 --- a/src/sound-service.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __INCLUDE_SOUND_SERVICE_H__ -#define __INCLUDE_SOUND_SERVICE_H__ - -/* -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 "dbus-shared-names.h" - -// ENTRY AND EXIT POINTS -void service_shutdown(IndicatorService * service, gpointer user_data); -int main (int argc, char ** argv); - -#endif diff --git a/src/sound-service.xml b/src/sound-service.xml deleted file mode 100644 index cb1d928..0000000 --- a/src/sound-service.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- cgit v1.2.3 From 69fc4a3d92bbbf5940769d877650e45a9c2b913b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 05:57:18 +0200 Subject: Remove sound-service-marshal (not needed anymore) --- src/Makefile.am | 10 ---------- src/sound-service-marshal.list | 2 -- 2 files changed, 12 deletions(-) delete mode 100644 src/sound-service-marshal.list (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 993b0d2..afbc364 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,13 +1,5 @@ libexec_PROGRAMS = indicator-sound-service -#################################################################### -# Sound Service -#################################################################### - -glib_marshal_list = sound-service-marshal.list -glib_marshal_prefix = _sound_service_marshal - - ##################### # Sound service vala ##################### @@ -80,5 +72,3 @@ CLEANFILES = \ $(BUILT_SOURCES) DISTCLEANFILES = - -include $(top_srcdir)/Makefile.am.marshal diff --git a/src/sound-service-marshal.list b/src/sound-service-marshal.list deleted file mode 100644 index 4c756d4..0000000 --- a/src/sound-service-marshal.list +++ /dev/null @@ -1,2 +0,0 @@ -VOID:STRING,STRING - -- cgit v1.2.3 From 77e3a0bb0453f7be6d98a068345ef4484ebf7792 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 06:11:15 +0200 Subject: Modernize vala compiling in Makefile.am This fixes parallel builds. --- src/Makefile.am | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index afbc364..083c5c2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,7 @@ libexec_PROGRAMS = indicator-sound-service -##################### -# Sound service vala -##################### -music_bridge_VALASOURCES = \ +indicator_sound_service_SOURCES = \ + common-defs.h \ service.vala \ main.vala \ volume-control.vala \ @@ -13,9 +11,8 @@ music_bridge_VALASOURCES = \ mpris2-watcher.vala \ freedesktop-interfaces.vala -music_bridge_VALAFLAGS = \ +indicator_sound_service_VALAFLAGS = \ --ccode \ - -H music-player-bridge.h -d . \ --vapidir=$(top_srcdir)/vapi/ \ --vapidir=./ \ --thread \ @@ -31,22 +28,6 @@ music_bridge_VALAFLAGS = \ --pkg libpulse-mainloop-glib \ --target-glib=2.36 - $(MAINTAINER_VALAFLAGS) - -music_bridge_APIFILES = \ - music-player-bridge.h - -music_bridge_vala.stamp $(music_bridge_APIFILES): $(music_bridge_VALASOURCES) - $(AM_V_GEN) $(VALAC) $(music_bridge_VALAFLAGS) $^ - touch music_bridge_vala.stamp - -############################### -# Sound Service C -############################### -indicator_sound_service_SOURCES = \ - common-defs.h \ - $(music_bridge_VALASOURCES:.vala=.c) - indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ $(SOUNDSERVICE_CFLAGS) \ $(GCONF_CFLAGS) \ @@ -54,21 +35,3 @@ indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ -DLIBEXECDIR=\"$(libexecdir)\" -Wall indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS) indicator_sound_service_LDFLAGS = $(COVERAGE_LDFLAGS) - -EXTRA_DIST = \ - $(DBUS_SPECS) \ - $(music_bridge_APIFILES) \ - $(music_bridge_VALASOURCES) - -####################### -# Stuff to clean Stuff -####################### -BUILT_SOURCES = \ - music_bridge_vala.stamp \ - $(music_bridge_APIFILES) \ - $(music_bridge_VALASOURCES:.vala=.c) - -CLEANFILES = \ - $(BUILT_SOURCES) - -DISTCLEANFILES = -- cgit v1.2.3 From 6fabe993476761f2bb1852c3380ca2e5be1e0ac4 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 06:38:50 +0200 Subject: Service: create player action right away, not lazily in an idle --- src/service.vala | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 03b72a6..a291f2d 100644 --- a/src/service.vala +++ b/src/service.vala @@ -143,21 +143,17 @@ public class IndicatorSound.Service { this.loop.quit (); } + Variant action_state_for_player (MediaPlayer player) { + var builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "running", new Variant ("b", player.is_running)); + return builder.end (); + } + bool update_player_actions () { foreach (var player in this.players) { - var builder = new VariantBuilder (new VariantType ("a{sv}")); - builder.add ("{sv}", "running", new Variant ("b", player.is_running)); - var state = builder.end (); - SimpleAction? action = this.actions.lookup (player.id) as SimpleAction; - if (action == null) { - action = new SimpleAction.stateful (player.id, null, state); - action.activate.connect ( () => { player.launch (); }); - this.actions.insert (action); - } - else { - action.set_state (state); - } + if (action != null) + action.set_state (this.action_state_for_player (player)); } this.player_action_update_id = 0; @@ -174,7 +170,10 @@ public class IndicatorSound.Service { item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); this.menu.insert_item (this.menu.get_n_items () -1, item); - eventually_update_player_actions (); + SimpleAction action = new SimpleAction.stateful (player.id, null, this.action_state_for_player (player)); + action.activate.connect ( () => { player.launch (); }); + this.actions.insert (action); + player.notify.connect (this.eventually_update_player_actions); } -- cgit v1.2.3 From 4138cbeb33798018034dff8c0a83ea70850d912f Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 16:59:01 +0200 Subject: Really sync player list with preferred-media-players setting Writing back to the key was missing. --- src/service.vala | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index a291f2d..cf0f1c2 100644 --- a/src/service.vala +++ b/src/service.vala @@ -165,6 +165,13 @@ public class IndicatorSound.Service { this.player_action_update_id = Idle.add (this.update_player_actions); } + void update_preferred_players () { + var builder = new VariantBuilder (VariantType.STRING_ARRAY); + foreach (var player in this.players) + builder.add ("s", player.id); + this.settings.set_value ("preferred-media-players", builder.end ()); + } + void player_added (MediaPlayer player) { var item = new MenuItem (player.name, player.id); item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); @@ -175,6 +182,8 @@ public class IndicatorSound.Service { this.actions.insert (action); player.notify.connect (this.eventually_update_player_actions); + + this.update_preferred_players (); } void player_removed (MediaPlayer player) { @@ -189,5 +198,7 @@ public class IndicatorSound.Service { break; } } + + this.update_preferred_players (); } } -- cgit v1.2.3 From e0e9e9bc1e6a0bf393134c9fa6927f2fccc525f2 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Apr 2013 17:09:51 +0200 Subject: Don't add players to the root menu --- src/service.vala | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index cf0f1c2..9b9900c 100644 --- a/src/service.vala +++ b/src/service.vala @@ -32,7 +32,8 @@ public class IndicatorSound.Service { this.actions.add_action (this.create_mute_action ()); this.actions.add_action (this.create_volume_action ()); - this.menu = create_base_menu (); + this.menu = create_menu (); + this.root_menu = create_root_menu (this.menu); this.players.sync (settings.get_strv ("preferred-media-players")); this.settings.changed["preferred-media-players"].connect ( () => { @@ -62,6 +63,7 @@ public class IndicatorSound.Service { MainLoop loop; SimpleActionGroup actions; + Menu root_menu; Menu menu; Settings settings; VolumeControl volume_control; @@ -76,16 +78,7 @@ public class IndicatorSound.Service { } } - static Menu create_base_menu () { - var submenu = new Menu (); - submenu.append ("Mute", "indicator.mute"); - - var slider = new MenuItem (null, "indicator.volume"); - slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); - submenu.append_item (slider); - - submenu.append ("Sound Settings…", "indicator.settings"); - + static Menu create_root_menu (Menu submenu) { var root = new MenuItem (null, "indicator.root"); root.set_attribute ("x-canonical-type", "s", "com.canonical.indicator.root"); root.set_submenu (submenu); @@ -96,6 +89,19 @@ public class IndicatorSound.Service { return menu; } + static Menu create_menu () { + var menu = new Menu (); + menu.append ("Mute", "indicator.mute"); + + var slider = new MenuItem (null, "indicator.volume"); + slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); + menu.append_item (slider); + + menu.append ("Sound Settings…", "indicator.settings"); + + return menu; + } + Action create_mute_action () { var mute_action = new SimpleAction.stateful ("mute", null, this.volume_control.mute); @@ -133,7 +139,7 @@ public class IndicatorSound.Service { void bus_acquired (DBusConnection connection, string name) { try { connection.export_action_group ("/com/canonical/indicator/sound", this.actions); - connection.export_menu_model ("/com/canonical/indicator/sound/desktop", this.menu); + connection.export_menu_model ("/com/canonical/indicator/sound/desktop", this.root_menu); } catch (Error e) { critical ("%s", e.message); } -- cgit v1.2.3 From 7fec353c5c10a320eb11fb4757ddeef3f544f4ec Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 9 Apr 2013 11:56:31 +0200 Subject: Add information about the current track to the player actions --- src/media-player.vala | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/service.vala | 7 +++++ 2 files changed, 77 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index 8037ddb..e5f1762 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -65,6 +65,30 @@ public class MediaPlayer: Object { } } + public string state { + get { + if (this.proxy != null && this.proxy.PlaybackStatus == "Playing") + return "Playing"; + else + return "Paused"; + } + } + + public class Track : Object { + public string artist { get; construct; } + public string title { get; construct; } + public string album { get; construct; } + public string art_url { get; construct; } + + public Track (string artist, string title, string album, string art_url) { + Object (artist: artist, title: title, album: album, art_url: art_url); + } + } + + public Track current_track { + get; set; + } + /** * Attach this object to a process of the associated media player. The player must own @dbus_name and * implement the org.mpris.MediaPlayer2.Player interface. @@ -78,7 +102,7 @@ public class MediaPlayer: Object { this._dbus_name = dbus_name; Bus.get_proxy.begin (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", - DBusProxyFlags.NONE, null, got_proxy); + DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_proxy); } /** @@ -114,11 +138,56 @@ public class MediaPlayer: Object { void got_proxy (Object? obj, AsyncResult res) { try { this.proxy = Bus.get_proxy.end (res); + + /* Connecting to GDBusProxy's "g-properties-changed" signal here, because vala's dbus objects don't + * emit notify signals */ + var gproxy = this.proxy as DBusProxy; + gproxy.g_properties_changed.connect (this.proxy_properties_changed); + this.notify_property ("is-running"); + this.notify_property ("state"); + this.update_current_track (gproxy.get_cached_property ("Metadata")); } catch (Error e) { this._dbus_name = null; warning ("unable to attach to media player: %s", e.message); } } + + /* some players (e.g. Spotify) don't follow the spec closely and pass single strings in metadata fields + * where an array of string is expected */ + static string sanitize_metadata_value (Variant? v) { + if (v == null) + return ""; + else if (v.is_of_type (VariantType.STRING)) + return v.get_string (); + else if (v.is_of_type (VariantType.STRING_ARRAY)) + return string.joinv (",", v.get_strv ()); + + warn_if_reached (); + return ""; + } + + void proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { + if (changed_properties.lookup ("PlaybackStatus", "s", null)) + this.notify_property ("state"); + + var metadata = changed_properties.lookup_value ("Metadata", new VariantType ("a{sv}")); + if (metadata != null) + this.update_current_track (metadata); + } + + void update_current_track (Variant metadata) { + if (metadata != null) { + this.current_track = new Track ( + sanitize_metadata_value (metadata.lookup_value ("xesam:artist", null)), + sanitize_metadata_value (metadata.lookup_value ("xesam:title", null)), + sanitize_metadata_value (metadata.lookup_value ("xesam:album", null)), + sanitize_metadata_value (metadata.lookup_value ("mpris:artUrl", null)) + ); + } + else { + this.current_track = null; + } + } } diff --git a/src/service.vala b/src/service.vala index 9b9900c..69ca6d1 100644 --- a/src/service.vala +++ b/src/service.vala @@ -152,6 +152,13 @@ public class IndicatorSound.Service { Variant action_state_for_player (MediaPlayer player) { var builder = new VariantBuilder (new VariantType ("a{sv}")); builder.add ("{sv}", "running", new Variant ("b", player.is_running)); + builder.add ("{sv}", "state", new Variant ("s", player.state)); + if (player.current_track != null) { + builder.add ("{sv}", "title", new Variant ("s", player.current_track.title)); + builder.add ("{sv}", "artist", new Variant ("s", player.current_track.artist)); + builder.add ("{sv}", "album", new Variant ("s", player.current_track.album)); + builder.add ("{sv}", "art-url", new Variant ("s", player.current_track.art_url)); + } return builder.end (); } -- cgit v1.2.3 From f3de5c9a3928dc8ed6154128a8e95718ebb343e8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 9 Apr 2013 16:25:32 +0200 Subject: Forget track information when player is quit --- src/media-player.vala | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index e5f1762..717d135 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -114,6 +114,7 @@ public class MediaPlayer: Object { this.proxy = null; this._dbus_name = null; this.notify_property ("is-running"); + this.current_track = null; } /** -- cgit v1.2.3 From 92cded6d7b87338d81ab3dba62817af8f9b977ba Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 29 May 2013 14:54:13 -0400 Subject: Use new indicator file format --- src/com.canonical.indicator.sound | 6 ++++++ src/com.canonical.sound.indicator | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 src/com.canonical.indicator.sound delete mode 100644 src/com.canonical.sound.indicator (limited to 'src') diff --git a/src/com.canonical.indicator.sound b/src/com.canonical.indicator.sound new file mode 100644 index 0000000..825fd30 --- /dev/null +++ b/src/com.canonical.indicator.sound @@ -0,0 +1,6 @@ +[Indicator Service] +Name=indicator-sound +ObjectPath=/com/canonical/indicator/sound + +[desktop] +ObjectPath=/com/canonical/indicator/sound/desktop diff --git a/src/com.canonical.sound.indicator b/src/com.canonical.sound.indicator deleted file mode 100644 index e2f26b0..0000000 --- a/src/com.canonical.sound.indicator +++ /dev/null @@ -1,4 +0,0 @@ -[Indicator Service] -Name=indicator-sound -BusName=com.canonical.indicator.sound -ObjectPath=/com/canonical/indicator/sound -- cgit v1.2.3 From 26315a5b447b10ac5926c9713e13dfd2383ccb95 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 29 May 2013 14:57:33 -0400 Subject: Change root action state to a{sv} --- src/service.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 69ca6d1..dc3ccbb 100644 --- a/src/service.vala +++ b/src/service.vala @@ -57,7 +57,7 @@ public class IndicatorSound.Service { } const ActionEntry[] action_entries = { - { "root", null, null, "('', 'audio-volume-high-panel', '', true)", null }, + { "root", null, null, "{ 'icon': <'audio-volume-high-panel'> }", null }, { "settings", activate_settings, null, null, null } }; -- cgit v1.2.3 From 36272c5bdb7b66e1a8c6c6e96d4b9d45c5e6e600 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 31 May 2013 19:17:46 -0400 Subject: Set attributes describing the slider menu item --- src/service.vala | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index dc3ccbb..468c80f 100644 --- a/src/service.vala +++ b/src/service.vala @@ -17,6 +17,9 @@ * Lars Uebernickel */ +/* Icon.serialize() is not yet in gio-2.0.vapi; remove this when it is */ +extern Variant? g_icon_serialize (Icon icon); + public class IndicatorSound.Service { public Service () { this.settings = new Settings ("com.canonical.indicator.sound"); @@ -90,11 +93,17 @@ public class IndicatorSound.Service { } static Menu create_menu () { + var menu = new Menu (); menu.append ("Mute", "indicator.mute"); var slider = new MenuItem (null, "indicator.volume"); slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); + slider.set_attribute_value ("primary-icon", g_icon_serialize (new ThemedIcon ("audio-volume-low-zero-panel"))); + slider.set_attribute_value ("secondary-icon", g_icon_serialize (new ThemedIcon ("audio-volume-high-panel"))); + slider.set_attribute ("min-value", "d", 0.0); + slider.set_attribute ("max-value", "d", 1.0); + slider.set_attribute ("step", "d", 0.01); menu.append_item (slider); menu.append ("Sound Settings…", "indicator.settings"); -- cgit v1.2.3 From 438516dce0eb6638a867b549fa1381280310d369 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 7 Jun 2013 16:57:49 -0400 Subject: Use {min,max}-icon instead of {primary,secondary}-icon --- src/service.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 468c80f..af718f9 100644 --- a/src/service.vala +++ b/src/service.vala @@ -99,8 +99,8 @@ public class IndicatorSound.Service { var slider = new MenuItem (null, "indicator.volume"); slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); - slider.set_attribute_value ("primary-icon", g_icon_serialize (new ThemedIcon ("audio-volume-low-zero-panel"))); - slider.set_attribute_value ("secondary-icon", g_icon_serialize (new ThemedIcon ("audio-volume-high-panel"))); + slider.set_attribute_value ("min-icon", g_icon_serialize (new ThemedIcon ("audio-volume-low-zero-panel"))); + slider.set_attribute_value ("max-icon", g_icon_serialize (new ThemedIcon ("audio-volume-high-panel"))); slider.set_attribute ("min-value", "d", 0.0); slider.set_attribute ("max-value", "d", 1.0); slider.set_attribute ("step", "d", 0.01); -- cgit v1.2.3 From 4f98702c1012ee633f477c7218d9aaf15c5313c6 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 13 Jun 2013 14:41:06 -0400 Subject: Use 'indicator.' prefix for player menu items --- src/service.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index af718f9..d42e267 100644 --- a/src/service.vala +++ b/src/service.vala @@ -195,7 +195,7 @@ public class IndicatorSound.Service { } void player_added (MediaPlayer player) { - var item = new MenuItem (player.name, player.id); + var item = new MenuItem (player.name, "indicator." + player.id); item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); this.menu.insert_item (this.menu.get_n_items () -1, item); -- cgit v1.2.3 From 911f43fa48dd296006963ee5e5e956cbfd509ffc Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 13 Jun 2013 14:41:41 -0400 Subject: Player menu item: export application icon --- src/service.vala | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index d42e267..945dc6f 100644 --- a/src/service.vala +++ b/src/service.vala @@ -197,6 +197,7 @@ public class IndicatorSound.Service { void player_added (MediaPlayer player) { var item = new MenuItem (player.name, "indicator." + player.id); item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); + item.set_attribute_value ("icon", g_icon_serialize (player.icon)); this.menu.insert_item (this.menu.get_n_items () -1, item); SimpleAction action = new SimpleAction.stateful (player.id, null, this.action_state_for_player (player)); -- cgit v1.2.3 From f7ff900fadff1277be47f0d772c6a84cfaa185a2 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 13 Jun 2013 15:15:20 -0400 Subject: Give players their own section in the menu --- src/service.vala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 945dc6f..22f77c3 100644 --- a/src/service.vala +++ b/src/service.vala @@ -195,10 +195,14 @@ public class IndicatorSound.Service { } void player_added (MediaPlayer player) { - var item = new MenuItem (player.name, "indicator." + player.id); - item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); - item.set_attribute_value ("icon", g_icon_serialize (player.icon)); - this.menu.insert_item (this.menu.get_n_items () -1, item); + var player_item = new MenuItem (player.name, "indicator." + player.id); + player_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); + player_item.set_attribute_value ("icon", g_icon_serialize (player.icon)); + + var section = new Menu (); + section.append_item (player_item); + + this.menu.insert_section (this.menu.get_n_items () -1, null, section); SimpleAction action = new SimpleAction.stateful (player.id, null, this.action_state_for_player (player)); action.activate.connect ( () => { player.launch (); }); @@ -214,8 +218,9 @@ public class IndicatorSound.Service { int n = this.menu.get_n_items (); for (int i = 0; i < n; i++) { + var section = this.menu.get_item_link (i, Menu.LINK_SECTION); string action; - this.menu.get_item_attribute (i, "action", "s", out action); + section.get_item_attribute (0, "action", "s", out action); if (action == player.id) { this.menu.remove (i); break; -- cgit v1.2.3 From 9b3162f7aeb23197f6bde643a897c64bd1133986 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 13 Jun 2013 15:16:53 -0400 Subject: Put mute and volume slider into a single section To avoid a separator between them. --- src/service.vala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 22f77c3..fe95ab8 100644 --- a/src/service.vala +++ b/src/service.vala @@ -93,9 +93,8 @@ public class IndicatorSound.Service { } static Menu create_menu () { - - var menu = new Menu (); - menu.append ("Mute", "indicator.mute"); + var volume_section = new Menu (); + volume_section.append ("Mute", "indicator.mute"); var slider = new MenuItem (null, "indicator.volume"); slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); @@ -104,8 +103,10 @@ public class IndicatorSound.Service { slider.set_attribute ("min-value", "d", 0.0); slider.set_attribute ("max-value", "d", 1.0); slider.set_attribute ("step", "d", 0.01); - menu.append_item (slider); + volume_section.append_item (slider); + var menu = new Menu (); + menu.append_section (null, volume_section); menu.append ("Sound Settings…", "indicator.settings"); return menu; -- cgit v1.2.3 From a0fae2ec3183d1d9a651b38d0e2980c029cd7d28 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 17 Jun 2013 12:18:36 -0400 Subject: Export playback menu item --- src/media-player.vala | 34 +++++++++++++++++++++++++++++++++- src/service.vala | 24 +++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index 717d135..b767268 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -74,6 +74,12 @@ public class MediaPlayer: Object { } } + public bool is_playing { + get { + return this.proxy != null && this.proxy.PlaybackStatus == "Playing"; + } + } + public class Track : Object { public string artist { get; construct; } public string title { get; construct; } @@ -132,6 +138,30 @@ public class MediaPlayer: Object { } } + /** + * Toggles playing status. + */ + public void play_pause () { + if (this.proxy != null) + this.proxy.PlayPause.begin (); + } + + /** + * Skips to the next track. + */ + public void next () { + if (this.proxy != null) + this.proxy.Next.begin (); + } + + /** + * Skips to the previous track. + */ + public void previous () { + if (this.proxy != null) + this.proxy.Previous.begin (); + } + DesktopAppInfo appinfo; MprisPlayer? proxy; string _dbus_name; @@ -170,8 +200,10 @@ public class MediaPlayer: Object { } void proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { - if (changed_properties.lookup ("PlaybackStatus", "s", null)) + if (changed_properties.lookup ("PlaybackStatus", "s", null)) { this.notify_property ("state"); + this.notify_property ("is-running"); + } var metadata = changed_properties.lookup_value ("Metadata", new VariantType ("a{sv}")); if (metadata != null) diff --git a/src/service.vala b/src/service.vala index fe95ab8..ad9698e 100644 --- a/src/service.vala +++ b/src/service.vala @@ -61,7 +61,7 @@ public class IndicatorSound.Service { const ActionEntry[] action_entries = { { "root", null, null, "{ 'icon': <'audio-volume-high-panel'> }", null }, - { "settings", activate_settings, null, null, null } + { "settings", activate_settings, null, null, null }, }; MainLoop loop; @@ -200,8 +200,15 @@ public class IndicatorSound.Service { player_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); player_item.set_attribute_value ("icon", g_icon_serialize (player.icon)); + var playback_item = new MenuItem (null, null); + playback_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.playback-item"); + playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id); + playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id); + playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id); + var section = new Menu (); section.append_item (player_item); + section.append_item (playback_item); this.menu.insert_section (this.menu.get_n_items () -1, null, section); @@ -209,6 +216,18 @@ public class IndicatorSound.Service { action.activate.connect ( () => { player.launch (); }); this.actions.insert (action); + var play_action = new SimpleAction.stateful ("play." + player.id, null, player.is_playing); + play_action.activate.connect ( () => player.play_pause () ); + this.actions.insert (play_action); + + var next_action = new SimpleAction ("next." + player.id, null); + next_action.activate.connect ( () => player.next () ); + this.actions.insert (next_action); + + var prev_action = new SimpleAction ("previous." + player.id, null); + prev_action.activate.connect ( () => player.previous () ); + this.actions.insert (prev_action); + player.notify.connect (this.eventually_update_player_actions); this.update_preferred_players (); @@ -216,6 +235,9 @@ public class IndicatorSound.Service { void player_removed (MediaPlayer player) { this.actions.remove (player.id); + this.actions.remove ("play." + player.id); + this.actions.remove ("next." + player.id); + this.actions.remove ("previous." + player.id); int n = this.menu.get_n_items (); for (int i = 0; i < n; i++) { -- cgit v1.2.3 From a44eabf4b4972160ead5285a993650a42af0c1bc Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 17 Jun 2013 18:01:43 -0400 Subject: Change playback action state to "string" To accomodate for a third state "Launching" (in addition to "Playing" and "Paused"). --- src/media-player.vala | 20 +++++--------------- src/service.vala | 6 +++++- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index b767268..5b59efb 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -66,18 +66,7 @@ public class MediaPlayer: Object { } public string state { - get { - if (this.proxy != null && this.proxy.PlaybackStatus == "Playing") - return "Playing"; - else - return "Paused"; - } - } - - public bool is_playing { - get { - return this.proxy != null && this.proxy.PlaybackStatus == "Playing"; - } + get; private set; default = "Paused"; } public class Track : Object { @@ -120,6 +109,7 @@ public class MediaPlayer: Object { this.proxy = null; this._dbus_name = null; this.notify_property ("is-running"); + this.state = "Paused"; this.current_track = null; } @@ -136,6 +126,7 @@ public class MediaPlayer: Object { catch (Error e) { warning ("unable to launch %s: %s", appinfo.get_name (), e.message); } + this.state = "Launching"; } /** @@ -176,7 +167,7 @@ public class MediaPlayer: Object { gproxy.g_properties_changed.connect (this.proxy_properties_changed); this.notify_property ("is-running"); - this.notify_property ("state"); + this.state = this.proxy.PlaybackStatus; this.update_current_track (gproxy.get_cached_property ("Metadata")); } catch (Error e) { @@ -201,8 +192,7 @@ public class MediaPlayer: Object { void proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { if (changed_properties.lookup ("PlaybackStatus", "s", null)) { - this.notify_property ("state"); - this.notify_property ("is-running"); + this.state = this.proxy.PlaybackStatus; } var metadata = changed_properties.lookup_value ("Metadata", new VariantType ("a{sv}")); diff --git a/src/service.vala b/src/service.vala index ad9698e..269ddd7 100644 --- a/src/service.vala +++ b/src/service.vala @@ -216,9 +216,13 @@ public class IndicatorSound.Service { action.activate.connect ( () => { player.launch (); }); this.actions.insert (action); - var play_action = new SimpleAction.stateful ("play." + player.id, null, player.is_playing); + var play_action = new SimpleAction.stateful ("play." + player.id, null, player.state); play_action.activate.connect ( () => player.play_pause () ); this.actions.insert (play_action); + player.notify.connect ( (object, pspec) => { + if (pspec.name == "state") + play_action.set_state (player.state); + }); var next_action = new SimpleAction ("next." + player.id, null); next_action.activate.connect ( () => player.next () ); -- cgit v1.2.3 From c6f14a086c0c1160293d1f4769e3bed9a92b674c Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 17 Jun 2013 18:30:59 -0400 Subject: Mpris2Watcher: use match rules to avoid unnecessary wakeups Previously, indicator-sound-service was waking up every time the owner of any name on the bus c Merged from trunk. --- src/mpris2-watcher.vala | 77 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 105c797..4ab75c8 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -21,8 +21,8 @@ using Xml; public class Mpris2Watcher : GLib.Object { - FreeDesktopObject fdesktop_obj; - + DBusConnection session_bus; + public signal void client_appeared ( string desktop_file_name, string dbus_name, bool use_playlists ); @@ -33,18 +33,37 @@ public class Mpris2Watcher : GLib.Object } construct - { + { + const string match_rule = "type='signal'," + + "sender='org.freedesktop.DBus'," + + "interface='org.freedesktop.DBus'," + + "member='NameOwnerChanged'," + + "path='/org/freedesktop/DBus'," + + "arg0namespace='org.mpris.MediaPlayer2'"; try { - this.fdesktop_obj = Bus.get_proxy_sync ( BusType.SESSION, - FREEDESKTOP_SERVICE, - FREEDESKTOP_OBJECT, - DBusProxyFlags.DO_NOT_LOAD_PROPERTIES ); - this.fdesktop_obj.name_owner_changed.connect (this.name_changes_detected); + this.session_bus = Bus.get_sync (BusType.SESSION); + + this.session_bus.call_sync ("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "AddMatch", + new Variant ("(s)", match_rule), + VariantType.TUPLE, + DBusCallFlags.NONE, + -1); + + this.session_bus.signal_subscribe ("org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + "/org/freedesktop/DBus", + null, + DBusSignalFlags.NO_MATCH_RULE, + this.name_owner_changed); + this.check_for_active_clients.begin(); } - catch ( IOError e ){ - warning( "Mpris2watcher could not set up a watch for mpris clients appearing on the bus: %s", - e.message ); + catch (GLib.Error e) { + warning ("unable to set up name watch for mrpis clients: %s", e.message); } } @@ -52,16 +71,25 @@ public class Mpris2Watcher : GLib.Object // More relevant for development and daemon's like mpd. public async void check_for_active_clients() { - string[] interfaces; - try{ - interfaces = yield this.fdesktop_obj.list_names(); + Variant interfaces; + + try { + interfaces = yield this.session_bus.call ("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "ListNames", + null, + new VariantType ("(as)"), + DBusCallFlags.NONE, + -1); } - catch ( IOError e) { - warning( "Mpris2watcher could fetch active interfaces at startup: %s", - e.message ); + catch (GLib.Error e) { + warning ("unable to search for existing mpris clients: %s ", e.message); return; } - foreach (var address in interfaces) { + + foreach (var val in interfaces.get_child_value (0)) { + var address = (string) val; if (address.has_prefix (MPRIS_PREFIX)){ MprisRoot? mpris2_root = this.create_mpris_root(address); if (mpris2_root == null) return; @@ -71,13 +99,16 @@ public class Mpris2Watcher : GLib.Object } } - private void name_changes_detected ( FreeDesktopObject dbus_obj, - string name, - string previous_owner, - string current_owner ) + public void name_owner_changed (DBusConnection con, string sender, string object_path, + string interface_name, string signal_name, Variant parameters) { - MprisRoot? mpris2_root = this.create_mpris_root(name); + string name, previous_owner, current_owner; + + message ("xyxp"); + + parameters.get ("(sss)", out name, out previous_owner, out current_owner); + MprisRoot? mpris2_root = this.create_mpris_root (name); if (mpris2_root == null) return; if (previous_owner != "" && current_owner == "") { -- cgit v1.2.3 From cb8cb6ef16fc0884d2499421fbeeca12e3288b56 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Jun 2013 08:03:10 -0400 Subject: Remove obsolete files (gtk and dbusmenu) --- src/Makefile.am | 5 - src/dbus-shared-names.h | 37 --- src/fetch-file.vala | 86 ----- src/metadata-menu-item.vala | 220 ------------ src/mpris2-controller.vala | 286 ---------------- src/mpris2-watcher.vala | 2 - src/music-player-bridge.vala | 305 ----------------- src/mute-menu-item.c | 154 --------- src/mute-menu-item.h | 59 ---- src/player-controller.vala | 247 -------------- src/player-item.vala | 104 ------ src/playlists-menu-item.vala | 154 --------- src/pulseaudio-mgr.c | 717 ---------------------------------------- src/pulseaudio-mgr.h | 33 -- src/scrub-menu-item.vala | 56 ---- src/settings-manager.vala | 136 -------- src/slider-menu-item.c | 258 --------------- src/slider-menu-item.h | 64 ---- src/sound-state.c | 43 --- src/sound-state.h | 31 -- src/specific-items-manager.vala | 109 ------ src/transport-menu-item.vala | 97 ------ src/voip-input-menu-item.c | 278 ---------------- 23 files changed, 3481 deletions(-) delete mode 100644 src/dbus-shared-names.h delete mode 100644 src/fetch-file.vala delete mode 100644 src/metadata-menu-item.vala delete mode 100644 src/mpris2-controller.vala delete mode 100644 src/music-player-bridge.vala delete mode 100644 src/mute-menu-item.c delete mode 100644 src/mute-menu-item.h delete mode 100644 src/player-controller.vala delete mode 100644 src/player-item.vala delete mode 100644 src/playlists-menu-item.vala delete mode 100644 src/pulseaudio-mgr.c delete mode 100644 src/pulseaudio-mgr.h delete mode 100644 src/scrub-menu-item.vala delete mode 100644 src/settings-manager.vala delete mode 100644 src/slider-menu-item.c delete mode 100644 src/slider-menu-item.h delete mode 100644 src/sound-state.c delete mode 100644 src/sound-state.h delete mode 100644 src/specific-items-manager.vala delete mode 100644 src/transport-menu-item.vala delete mode 100644 src/voip-input-menu-item.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 083c5c2..84ee0f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,6 @@ libexec_PROGRAMS = indicator-sound-service indicator_sound_service_SOURCES = \ - common-defs.h \ service.vala \ main.vala \ volume-control.vala \ @@ -16,13 +15,9 @@ indicator_sound_service_VALAFLAGS = \ --vapidir=$(top_srcdir)/vapi/ \ --vapidir=./ \ --thread \ - --pkg gee-1.0 \ - --pkg Dbusmenu-0.4 \ - --pkg common-defs \ --pkg config \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ - --pkg gdk-pixbuf-2.0 \ --pkg libxml-2.0 \ --pkg libpulse \ --pkg libpulse-mainloop-glib \ diff --git a/src/dbus-shared-names.h b/src/dbus-shared-names.h deleted file mode 100644 index 2517eb9..0000000 --- a/src/dbus-shared-names.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2010 Canonical Ltd. - -Authors: - Conor Curran - Ted Gould - -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 . -*/ - - -#ifndef __DBUS_SHARED_NAMES_H__ -#define __DBUS_SHARED_NAMES_H__ - -#define INDICATOR_SOUND_DBUS_NAME "com.canonical.indicator.sound" -#define INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH "/com/canonical/indicator/sound/menu" -#define INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH "/com/canonical/indicator/sound/service" -#define INDICATOR_SOUND_DBUS_INTERFACE "com.canonical.indicator.sound" -#define INDICATOR_SOUND_DBUS_VERSION 0 - -#define INDICATOR_SOUND_SIGNAL_STATE_UPDATE "SoundStateUpdate" - - -#endif /* __DBUS_SHARED_NAMES_H__ */ diff --git a/src/fetch-file.vala b/src/fetch-file.vala deleted file mode 100644 index e94afef..0000000 --- a/src/fetch-file.vala +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 Canonical, Ltd. - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License - * version 3.0 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License version 3.0 for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - * - * Authors - * Gordon Allott - * Conor Curran - */ - -public class FetchFile : Object -{ - /* public variables */ - public string uri {get; construct;} - public string intended_property {get; construct;} - - /* private variables */ - private DataInputStream stream; - private File? file; - private ByteArray data; - - /* public signals */ - public signal void failed (); - public signal void completed (ByteArray data, string property); - - public FetchFile (string uri, string prop) - { - Object (uri: uri, intended_property: prop); - } - - construct - { - this.file = File.new_for_uri(this.uri); - this.data = new ByteArray (); - } - - public async void fetch_data () - { - try { - this.stream = new DataInputStream(this.file.read(null)); - this.stream.set_byte_order (DataStreamByteOrder.LITTLE_ENDIAN); - } catch (GLib.Error e) { - this.failed (); - } - this.read_something_async (); - } - - private async void read_something_async () - { - ssize_t size = 1024; - uint8[] buffer = new uint8[size]; - - ssize_t bufsize = 1; - do { - try { - bufsize = yield this.stream.read_async (buffer, GLib.Priority.DEFAULT, null); - if (bufsize < 1) { break;} - - if (bufsize != size) - { - uint8[] cpybuf = new uint8[bufsize]; - Memory.copy (cpybuf, buffer, bufsize); - this.data.append (cpybuf); - } - else - { - this.data.append (buffer); - } - } catch (Error e) { - this.failed (); - } - } while (bufsize > 0); - this.completed (this.data, this.intended_property); - } -} diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala deleted file mode 100644 index b2dd752..0000000 --- a/src/metadata-menu-item.vala +++ /dev/null @@ -1,220 +0,0 @@ -/* -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 . -*/ - -using Gee; -using Dbusmenu; -using DbusmenuMetadata; -using Gdk; - -public class MetadataMenuitem : PlayerItem -{ - public const string ALBUM_ART_DIR_SUFFIX = "indicator/sound/album-art-cache"; - - public static string album_art_cache_dir; - private static FetchFile fetcher; - private string previous_temp_album_art_path; - - public MetadataMenuitem (PlayerController parent) - { - Object(item_type: MENUITEM_TYPE, owner: parent); - } - - construct{ - MetadataMenuitem.clean_album_art_temp_dir(); - this.previous_temp_album_art_path = null; - this.album_art_cache_dir = MetadataMenuitem.create_album_art_temp_dir(); - //debug ("JUST ABOUT TO ATTEMPT PLAYER NAME SETTING %s", this.owner.app_info.get_name()); - this.property_set (MENUITEM_PLAYER_NAME, this.owner.app_info.get_name()); - this.property_set (MENUITEM_PLAYER_ICON, this.owner.icon_name); - this.property_set_bool (MENUITEM_PLAYER_RUNNING, false); - this.property_set_bool (MENUITEM_HIDE_TRACK_DETAILS, true); - reset (relevant_attributes_for_ui()); - } - - private static void clean_album_art_temp_dir() - { - string path = GLib.Path.build_filename(Environment.get_user_cache_dir(), ALBUM_ART_DIR_SUFFIX); - - GLib.File? album_art_dir = GLib.File.new_for_path(path); - - if(delete_album_art_contents(album_art_dir) == false) - { - warning("could not remove the temp album art files %s", path); - } - } - - private static string? create_album_art_temp_dir() - { - string path = GLib.Path.build_filename(Environment.get_user_cache_dir(), ALBUM_ART_DIR_SUFFIX); - if(DirUtils.create_with_parents(path, 0700) == -1){ - warning("could not create temp dir %s for remote album art, it must have been created already", path); - } - return path; - } - - private static bool delete_album_art_contents (GLib.File dir) - { - bool result = true; - try { - var e = dir.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, - FileQueryInfoFlags.NOFOLLOW_SYMLINKS, - null); - while (true) - { - var file = e.next_file (null); - - if (file == null) - break; - - debug("file name = %s", file.get_name()); - - var child = dir.get_child (file.get_name ()); - - try { - child.delete (null); - } catch (Error error_) { - warning (@"Unable to delete file '$(child.get_basename ()): $(error_.message)"); - result = false; - } - } - } catch (Error error) { - warning (@"Unable to read files from directory '$(dir.get_basename ())': %s", - error.message); - result = false; - } - return result; - } - - public void fetch_art(string uri, string prop) - { - File art_file = File.new_for_uri(uri); - if (art_file.is_native() == true){ - if (art_file.query_exists() == false){ - // Can't load the image, set prop to empty and return. - this.property_set_int ( prop, EMPTY ); - return; - } - string path; - try{ - path = Filename.from_uri ( uri.strip() ); - debug ("Populating the artwork field with %s", uri.strip()); - this.property_set ( prop, path ); - } - catch(ConvertError e){ - warning("Problem converting URI %s to file path", - uri); - } - // eitherway return, the artwork was local - return; - } - debug("fetch_art -remotely %s", this.album_art_cache_dir); - // If we didn't manage to create the temp dir - // don't bother with remote - if(this.album_art_cache_dir == null){ - return; - } - // green light to go remote - this.fetcher = new FetchFile (uri, prop); - this.fetcher.failed.connect (() => { this.on_fetcher_failed ();}); - this.fetcher.completed.connect (this.on_fetcher_completed); - this.fetcher.fetch_data (); - } - - private void on_fetcher_failed () - { - warning("on_fetcher_failed -> could not fetch artwork"); - } - - private void on_fetcher_completed(ByteArray update, string property) - { - try{ - PixbufLoader loader = new PixbufLoader (); - loader.write (update.data); - loader.close (); - Pixbuf icon = loader.get_pixbuf (); - string path = this.album_art_cache_dir.concat("/downloaded-coverart-XXXXXX"); - int r = FileUtils.mkstemp(path); - if(r != -1){ - icon.save (path, loader.get_format().get_name()); - this.property_set(property, path); - if(this.previous_temp_album_art_path != null){ - FileUtils.remove(this.previous_temp_album_art_path); - } - this.previous_temp_album_art_path = path; - } - } - catch(GLib.Error e){ - warning("Problem creating file from bytearray fetched from the interweb - error: %s", - e.message); - } - } - - public override void handle_event (string name, - Variant input_value, - uint timestamp) - { - if(this.owner.current_state == PlayerController.state.OFFLINE) - { - this.owner.instantiate(); - } - else if(this.owner.current_state == PlayerController.state.CONNECTED){ - this.owner.mpris_bridge.expose(); - } - } - - public void alter_label (string? new_title) - { - if (new_title == null) return; - this.property_set (MENUITEM_PLAYER_NAME, new_title); - } - - public void toggle_active_triangle (bool update) - { - debug ("toggle active triangle"); - this.property_set_bool (MENUITEM_PLAYER_RUNNING, update); - } - - public void should_collapse(bool collapse) - { - this.property_set_bool (MENUITEM_HIDE_TRACK_DETAILS, collapse); - } - - public static HashSet attributes_format() - { - HashSet attrs = new HashSet(); - attrs.add(MENUITEM_TITLE); - attrs.add(MENUITEM_ARTIST); - attrs.add(MENUITEM_ALBUM); - attrs.add(MENUITEM_ARTURL); - attrs.add(MENUITEM_PLAYER_NAME); - attrs.add(MENUITEM_PLAYER_ICON); - attrs.add(MENUITEM_PLAYER_RUNNING); - return attrs; - } - - public static HashSet relevant_attributes_for_ui() - { - HashSet attrs = new HashSet(); - attrs.add(MENUITEM_TITLE); - attrs.add(MENUITEM_ARTIST); - attrs.add(MENUITEM_ALBUM); - attrs.add(MENUITEM_ARTURL); - return attrs; - } -} diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala deleted file mode 100644 index 2cc8d12..0000000 --- a/src/mpris2-controller.vala +++ /dev/null @@ -1,286 +0,0 @@ -/* -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 . -*/ -using Dbusmenu; -using Transport; - -public class Mpris2Controller : GLib.Object -{ - public const int MAX_PLAYLIST_COUNT = 100; - - public MprisRoot mpris2_root {get; construct;} - public MprisPlayer player {get; construct;} - public MprisPlaylists playlists {get; construct;} - public FreeDesktopProperties properties_interface {get; construct;} - public PlayerController owner {get; construct;} - - public Mpris2Controller(PlayerController ctrl) - { - GLib.Object(owner: ctrl); - } - - construct{ - try { - this.mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, - this.owner.dbus_name, - "/org/mpris/MediaPlayer2" ); - this.player = Bus.get_proxy_sync ( BusType.SESSION, - this.owner.dbus_name, - "/org/mpris/MediaPlayer2" ); - this.properties_interface = Bus.get_proxy_sync ( BusType.SESSION, - "org.freedesktop.Properties.PropertiesChanged", - "/org/mpris/MediaPlayer2" ); - this.properties_interface.PropertiesChanged.connect ( property_changed_cb ); - if ( this.owner.use_playlists == true ){ - this.playlists = Bus.get_proxy_sync ( BusType.SESSION, - this.owner.dbus_name, - "/org/mpris/MediaPlayer2" ); - this.playlists.PlaylistChanged.connect (on_playlistdetails_changed); - } - } - catch (IOError e) { - critical("Problems connecting to the session bus - %s", e.message); - } - } - /* - * property_changed_cb - * Called when a property changed signal is emitted from any of mpris - * objects on the bus. - * Note that the signal will be received by each instance for each player - * and at that moment there is no way to know what player that signal - * came from therefore it is necessary to query each relevant property - * to update the respective dbusmenuitem property inorder to keep the UI in sync - * Please also note due to some race condition in the depths of gdbus - * a timeout is needed between receiving the prop update and query the respective property. - * This can be seen at various points below. - */ - public void property_changed_cb ( string interface_source, - HashTable changed_properties, - string[] invalid ) - { - if ( changed_properties == null || - interface_source.has_prefix ( MPRIS_PREFIX ) == false ){ - warning("Property-changed hash is null or this is an interface that doesn't concern us"); - return; - } - Variant? play_v = changed_properties.lookup("PlaybackStatus"); - if(play_v != null){ - string state = this.player.PlaybackStatus; - Timeout.add ( 200, ensure_correct_playback_status ); - Transport.State p = (Transport.State)this.determine_play_state(state); - (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(p); - } - Variant? meta_v = changed_properties.lookup("Metadata"); - if(meta_v != null) - { - Timeout.add ( 200, ensure_correct_metadata ); - } - Variant? playlist_v = changed_properties.lookup("ActivePlaylist"); - if ( playlist_v != null && this.owner.use_playlists == true ){ - Timeout.add (500, this.fetch_active_playlist); - } - Variant? playlist_count_v = changed_properties.lookup("PlaylistCount"); - if ( playlist_count_v != null && this.owner.use_playlists == true ){ - this.fetch_playlists.begin(); - this.fetch_active_playlist(); - } - Variant? playlist_orderings_v = changed_properties.lookup("Orderings"); - if ( playlist_orderings_v != null && this.owner.use_playlists == true ){ - this.fetch_playlists.begin(); - this.fetch_active_playlist(); - } - Variant? identity_v = changed_properties.lookup("Identity"); - if (identity_v != null){ - MetadataMenuitem md = this.owner.custom_items[PlayerController.widget_order.METADATA] as MetadataMenuitem; - md.alter_label (this.mpris2_root.Identity); - } - } - - private bool ensure_correct_metadata () - { - GLib.HashTable changed_updates = clean_metadata(); - PlayerItem metadata = this.owner.custom_items[PlayerController.widget_order.METADATA]; - metadata.reset (MetadataMenuitem.relevant_attributes_for_ui()); - metadata.update ( changed_updates, - MetadataMenuitem.relevant_attributes_for_ui()); - MetadataMenuitem md = this.owner.custom_items[PlayerController.widget_order.METADATA] as MetadataMenuitem; - bool collapsing = !metadata.populated(MetadataMenuitem.relevant_attributes_for_ui()); - md.should_collapse(collapsing); - - return false; - } - - private bool ensure_correct_playback_status() - { - Transport.State p = (Transport.State)this.determine_play_state(this.player.PlaybackStatus); - (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(p); - return false; - } - - private GLib.HashTable? clean_metadata() - { - GLib.HashTable changed_updates = this.player.Metadata; - - Variant? artist_v = this.player.Metadata.lookup("xesam:artist"); - if(artist_v != null){ - string display_artists; - // Accomodate Spotify (should return 'as' and not 's') - if(artist_v.get_type_string() == "s"){ - display_artists = artist_v.get_string(); - } - else{ - string[] artists = artist_v.dup_strv(); - display_artists = string.joinv(", ", artists); - } - changed_updates.replace("xesam:artist", display_artists); - } - return changed_updates; - } - - private Transport.State determine_play_state(string? status){ - if(status != null && status == "Playing"){ - return Transport.State.PLAYING; - } - return Transport.State.PAUSED; - } - - public void initial_update() - { - Transport.State update; - - if(this.player.PlaybackStatus == null){ - update = Transport.State.PAUSED; - } - else{ - update = determine_play_state (this.player.PlaybackStatus); - } - if (this.mpris2_root.Identity != null){ - MetadataMenuitem md = this.owner.custom_items[PlayerController.widget_order.METADATA] as MetadataMenuitem; - md.alter_label (this.mpris2_root.Identity); - } - (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state (update); - GLib.HashTable? cleaned_metadata = this.clean_metadata(); - this.owner.custom_items[PlayerController.widget_order.METADATA].update (cleaned_metadata, - MetadataMenuitem.attributes_format()); - - if ( this.owner.use_playlists == true ){ - this.fetch_playlists.begin(); - this.fetch_active_playlist(); - } - } - - public void transport_update(Transport.Action command) - { - if(command == Transport.Action.PLAY_PAUSE){ - this.player.PlayPause.begin(); - } - else if(command == Transport.Action.PREVIOUS){ - this.player.Previous.begin(); - } - else if(command == Transport.Action.NEXT){ - this.player.Next.begin(); - } - else if(command == Transport.Action.REWIND){ - this.player.Seek.begin(-500000); - } - else if(command == Transport.Action.FORWIND){ - this.player.Seek.begin(400000); - } - } - - public bool connected() - { - return (this.player != null && this.mpris2_root != null); - } - - public void expose() - { - if(this.connected() == true){ - this.mpris2_root.Raise.begin(); - } - } - - private void on_playlistdetails_changed (PlaylistDetails details) - { - PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; - playlists_item.update_individual_playlist (details); - } - - public async void fetch_playlists() - { - PlaylistDetails[] current_playlists = null; - - try{ - current_playlists = yield this.playlists.GetPlaylists (0, - MAX_PLAYLIST_COUNT, - "Alphabetical", - false); - } - catch (IOError e){ - return; - } - - if( current_playlists != null ){ - PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; - playlists_item.update(current_playlists); - } - else{ - warning(" Playlists are on but %s is returning no current_playlists ?", - this.owner.app_info.get_name()); - this.owner.use_playlists = false; - } - } - - private bool validate_playlists_details() - { - if (this.playlists.ActivePlaylist == null){ - return false; - } - if (this.playlists.ActivePlaylist.valid == false){ - return false; - } - if (this.playlists.ActivePlaylist.details == null){ - return false; - } - if (this.playlists.ActivePlaylist.details.path == null || - this.playlists.ActivePlaylist.details.name == null){ - return false; - } - return true; - } - - private bool fetch_active_playlist() - { - if (this.validate_playlists_details() == false){ - return false; - } - PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; - playlists_item.active_playlist_update ( this.playlists.ActivePlaylist.details ); - return false; - } - - public void activate_playlist (ObjectPath path) - { - try{ - this.playlists.ActivatePlaylist.begin(path); - } - catch(IOError e){ - warning ("Could not activate playlist %s because %s", (string)path, e.message); - } - } -} diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 4ab75c8..740089d 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -104,8 +104,6 @@ public class Mpris2Watcher : GLib.Object { string name, previous_owner, current_owner; - message ("xyxp"); - parameters.get ("(sss)", out name, out previous_owner, out current_owner); MprisRoot? mpris2_root = this.create_mpris_root (name); diff --git a/src/music-player-bridge.vala b/src/music-player-bridge.vala deleted file mode 100644 index 59ced36..0000000 --- a/src/music-player-bridge.vala +++ /dev/null @@ -1,305 +0,0 @@ -/* -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 . -*/ - -using Dbusmenu; -using Gee; -using GLib; - -public class MusicPlayerBridge : GLib.Object -{ - const int DEVICE_ITEMS_COUNT = 3; - - private SettingsManager settings_manager; - private Dbusmenu.Menuitem root_menu; - private HashMap registered_clients; - private HashMap file_monitors; - private HashMap mpris_to_desktop; - private Mpris2Watcher watcher; - - public MusicPlayerBridge() - { - } - - construct{ - this.registered_clients = new HashMap (); - this.file_monitors = new HashMap (); - this.mpris_to_desktop = new HashMap (); - this.settings_manager = new SettingsManager(); - this.settings_manager.blacklist_updates.connect ( this.on_blacklist_update ); - this.settings_manager.preferred_updates.connect ( this.on_preferred_update ); - } - - private void on_blacklist_update ( string[] blacklist ) - { - debug("some blacklist update"); - - foreach(var desktop_id in blacklist){ - string key = desktop_id; - if (this.registered_clients.has_key (key)){ - debug ("Apparently %s is now blacklisted - remove thy self", key); - this.registered_clients[key].remove_from_menu(); - this.registered_clients.unset (key); - } - } - // double check present players to ensure dynamic removal/addition - this.watcher.check_for_active_clients.begin(); - } - - private void on_preferred_update ( Gee.ArrayList preferred ) - { - debug ("Preferred players update. Clearing current preferred players..."); - - foreach (var player_controller in this.registered_clients.values) { - player_controller.set_as_preferred (false); - } - - foreach (var desktop_id in preferred) { - string key = desktop_id; - if (this.registered_clients.has_key (key)) { - debug ("Setting %s as preferred player", key); - this.registered_clients[key].set_as_preferred (true); - } - } - } - - private void try_to_add_inactive_familiar_clients() - { - var preferred_players = this.settings_manager.fetch_preferred (); - foreach ( string desktop in this.settings_manager.fetch_interested()){ - debug ( "interested client found : %s", desktop ); - AppInfo? app_info = create_app_info ( desktop.concat( ".desktop" ) ); - if ( app_info == null ){ - warning ( "Could not create app_info for path %s \n Getting out of here ", - desktop ); - continue; - } - bool is_preferred = desktop in preferred_players; - PlayerController ctrl = new PlayerController ( this.root_menu, - app_info, - null, - this.fetch_icon_name(desktop), - calculate_menu_position(), - null, - PlayerController.state.OFFLINE, - is_preferred ); - var mpris_key = desktop; - this.registered_clients.set(mpris_key, ctrl); - this.establish_file_monitoring (app_info, mpris_key); - } - } - - private void establish_file_monitoring (AppInfo info, string mpris_key){ - DesktopAppInfo desktop_info = info as DesktopAppInfo; - var file_path = desktop_info.get_filename (); - File f = File.new_for_path (file_path); - try { - FileMonitor monitor = f.monitor (FileMonitorFlags.SEND_MOVED, null); - unowned FileMonitor weak_monitor = monitor; - monitor.changed.connect ((desktop_file, other_file, event_type) => { - this.relevant_desktop_file_changed (desktop_file, other_file, event_type, weak_monitor); - }); - monitor.ref(); // will be unref()ed by relevant_desktop_file_changed() - GLib.debug ("monitoring file '%s'", file_path); - this.file_monitors.set (file_path, mpris_key); - } - catch (Error e){ - warning ("Unable to create a file monitor for %s", info.get_name()); - return; - } - } - - private void relevant_desktop_file_changed (File desktop_file, - File? other_file, - FileMonitorEvent event_type, - FileMonitor monitor) - { - if (event_type != FileMonitorEvent.DELETED) - return; - - string? path = desktop_file.get_path (); - if (path == null){ - warning ("relevant_desktop_file_changed is returning a file with no path !"); - return; - } - if (!this.file_monitors.has_key (path)){ - warning ("relevant_desktop_file_changed is returning a file which we know nothing about - %s", - path); - return; - } - - var mpris_key = this.file_monitors[path]; - GLib.debug ("file \"%s\" was removed; stopping monitoring \"%s\"", path, mpris_key); - this.registered_clients[mpris_key].remove_from_menu(); - this.settings_manager.remove_interested (mpris_key); - this.registered_clients.unset (mpris_key); - monitor.cancel (); - monitor.unref(); - } - - private int calculate_menu_position() - { - if(this.registered_clients.size == 0){ - return DEVICE_ITEMS_COUNT; - } - else{ - return (DEVICE_ITEMS_COUNT + (this.registered_clients.size * PlayerController.WIDGET_QUANTITY)); - } - } - - public void client_has_become_available ( string desktop, - string dbus_name, - bool use_playlists ) - { - if (desktop == null || desktop == ""){ - warning("Client %s attempting to register without desktop entry being set on the mpris root", - dbus_name); - return; - } - if (desktop in this.settings_manager.fetch_blacklist()) { - debug ("Client %s attempting to register but I'm afraid it is blacklisted", - desktop); - return; - } - - debug ( "client_has_become_available %s", desktop ); - AppInfo? app_info = create_app_info ( desktop.concat( ".desktop" ) ); - if ( app_info == null ){ - warning ( "Could not create app_info for path %s \n Getting out of here ", - desktop ); - return; - } - - var mpris_key = desktop; - bool is_preferred = desktop in this.settings_manager.fetch_preferred (); - - mpris_to_desktop.set (dbus_name, desktop); - if ( this.registered_clients.has_key (mpris_key) == false ){ - debug("New client has registered that we have not seen before: %s", dbus_name ); - PlayerController ctrl = new PlayerController ( this.root_menu, - app_info, - dbus_name, - this.fetch_icon_name(desktop), - this.calculate_menu_position(), - use_playlists, - PlayerController.state.READY, - is_preferred); - this.registered_clients.set ( mpris_key, ctrl ); - debug ( "Have not seen this %s before, new controller created.", desktop ); - this.settings_manager.add_interested ( desktop ); - this.establish_file_monitoring (app_info, mpris_key); - debug ( "application added to the interested list" ); - } - else{ - this.registered_clients[mpris_key].use_playlists = use_playlists; - this.registered_clients[mpris_key].update_state ( PlayerController.state.READY ); - this.registered_clients[mpris_key].activate ( dbus_name ); - debug("Application has already registered - awaken the hibernation: %s with playlists %s \n", dbus_name, use_playlists.to_string() ); - } - } - - public void client_has_vanished ( string mpris_root_interface ) - { - debug("\n MusicPlayerBridge -> client with dbus interface %s has vanished", - mpris_root_interface ); - if (root_menu != null){ - debug("\n attempt to remove %s", mpris_root_interface); - if (mpris_to_desktop.has_key(mpris_root_interface)){ - var mpris_key = mpris_to_desktop[mpris_root_interface]; - mpris_to_desktop.unset(mpris_root_interface); - if ( mpris_key != null && this.registered_clients.has_key(mpris_key)){ - registered_clients[mpris_key].hibernate(); - debug("\n Successively offlined client %s", mpris_key); - } - } - } - } - - public void set_root_menu_item ( Dbusmenu.Menuitem menu ) - { - this.root_menu = menu; - this.try_to_add_inactive_familiar_clients(); - this.watcher = new Mpris2Watcher (); - this.watcher.client_appeared.connect (this.client_has_become_available); - this.watcher.client_disappeared.connect (this.client_has_vanished); - } - - public void enable_player_specific_items_for_client (string object_path, - string desktop_id) - { - var mpris_key = desktop_id; - if (this.registered_clients.has_key (mpris_key) == false){ - warning ("we don't have a client with desktop id %s registered", desktop_id); - return; - } - this.registered_clients[mpris_key].enable_player_specific_items(object_path); - } - - public void enable_track_specific_items_for_client (string object_path, - string desktop_id) - { - var mpris_key = desktop_id; - if (this.registered_clients.has_key (mpris_key) == false){ - warning ("we don't have a client with desktop id %s registered", desktop_id); - return; - } - this.registered_clients[mpris_key].enable_track_specific_items(object_path); - } - - private static AppInfo? create_app_info ( string desktop ) - { - DesktopAppInfo info = new DesktopAppInfo ( desktop ); - if ( desktop == null || info == null ){ - warning ( "Could not create a desktopappinfo instance from app: %s", desktop ); - return null; - } - GLib.AppInfo app_info = info as GLib.AppInfo; - return app_info; - } - - private static string? fetch_icon_name(string desktop) - { - // We know the appinfo is good because it was loaded in the previous reg step. - DesktopAppInfo info = new DesktopAppInfo ( desktop.concat( ".desktop" ) ) ; - KeyFile desktop_keyfile = new KeyFile (); - try{ - desktop_keyfile.load_from_file (info.get_filename(), KeyFileFlags.NONE); - } - catch(GLib.FileError error){ - warning("Error loading keyfile - FileError"); - return null; - } - catch(GLib.KeyFileError error){ - warning("Error loading keyfile - KeyFileError"); - return null; - } - - try{ - return desktop_keyfile.get_string (KeyFileDesktop.GROUP, - KeyFileDesktop.KEY_ICON); - } - catch(GLib.KeyFileError error){ - warning("Error trying to fetch the icon name from the keyfile"); - return null; - } - } -} - - - - diff --git a/src/mute-menu-item.c b/src/mute-menu-item.c deleted file mode 100644 index 0e6a46f..0000000 --- a/src/mute-menu-item.c +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2011 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 . -*/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "common-defs.h" -#include "mute-menu-item.h" -#include "pulseaudio-mgr.h" - -typedef struct _MuteMenuItemPrivate MuteMenuItemPrivate; - -struct _MuteMenuItemPrivate { - DbusmenuMenuitem* button; -}; - -#define MUTE_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUTE_MENU_ITEM_TYPE, MuteMenuItemPrivate)) - -/* Prototypes */ -static void mute_menu_item_class_init (MuteMenuItemClass *klass); -static void mute_menu_item_init (MuteMenuItem *self); -static void mute_menu_item_dispose (GObject *object); -static void mute_menu_item_finalize (GObject *object); -static void mute_menu_item_set_global_mute_from_ui (gpointer user_data); - -G_DEFINE_TYPE (MuteMenuItem, mute_menu_item, G_TYPE_OBJECT); - -static void -mute_menu_item_class_init (MuteMenuItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (MuteMenuItemPrivate)); - - object_class->dispose = mute_menu_item_dispose; - object_class->finalize = mute_menu_item_finalize; - - return; -} - -static void -mute_menu_item_init (MuteMenuItem *self) -{ - g_debug("Building new Mute Menu Item"); - MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE(self); - priv->button = NULL; - priv->button = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set(priv->button, - DBUSMENU_MENUITEM_PROP_TYPE, - DBUSMENU_MUTE_MENUITEM_TYPE); - - dbusmenu_menuitem_property_set_bool (priv->button, - DBUSMENU_MENUITEM_PROP_VISIBLE, - TRUE); - - g_signal_connect (G_OBJECT (priv->button), - DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (mute_menu_item_set_global_mute_from_ui), - self); - return; -} - -static void -mute_menu_item_dispose (GObject *object) -{ - G_OBJECT_CLASS (mute_menu_item_parent_class)->dispose (object); - return; -} - -static void -mute_menu_item_finalize (GObject *object) -{ - G_OBJECT_CLASS (mute_menu_item_parent_class)->finalize (object); -} - -static void -mute_menu_item_set_global_mute_from_ui (gpointer user_data) -{ - g_return_if_fail (DBUSMENU_IS_MENUITEM (user_data)); - DbusmenuMenuitem* button = DBUSMENU_MENUITEM (user_data); - gboolean current_value = dbusmenu_menuitem_property_get_bool (button, - DBUSMENU_MUTE_MENUITEM_VALUE); - gboolean new_value = !current_value; - pm_update_mute (new_value); -} - -void -mute_menu_item_update (MuteMenuItem* item, gboolean value_update) -{ - MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); - - dbusmenu_menuitem_property_set_bool (priv->button, - DBUSMENU_MUTE_MENUITEM_VALUE, - value_update); - dbusmenu_menuitem_property_set (priv->button, - DBUSMENU_MENUITEM_PROP_LABEL, - value_update == FALSE ? _("Mute") : _("Unmute")); -} - -void -mute_menu_item_enable (MuteMenuItem* item, gboolean active) -{ - MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); - dbusmenu_menuitem_property_set_bool (priv->button, - DBUSMENU_MENUITEM_PROP_VISIBLE, - TRUE); - - dbusmenu_menuitem_property_set_bool (priv->button, - DBUSMENU_MENUITEM_PROP_ENABLED, - active); -} - -DbusmenuMenuitem* -mute_menu_item_get_button (MuteMenuItem* item) -{ - MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); - return priv->button; -} - -gboolean -mute_menu_item_is_muted (MuteMenuItem* item) -{ - MuteMenuItemPrivate* priv = MUTE_MENU_ITEM_GET_PRIVATE (item); - return dbusmenu_menuitem_property_get_bool (priv->button, - DBUSMENU_MUTE_MENUITEM_VALUE); -} - -MuteMenuItem* -mute_menu_item_new (gboolean initial_update, gboolean enabled) -{ - MuteMenuItem *self = g_object_new (MUTE_MENU_ITEM_TYPE, NULL); - mute_menu_item_update (self, initial_update); - mute_menu_item_enable (self, enabled); - return self; -} diff --git a/src/mute-menu-item.h b/src/mute-menu-item.h deleted file mode 100644 index 81a4b33..0000000 --- a/src/mute-menu-item.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -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 . -*/ - -#ifndef __MUTE_MENU_ITEM_H__ -#define __MUTE_MENU_ITEM_H__ - -#include -#include -#include - -G_BEGIN_DECLS - -#define MUTE_MENU_ITEM_TYPE (mute_menu_item_get_type ()) -#define MUTE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MUTE_MENU_ITEM_TYPE, MuteMenuItem)) -#define MUTE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MUTE_MENU_ITEM_TYPE, MuteMenuItemClass)) -#define IS_MUTE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MUTE_MENU_ITEM_TYPE)) -#define IS_MUTE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MUTE_MENU_ITEM_TYPE)) -#define MUTE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MUTE_MENU_ITEM_TYPE, MuteMenuItemClass)) - -typedef struct _MuteMenuItem MuteMenuItem; -typedef struct _MuteMenuItemClass MuteMenuItemClass; - -struct _MuteMenuItemClass { - GObjectClass parent_class; -}; - -struct _MuteMenuItem { - GObject parent; -}; - -GType mute_menu_item_get_type (void); - -MuteMenuItem* mute_menu_item_new (); - -void mute_menu_item_update (MuteMenuItem* item, gboolean update); -void mute_menu_item_enable (MuteMenuItem* item, gboolean active); -gboolean mute_menu_item_is_muted (MuteMenuItem* item); - -DbusmenuMenuitem* mute_menu_item_get_button (MuteMenuItem* item); - -G_END_DECLS - -#endif \ No newline at end of file diff --git a/src/player-controller.vala b/src/player-controller.vala deleted file mode 100644 index fd66a8a..0000000 --- a/src/player-controller.vala +++ /dev/null @@ -1,247 +0,0 @@ -/* -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 . -*/ - -using Dbusmenu; -using Gee; - -public class PlayerController : GLib.Object -{ - public const int WIDGET_QUANTITY = 4; - - public enum widget_order{ - SEPARATOR, - METADATA, - TRANSPORT, - PLAYLISTS - } - - public enum state{ - OFFLINE, - INSTANTIATING, - READY, - CONNECTED, - DISCONNECTED - } - - public int current_state = state.OFFLINE; - - public Dbusmenu.Menuitem root_menu; - public string dbus_name { get; set;} - public ArrayList custom_items; - public Mpris2Controller mpris_bridge; - public AppInfo? app_info { get; set;} - public int menu_offset { get; set;} - public string icon_name { get; set; } - public bool? use_playlists; - public bool is_preferred { get; private set; } - private SpecificItemsManager track_specific_mgr; - private SpecificItemsManager player_specific_mgr; - - public PlayerController(Dbusmenu.Menuitem root, - GLib.AppInfo app, - string? dbus_name, - string icon_name, - int offset, - bool? use_playlists, - state initial_state, - bool is_preferred) - { - this.use_playlists = use_playlists; - this.root_menu = root; - this.app_info = app; - this.dbus_name = dbus_name; - this.icon_name = icon_name; - this.custom_items = new ArrayList(); - this.current_state = initial_state; - this.menu_offset = offset; - this.is_preferred = is_preferred; - - this.construct_widgets(); - this.establish_mpris_connection(); - this.update_layout(); - debug ("New player controller for %s with icon name %s", this.app_info.get_name(), this.icon_name); - } - - public void update_state(state new_state) - { - debug("update_state - player controller %s : new state %i", this.app_info.get_name(), - new_state); - this.current_state = new_state; - this.update_layout(); - } - - public void activate( string dbus_name ) - { - this.dbus_name = dbus_name; - this.establish_mpris_connection(); - } - - /* - instantiate() - The user should be able to start the app from the transport bar when in an offline state - There is a need to wait before the application is on DBus before attempting to access its mpris address - Hence only when the it has registered with us via libindicate do we attempt to kick off mpris communication - */ - public void instantiate() - { - debug("instantiate in player controller for %s", this.app_info.get_name() ); - try{ - this.app_info.launch(null, null); - this.update_state(state.INSTANTIATING); - } - catch(GLib.Error error){ - warning("Failed to launch app %s with error message: %s", this.app_info.get_name(), - error.message ); - } - } - - public void enable_track_specific_items (string object_path) - { - if (this.track_specific_mgr == null){ - track_specific_mgr = new SpecificItemsManager (this, - object_path, - SpecificItemsManager.category.TRACK); - } - } - - public void enable_player_specific_items (string object_path) - { - if (this.player_specific_mgr == null){ - player_specific_mgr = new SpecificItemsManager (this, - object_path, - SpecificItemsManager.category.PLAYER); - } - } - - public int track_specific_count () - { - if (this.track_specific_mgr == null) { - return 0; - } - return this.track_specific_mgr.proxy_items.size; - } - - private void establish_mpris_connection() - { - if(this.current_state != state.READY || this.dbus_name == null ){ - debug("establish_mpris_connection - Not ready to connect"); - return; - } - debug ( " establish mpris connection - use playlists value = %s ", - this.use_playlists.to_string() ); - this.mpris_bridge = new Mpris2Controller (this); - this.determine_state (); - } - - public void remove_from_menu() - { - foreach(PlayerItem item in this.custom_items){ - this.root_menu.child_delete(item); - } - if (this.use_playlists == true){ - PlaylistsMenuitem playlists_menuitem = this.custom_items[widget_order.PLAYLISTS] as PlaylistsMenuitem; - this.root_menu.child_delete (playlists_menuitem.root_item); - } - } - - public void set_as_preferred (bool val) { - this.is_preferred = val; - this.update_layout(); - } - - public void hibernate() - { - update_state(PlayerController.state.OFFLINE); - TransportMenuitem transport = this.custom_items[widget_order.TRANSPORT] as TransportMenuitem; - transport.change_play_state (Transport.State.PAUSED); - this.custom_items[widget_order.METADATA].reset(MetadataMenuitem.relevant_attributes_for_ui()); - MetadataMenuitem md = this.custom_items[widget_order.METADATA] as MetadataMenuitem; - md.toggle_active_triangle (false); - this.mpris_bridge = null; - } - - public void update_layout() - { - PlaylistsMenuitem playlists_menuitem = this.custom_items[widget_order.PLAYLISTS] as PlaylistsMenuitem; - MetadataMenuitem metadata_menuitem = this.custom_items[widget_order.METADATA] as MetadataMenuitem; - if(this.current_state != state.CONNECTED){ - metadata_menuitem.should_collapse (true); - playlists_menuitem.root_item.property_set_bool (MENUITEM_PROP_VISIBLE, - false); - this.custom_items[widget_order.TRANSPORT].property_set_bool (MENUITEM_PROP_VISIBLE, is_preferred); - return; - } - - bool should_collapse = !this.custom_items[widget_order.METADATA].populated (MetadataMenuitem.relevant_attributes_for_ui()); - metadata_menuitem.should_collapse (should_collapse); - - if (is_preferred){ - TransportMenuitem transport = this.custom_items[widget_order.TRANSPORT] as TransportMenuitem; - transport.handle_cached_action(); - } - else{ - this.custom_items[widget_order.TRANSPORT].property_set_bool (MENUITEM_PROP_VISIBLE, - true); - } - playlists_menuitem.root_item.property_set_bool ( MENUITEM_PROP_VISIBLE, - this.use_playlists ); - } - - private void construct_widgets() - { - // Separator item - this.custom_items.add(new PlayerItem(CLIENT_TYPES_SEPARATOR)); - - // Metadata item - MetadataMenuitem metadata_item = new MetadataMenuitem(this); - this.custom_items.add(metadata_item); - - // Transport item - TransportMenuitem transport_item = new TransportMenuitem(this); - this.custom_items.add(transport_item); - - // Playlist item - PlaylistsMenuitem playlist_menuitem = new PlaylistsMenuitem(this); - this.custom_items.add(playlist_menuitem); - - foreach(PlayerItem item in this.custom_items){ - if (this.custom_items.index_of(item) == WIDGET_QUANTITY-1) { - PlaylistsMenuitem playlists_menuitem = item as PlaylistsMenuitem; - root_menu.child_add_position(playlists_menuitem.root_item, this.menu_offset + this.custom_items.index_of(item)); - } - else{ - root_menu.child_add_position (item, - this.menu_offset + this.custom_items.index_of(item)); - } - } - } - - private void determine_state() - { - if(this.mpris_bridge.connected() == true){ - this.update_state(state.CONNECTED); - MetadataMenuitem md = this.custom_items[widget_order.METADATA] as MetadataMenuitem; - md.toggle_active_triangle(true); - this.mpris_bridge.initial_update(); - } - else{ - this.update_state(state.DISCONNECTED); - } - } -} diff --git a/src/player-item.vala b/src/player-item.vala deleted file mode 100644 index 7867653..0000000 --- a/src/player-item.vala +++ /dev/null @@ -1,104 +0,0 @@ -/* -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 . -*/ - -using Dbusmenu; -using Gee; - -public class PlayerItem : Dbusmenu.Menuitem -{ - public PlayerController owner {get; construct;} - public string item_type { get; construct; } - public const int EMPTY = -1; - - public PlayerItem(string type) - { - Object(item_type: type); - } - - construct { - this.property_set(MENUITEM_PROP_TYPE, item_type); - } - - public void reset(HashSet attrs){ - foreach(string s in attrs){ - this.property_set_int(s, EMPTY); - } - } - - /** - * update() - * Base update method for playeritems, takes the attributes and the incoming updates - * and attmepts to update the appropriate props on the object. - * Album art is handled separately to deal with remote and local file paths. - */ - public void update(HashTable data, HashSet attributes) - { - //debug("PlayerItem::update()"); - if(data == null){ - warning("PlayerItem::Update -> The hashtable was null - just leave it!"); - return; - } - - foreach(string property in attributes){ - string[] input_keys = property.split("-"); - string search_key = input_keys[input_keys.length-1 : input_keys.length][0]; - //debug("search key = %s", search_key); - Variant? v = data.lookup(search_key); - - if (v == null) continue; - - if (v.is_of_type ( VariantType.STRING )){ - string update = v.get_string().strip(); - //debug("with value : %s", update); - if(property.contains("mpris:artUrl")){ - // We know its a metadata instance because thats the only - // object with the arturl prop - MetadataMenuitem metadata = this as MetadataMenuitem; - metadata.fetch_art ( update, property ); - continue; - } - this.property_set(property, update); - } - else if (v.is_of_type (VariantType.INT32 )){ - //debug("with value : %i", v.get_int32()); - this.property_set_int(property, v.get_int32()); - } - else if (v.is_of_type (VariantType.INT64 )){ - //debug("with value : %i", (int)v.get_int64()); - this.property_set_int(property, (int)v.get_int64()); - } - else if(v.is_of_type ( VariantType.BOOLEAN )){ - //debug("with value : %s", v.get_boolean().to_string()); - this.property_set_bool(property, v.get_boolean()); - } - } - } - - public bool populated(HashSet attrs) - { - foreach(string prop in attrs){ - if(property_get_int(prop) != EMPTY){ - return true; - } - } - return false; - } - -} - diff --git a/src/playlists-menu-item.vala b/src/playlists-menu-item.vala deleted file mode 100644 index 4666a50..0000000 --- a/src/playlists-menu-item.vala +++ /dev/null @@ -1,154 +0,0 @@ -/* -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 . -*/ - -using Config; -using Dbusmenu; -using DbusmenuPlaylists; -using DbusmenuPlaylist; -using Gee; - - -public class PlaylistsMenuitem : PlayerItem -{ - private HashMap current_playlists; - public Menuitem root_item; - - public PlaylistsMenuitem ( PlayerController parent ) - { - Object ( item_type: MENUITEM_TYPE, owner: parent ); - } - - construct{ - this.current_playlists = new HashMap(); - this.root_item = new Menuitem(); - this.root_item.property_set ( MENUITEM_PROP_LABEL, _("Choose Playlist") ); - this.root_item.property_set ( MENUITEM_PATH, "" ); - } - - public new void update (PlaylistDetails[] playlists) - { - foreach ( PlaylistDetails detail in playlists ){ - // We don't want to list playlists which are for videos)' - if (this.already_observed(detail) || this.is_video_related(detail)) - continue; - - Dbusmenu.Menuitem menuitem = new Menuitem(); - menuitem.property_set (MENUITEM_PROP_LABEL, - truncate_item_label_if_needs_be (detail.name)); - menuitem.property_set (MENUITEM_PROP_ICON_NAME, "playlist-symbolic"); - - menuitem.property_set (MENUITEM_PATH, (string)detail.path); - menuitem.property_set_bool (MENUITEM_PROP_VISIBLE, true); - menuitem.property_set_bool (MENUITEM_PROP_ENABLED, true); - - menuitem.item_activated.connect(() => { - submenu_item_activated (menuitem.id ); - } - ); - this.current_playlists.set( menuitem.id, menuitem ); - this.root_item.child_append( menuitem ); - debug ("populating valid playlists %s", detail.name); - } - // Finally remove any that might have been deleted - foreach (Dbusmenu.Menuitem item in this.current_playlists.values) { - bool within = false; - foreach (PlaylistDetails detail in playlists){ - if (detail.path == item.property_get (MENUITEM_PATH)) { - within = true; - break; - } - } - if (within == false){ - if (this.root_item.property_get (MENUITEM_PATH) == item.property_get (MENUITEM_PATH)){ - this.root_item.property_set (MENUITEM_PROP_LABEL, _("Choose Playlist")); - } - this.root_item.child_delete (item); - } - } - } - - public void update_individual_playlist (PlaylistDetails new_detail) - { - foreach ( Dbusmenu.Menuitem item in this.current_playlists.values ){ - if (new_detail.path == item.property_get (MENUITEM_PATH)){ - item.property_set (MENUITEM_PROP_LABEL, - truncate_item_label_if_needs_be (new_detail.name)); - } - } - // If its active make sure the name is updated on the root item. - if (this.root_item.property_get (MENUITEM_PATH) == new_detail.path) { - this.root_item.property_set (MENUITEM_PROP_LABEL, - truncate_item_label_if_needs_be (new_detail.name)); - } - } - - private bool already_observed (PlaylistDetails new_detail) - { - foreach ( Dbusmenu.Menuitem item in this.current_playlists.values ){ - var path = item.property_get (MENUITEM_PATH); - if (new_detail.path == path) return true; - } - return false; - } - - private bool is_video_related (PlaylistDetails new_detail) - { - var location = (string)new_detail.path; - if (location.contains ("/VideoLibrarySource/")) return true; - return false; - } - - public void active_playlist_update (PlaylistDetails detail) - { - var update = detail.name; - if ( update == "" ) update = _("Choose Playlist"); - this.root_item.property_set (MENUITEM_PROP_LABEL, - truncate_item_label_if_needs_be(update)); - this.root_item.property_set (MENUITEM_PATH, detail.path); - } - - private void submenu_item_activated (int menu_item_id) - { - if (!this.current_playlists.has_key(menu_item_id)) { - warning( "item %i was activated but we don't have a corresponding playlist", - menu_item_id ); - return; - } - this.owner.mpris_bridge.activate_playlist ( (GLib.ObjectPath)this.current_playlists[menu_item_id].property_get (MENUITEM_PATH) ); - } - - private string truncate_item_label_if_needs_be(string item_label) - { - var result = item_label; - if (item_label.char_count(-1) > 17){ - result = item_label.slice ((long)0, (long)15); - result += "…"; - } - return result; - } - - public static HashSet attributes_format() - { - HashSet attrs = new HashSet(); - attrs.add(MENUITEM_TITLE); - attrs.add(MENUITEM_PLAYLISTS); - return attrs; - } - -} diff --git a/src/pulseaudio-mgr.c b/src/pulseaudio-mgr.c deleted file mode 100644 index f205723..0000000 --- a/src/pulseaudio-mgr.c +++ /dev/null @@ -1,717 +0,0 @@ -/* -Copyright 2011 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 . -*/ - -/**Notes - * - * Approach now is to set up the communication channels, query the server - * fetch its default sink/source. If this fails then fetch the list of sinks/sources - * and take the first one which is not the auto-null sink. - * TODO: need to handle the situation where one chink in this linear chain breaks - * i.e. start off the process again and count the attempts (note different to - reconnect attempts) - */ -#include -#include -#include - -#include "pulseaudio-mgr.h" -#include "config.h" - -#define RECONNECT_DELAY 5 - - -static void pm_context_state_callback(pa_context *c, void *userdata); -static void pm_subscribed_events_callback (pa_context *c, - enum pa_subscription_event_type t, - uint32_t index, - void* userdata); -static void pm_server_info_callback (pa_context *c, - const pa_server_info *info, - void *userdata); -static void pm_default_sink_info_callback (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata); -static void pm_default_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata); -static void pm_sink_info_callback (pa_context *c, - const pa_sink_info *sink, - int eol, - void *userdata); -static void pm_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata); -static void pm_update_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata); -static void pm_sink_input_info_callback (pa_context *c, - const pa_sink_input_info *info, - int eol, - void *userdata); -static void pm_update_device (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata); -static void pm_toggle_mute_for_every_sink_callback (pa_context *c, - const pa_sink_info *sink, - int eol, - void* userdata); -static void pm_source_output_info_callback (pa_context *c, - const pa_source_output_info *info, - int eol, - void *userdata); - -static gboolean reconnect_to_pulse (gpointer user_data); - -static gint connection_attempts = 0; -static gint reconnect_idle_id = 0; -static pa_context *pulse_context = NULL; -static pa_glib_mainloop *pa_main_loop = NULL; - -/** - Entry Point - **/ -void -pm_establish_pulse_connection (Device* device) -{ - pa_main_loop = pa_glib_mainloop_new (g_main_context_default ()); - g_assert (pa_main_loop); - reconnect_to_pulse ((gpointer)device); -} - -/** -close_pulse_activites() -Gracefully close our connection with the Pulse async library. -**/ -void close_pulse_activites() -{ - if (pulse_context != NULL) { - pa_context_unref(pulse_context); - pulse_context = NULL; - } - pa_glib_mainloop_free(pa_main_loop); - pa_main_loop = NULL; -} - -/** -reconnect_to_pulse (gpointer user_data) -Method which connects to the pulse server and is used to track reconnects. - */ -static gboolean -reconnect_to_pulse (gpointer user_data) -{ - g_debug("Attempt a pulse connection"); - g_return_val_if_fail (IS_DEVICE (user_data), FALSE); - - connection_attempts += 1; - if (pulse_context != NULL) { - pa_context_unref(pulse_context); - pulse_context = NULL; - } - - pa_proplist *proplist; - - proplist = pa_proplist_new (); - pa_proplist_sets (proplist, - PA_PROP_APPLICATION_NAME, - "Indicator Sound"); - pa_proplist_sets (proplist, - PA_PROP_APPLICATION_ID, - "com.canonical.indicator.sound"); - pa_proplist_sets (proplist, - PA_PROP_APPLICATION_ICON_NAME, - "multimedia-volume-control"); - pa_proplist_sets (proplist, - PA_PROP_APPLICATION_VERSION, - PACKAGE_VERSION); - - pulse_context = pa_context_new_with_proplist (pa_glib_mainloop_get_api( pa_main_loop ), - NULL, - proplist); - pa_proplist_free (proplist); - g_assert(pulse_context); - pa_context_set_state_callback (pulse_context, - pm_context_state_callback, - user_data); - int result = pa_context_connect (pulse_context, - NULL, - (pa_context_flags_t)PA_CONTEXT_NOFAIL, - NULL); - - if (result < 0) { - g_warning ("Failed to connect context: %s", - pa_strerror (pa_context_errno (pulse_context))); - } - if (connection_attempts > 5){ - return FALSE; - } - else{ - return TRUE; - } -} - -void -pm_update_volume (gint sink_index, pa_cvolume new_volume) -{ - if (sink_index < 0 || pulse_context == NULL){ - g_warning ("pm_update_volume sink index is negative or the context is null"); - return; - } - - if (pa_context_get_state (pulse_context) != PA_CONTEXT_READY ){ - g_warning ("pm_update_volume context is not in a ready state"); - return; - } - - pa_operation *operation = NULL; - - operation = pa_context_set_sink_volume_by_index (pulse_context, - sink_index, - &new_volume, - NULL, - NULL); - if (!operation){ - g_warning ("pm_update_volume operation failed for some reason"); - return; - } - pa_operation_unref (operation); -} - -void -pm_update_mute (gboolean update) -{ - if (pulse_context == NULL){ - g_warning ("pm_update_mute - the context is null"); - return; - } - - if (pa_context_get_state (pulse_context) != PA_CONTEXT_READY ){ - g_warning ("pm_update_mute context is not in a ready state"); - return; - } - - pa_operation *operation = NULL; - - operation = pa_context_get_sink_info_list (pulse_context, - pm_toggle_mute_for_every_sink_callback, - GINT_TO_POINTER (update)); - if (!operation){ - g_warning ("pm_update_mute operation failed for some reason"); - return; - } - pa_operation_unref (operation); -} - -void -pm_update_mic_gain (gint source_index, pa_cvolume new_gain) -{ - if (source_index < 0 || pulse_context == NULL){ - g_warning ("pm_update_mic_gain source index is negative or the context is null"); - return; - } - - if (pa_context_get_state (pulse_context) != PA_CONTEXT_READY ){ - g_warning ("pm_update_mic_gain context is not in a ready state"); - return; - } - - pa_operation *operation = NULL; - - operation = pa_context_set_source_volume_by_index (pulse_context, - source_index, - &new_gain, - NULL, - NULL); - if (!operation){ - g_warning ("pm_update_mic_gain operation failed for some reason"); - return; - } - pa_operation_unref (operation); -} - -void -pm_update_mic_mute (gint source_index, gint mute_update) -{ - if (source_index < 0){ - return; - } - - if (pa_context_get_state (pulse_context) != PA_CONTEXT_READY ){ - g_warning ("pm_update_mic_mute context is not in a ready state"); - return; - } - - pa_operation *operation = NULL; - - operation = pa_context_set_source_mute_by_index (pulse_context, - source_index, - mute_update, - NULL, - NULL); - if (!operation){ - g_warning ("pm_update_mic_mute operation failed for some reason"); - return; - } - pa_operation_unref (operation); -} - -/**********************************************************************************************************************/ -// Pulse-Audio asychronous call-backs -/**********************************************************************************************************************/ - - -static void -pm_subscribed_events_callback (pa_context *c, - enum pa_subscription_event_type t, - uint32_t index, - void* userdata) -{ - if (IS_DEVICE (userdata) == FALSE){ - g_critical ("subscribed events callback - our userdata is not what we think it should be"); - return; - } - Device* sink = DEVICE (userdata); - - switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - case PA_SUBSCRIPTION_EVENT_SINK: - - // We don't care about any other sink other than the active one. - if (index != device_get_sink_index (sink)) - return; - - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - device_sink_deactivated (sink); - - } - else{ - pa_operation_unref (pa_context_get_sink_info_by_index (c, - index, - pm_update_device, - userdata) ); - } - break; - case PA_SUBSCRIPTION_EVENT_SOURCE: - g_debug ("Looks like source event of some description - index = %i", index); - // We don't care about any other sink other than the active one. - if (index != device_get_source_index (sink)) - return; - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - g_debug ("Source removal event - index = %i", index); - device_deactivate_voip_source (sink, FALSE); - } - else{ - pa_operation_unref (pa_context_get_source_info_by_index (c, - index, - pm_update_source_info_callback, - userdata) ); - } - break; - case PA_SUBSCRIPTION_EVENT_SINK_INPUT: - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - g_debug ("some new sink input event ? - index = %i", index); - // Maybe blocking state ?. - pa_operation_unref (pa_context_get_sink_input_info (c, - index, - pm_sink_input_info_callback, userdata)); - } - break; - case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: - g_debug ("source output event"); - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - gint cached_source_output_index = device_get_voip_source_output_index (sink); - if (index == cached_source_output_index){ - g_debug ("Just saw a source output removal event - index = %i and cached index = %i", index, cached_source_output_index); - device_deactivate_voip_client (sink); - } - } - else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - g_debug ("some new source output event ? - index = %i", index); - // Determine if its a VOIP app. - pa_operation_unref (pa_context_get_source_output_info (c, - index, - pm_source_output_info_callback, userdata)); - } - break; - case PA_SUBSCRIPTION_EVENT_SERVER: - g_debug("PA_SUBSCRIPTION_EVENT_SERVER event triggered."); - pa_operation *o; - if (!(o = pa_context_get_server_info (c, pm_server_info_callback, userdata))) { - g_warning("subscribed_events_callback - pa_context_get_server_info() failed"); - return; - } - pa_operation_unref(o); - break; - } -} - -static void -pm_context_state_callback (pa_context *c, void *userdata) -{ - switch (pa_context_get_state(c)) { - case PA_CONTEXT_UNCONNECTED: - g_debug("unconnected"); - break; - case PA_CONTEXT_CONNECTING: - g_debug("connecting - waiting for the server to become available"); - break; - case PA_CONTEXT_AUTHORIZING: - g_debug ("Authorizing"); - break; - case PA_CONTEXT_SETTING_NAME: - g_debug ("Setting name"); - break; - case PA_CONTEXT_FAILED: - g_warning("PA_CONTEXT_FAILED - Is PulseAudio Daemon running ?"); - device_sink_deactivated (DEVICE (userdata)); - if (reconnect_idle_id == 0){ - reconnect_idle_id = g_timeout_add_seconds (RECONNECT_DELAY, - reconnect_to_pulse, - (gpointer)userdata); - } - break; - case PA_CONTEXT_TERMINATED: - g_debug ("Terminated"); - device_sink_deactivated (DEVICE (userdata)); - - if (reconnect_idle_id != 0){ - g_source_remove (reconnect_idle_id); - reconnect_idle_id = 0; - } - break; - case PA_CONTEXT_READY: - connection_attempts = 0; - g_debug("PA_CONTEXT_READY"); - - if (reconnect_idle_id != 0){ - g_source_remove (reconnect_idle_id); - reconnect_idle_id = 0; - } - - pa_context_set_subscribe_callback(c, pm_subscribed_events_callback, userdata); - pa_operation *o = NULL; - - o = pa_context_subscribe (c, (pa_subscription_mask_t) - (PA_SUBSCRIPTION_MASK_SINK| - PA_SUBSCRIPTION_MASK_SOURCE| - PA_SUBSCRIPTION_MASK_SINK_INPUT| - PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| - PA_SUBSCRIPTION_MASK_SERVER), - NULL, - NULL); - - - if (!o){ - g_critical("pa_context_subscribe() failed - ?"); - return; - } - - pa_operation_unref(o); - - o = pa_context_get_server_info (c, pm_server_info_callback, userdata); - - if (!o){ - g_warning("pa_context_get_server_info() failed - ?"); - return; - } - - pa_operation_unref(o); - - break; - } -} - -/** - After startup we go straight for the server info to see if it has details of - the default sink and source. Normally these are valid, if there is none set - fetch the list of each and try to determine the sink. - **/ -static void -pm_server_info_callback (pa_context *c, - const pa_server_info *info, - void *userdata) -{ - pa_operation *operation; - g_debug ("server info callback"); - - if (info == NULL) { - g_warning("No PA server - get the hell out of here"); - device_sink_deactivated (DEVICE (userdata)); - return; - } - // Go for the default sink - if (info->default_sink_name != NULL) { - g_debug ("default sink name from the server ain't null'"); - if (!(operation = pa_context_get_sink_info_by_name (c, - info->default_sink_name, - pm_default_sink_info_callback, - userdata) )) { - g_warning("pa_context_get_sink_info_by_namet() failed"); - device_sink_deactivated (DEVICE (userdata)); - pa_operation_unref(operation); - return; - } - } // If there is no default sink, try to determine a sink from the list of sinks - else if (!(operation = pa_context_get_sink_info_list(c, - pm_sink_info_callback, - userdata))) { - g_warning("pa_context_get_sink_info_list() failed"); - device_sink_deactivated (DEVICE (userdata)); - pa_operation_unref(operation); - return; - } - // And the source - if (info->default_source_name != NULL) { - g_debug ("default source name from the server is not null'"); - if (!(operation = pa_context_get_source_info_by_name (c, - info->default_source_name, - pm_default_source_info_callback, - userdata) )) { - g_warning("pa_context_get_default_source_info() failed"); - // TODO: call some input deactivate method on active sink - pa_operation_unref(operation); - return; - } - } - else if (!(operation = pa_context_get_source_info_list(c, - pm_source_info_callback, - userdata))) { - g_warning("pa_context_get_sink_info_list() failed"); - // TODO: call some input deactivate method for the source - } - pa_operation_unref(operation); -} - -// If the server doesn't have a default sink to give us -// we should attempt to pick up the first of the list of sinks which doesn't have -// the name 'auto_null' (that was all really I was doing before) -static void -pm_sink_info_callback (pa_context *c, - const pa_sink_info *sink, - int eol, - void* userdata) -{ - if (eol > 0) { - return; - } - else { - if (IS_DEVICE (userdata) == FALSE || sink == NULL){ - g_warning ("sink info callback - our user data is not what we think it should be or the sink parameter is null"); - return; - } - Device* a_sink = DEVICE (userdata); - if (device_is_sink_populated (a_sink) == FALSE && - g_ascii_strncasecmp("auto_null", sink->name, 9) != 0){ - device_sink_populate (a_sink, sink); - } - } -} - -static void -pm_default_sink_info_callback (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (IS_DEVICE (userdata) == FALSE || info == NULL){ - g_warning ("Default sink info callback - our user data is not what we think it should be or the info parameter is null"); - return; - } - // Only repopulate if there is a change with regards the index - if (device_get_sink_index (DEVICE (userdata)) == info->index) - return; - - g_debug ("Pulse Server has handed us a new default sink"); - device_sink_populate (DEVICE (userdata), info); - } -} - -static void -pm_sink_input_info_callback (pa_context *c, - const pa_sink_input_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (info == NULL || IS_DEVICE (userdata) == FALSE) { - g_warning("Sink input info callback : SINK INPUT INFO IS NULL or our user_data is not what we think it should be"); - return; - } - Device* a_sink = DEVICE (userdata); - // And finally check for the mute blocking state - if (device_get_sink_index (a_sink) == info->sink){ - device_determine_blocking_state (a_sink); - } - } -} - -static void -pm_source_output_info_callback (pa_context *c, - const pa_source_output_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (info == NULL || IS_DEVICE (userdata) == FALSE) { - g_warning("Source output callback: SOURCE OUTPUT INFO IS NULL or our user_data is not what we think it should be"); - return; - } - - // Check if this is Voip sink input - gint result = pa_proplist_contains (info->proplist, PA_PROP_MEDIA_ROLE); - Device* a_sink = DEVICE (userdata); - - if (result == 1){ - //g_debug ("Source output info has media role property"); - const char* value = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE); - //g_debug ("prop role = %s", value); - if (g_strcmp0 (value, "phone") == 0 || g_strcmp0 (value, "production") == 0) { - g_debug ("We have a VOIP/PRODUCTION ! - index = %i", info->index); - device_activate_voip_item (a_sink, (gint)info->index, (gint)info->client); - // TODO to start with we will assume our source is the same as what this 'client' - // is pointing at. This should probably be more intelligent : - // query for the list of source output info's and going on the name of the client - // from the sink input ensure our voip item is using the right source. - } - } - } -} - -static void -pm_update_device (pa_context *c, - const pa_sink_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else{ - if (IS_DEVICE (userdata) == FALSE || info == NULL){ - g_warning ("update_device - our user data is not what we think it should be or the info parameter is null"); - return; - } - device_sink_update (DEVICE(userdata), info); - } -} - -static void -pm_toggle_mute_for_every_sink_callback (pa_context *c, - const pa_sink_info *sink, - int eol, - void* userdata) -{ - if (eol > 0) { - return; - } - - if (sink == NULL) { - g_warning ("toggle_mute cb - sink parameter is null - why ?"); - return; - } - - pa_operation *operation = NULL; - operation = pa_context_set_sink_mute_by_index (c, - sink->index, - GPOINTER_TO_INT(userdata), - NULL, - NULL); - if (!operation){ - g_warning ("pm_update_mic_mute operation failed for some reason"); - return; - } - pa_operation_unref (operation); -} - -// Source info related callbacks -static void -pm_default_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (IS_DEVICE (userdata) == FALSE || info == NULL){ - g_warning ("Default source info callback - our user data is not what we think it should be or the source info parameter is null"); - return; - } - // If there is an index change we need to change our cached source - if (device_get_source_index (DEVICE (userdata)) == info->index) - return; - g_debug ("Pulse Server has handed us a new default source"); - device_deactivate_voip_source (DEVICE (userdata), TRUE); - device_update_voip_input_source (DEVICE (userdata), info); - } -} - -static void -pm_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (IS_DEVICE (userdata) == FALSE || info == NULL){ - g_warning ("source info callback - our user data is not what we think it should be or the source info parameter is null"); - return; - } - // For now we will take the first available - if (device_is_voip_source_populated (DEVICE (userdata)) == FALSE){ - device_update_voip_input_source (DEVICE (userdata), info); - } - } -} - -static void -pm_update_source_info_callback (pa_context *c, - const pa_source_info *info, - int eol, - void *userdata) -{ - if (eol > 0) { - return; - } - else { - if (IS_DEVICE (userdata) == FALSE || info == NULL ){ - g_warning ("source info update callback - our user data is not what we think it should be or the source info paramter is null"); - return; - } - g_debug ("Got a source update for %s , index %i", info->name, info->index); - device_update_voip_input_source (DEVICE (userdata), info); - } -} diff --git a/src/pulseaudio-mgr.h b/src/pulseaudio-mgr.h deleted file mode 100644 index ace47f3..0000000 --- a/src/pulseaudio-mgr.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2011 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 "device.h" - -void pm_establish_pulse_connection (Device* device); -void close_pulse_activites(); -void pm_update_volume (gint sink_index, pa_cvolume new_volume); -void pm_update_mic_gain (gint source_index, pa_cvolume new_gain); -void pm_update_mic_mute (gint source_index, int mute_update); -void pm_update_mute (gboolean update); - - - - - - diff --git a/src/scrub-menu-item.vala b/src/scrub-menu-item.vala deleted file mode 100644 index e300050..0000000 --- a/src/scrub-menu-item.vala +++ /dev/null @@ -1,56 +0,0 @@ -/* -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 . -*/ - -using Dbusmenu; -using DbusmenuScrub; -using Gee; - -public class ScrubMenuitem : PlayerItem -{ - public ScrubMenuitem(PlayerController parent) - { - Object(item_type: MENUITEM_TYPE, owner: parent); - reset(attributes_format()); - } - - public override void handle_event(string name, GLib.Value input_value, uint timestamp) - { - debug("handle_event for owner %s with value: %f", this.owner.name, input_value.get_double()); - this.owner.mpris_bridge.set_track_position(input_value.get_double()); - } - - public void update_position(int32 new_position) - { - this.property_set_int(MENUITEM_POSITION, new_position); - } - - public void update_playstate(int state) - { - this.property_set_int(MENUITEM_PLAY_STATE, state); - } - - public static HashSet attributes_format() - { - HashSet attrs = new HashSet(); - attrs.add(MENUITEM_DURATION); - attrs.add(MENUITEM_POSITION); - attrs.add(MENUITEM_PLAY_STATE); - return attrs; - } -} \ No newline at end of file diff --git a/src/settings-manager.vala b/src/settings-manager.vala deleted file mode 100644 index 458ac21..0000000 --- a/src/settings-manager.vala +++ /dev/null @@ -1,136 +0,0 @@ -/* -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 . -*/ -using Gee; - -public class SettingsManager : GLib.Object -{ - private Settings settings; - public signal void blacklist_updates ( string[] new_blacklist ); - public signal void preferred_updates (Gee.ArrayList new_preferred); - - public SettingsManager ( ){ - } - construct{ - this.settings = new Settings ("com.canonical.indicator.sound"); - this.settings.changed["blacklisted-media-players"].connect (on_blacklist_event); - this.settings.changed["preferred-media-players"].connect (on_preferred_event); - } - - public string[] fetch_blacklist() - { - return this.settings.get_strv ("blacklisted-media-players"); - } - - public ArrayList fetch_preferred() - { - var list = new ArrayList(); - - var preferred = this.settings.get_strv ("preferred-media-players"); - var interested = fetch_interested (); - - foreach (var s in preferred) { - if (!(s in list) && interested.contains (s)) - list.add (s); - } - - return list; - } - - public ArrayList fetch_interested() - { - var blacklisted = fetch_blacklist (); - var interested = this.settings.get_strv ("interested-media-players"); - var list = new ArrayList(); - foreach(var s in interested){ - if (s == "banshee-1"){ - s = "banshee"; - } - if (s in list) continue; - if (s in blacklisted) continue; - list.add(s); - } - return list; - } - - public void clear_list() - { - this.settings.reset("interested-media-players"); - } - - public void remove_interested (string app_desktop_name) - { - const string key = "interested-media-players"; - var players = new GLib.VariantBuilder (new VariantType ("as")); // array of strings - - foreach (var player in this.settings.get_strv (key)) { - if (player != app_desktop_name) - players.add ("s", player); - } - - this.settings.set_value(key, players.end()); - this.settings.apply(); - } - - public void add_interested (string app_desktop_name) - { - const string key = "interested-media-players"; - var players = new GLib.VariantBuilder (new VariantType ("as")); // array of strings - - foreach (var player in this.settings.get_strv (key)) { - if (player == app_desktop_name) - return; - players.add ("s", player); - } - - players.add ("s", app_desktop_name); - this.settings.set_value(key, players.end()); - this.settings.apply(); - } - - private void on_blacklist_event() - { - this.blacklist_updates(this.settings.get_strv ("blacklisted-media-players")); - } - - private void on_preferred_event() - { - this.preferred_updates (this.fetch_preferred()); - } - - // Convenient debug method inorder to provide visability over - // the contents of both interested and blacklisted containers in its gsettings -/** - private void reveal_contents() - { - var already_interested = this.settings.get_strv ("interested-media-players"); - foreach (var s in already_interested) - { - debug ("client %s is in interested array", s); - } - var blacklisted = this.settings.get_strv ("blacklisted-media-players"); - foreach (var s in blacklisted) - { - debug ("client %s is in blacklisted array", s); - } - - debug ("interested array size = %i", already_interested.length); - debug ("blacklisted array size = %i", blacklisted.length); - } -**/ -} diff --git a/src/slider-menu-item.c b/src/slider-menu-item.c deleted file mode 100644 index dc0671c..0000000 --- a/src/slider-menu-item.c +++ /dev/null @@ -1,258 +0,0 @@ -/* -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 . -*/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "slider-menu-item.h" -#include "common-defs.h" -#include "pulseaudio-mgr.h" - -typedef struct _SliderMenuItemPrivate SliderMenuItemPrivate; - -struct _SliderMenuItemPrivate { - Device* a_sink; - gint index; - gchar* name; - gboolean mute; - pa_cvolume volume; - pa_channel_map channel_map; - pa_volume_t base_volume; -}; - -#define SLIDER_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SLIDER_MENU_ITEM_TYPE, SliderMenuItemPrivate)) - -/* Prototypes */ -static void slider_menu_item_class_init (SliderMenuItemClass *klass); -static void slider_menu_item_init (SliderMenuItem *self); -static void slider_menu_item_dispose (GObject *object); -static void slider_menu_item_finalize (GObject *object); -static void handle_event (DbusmenuMenuitem * mi, const gchar * name, - GVariant * value, guint timestamp); -static pa_cvolume slider_menu_item_construct_mono_volume (const pa_cvolume* vol); -static void slider_menu_item_update_volume (SliderMenuItem* self, gdouble percent); - -G_DEFINE_TYPE (SliderMenuItem, slider_menu_item, DBUSMENU_TYPE_MENUITEM); - -static void -slider_menu_item_class_init (SliderMenuItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (SliderMenuItemPrivate)); - - object_class->dispose = slider_menu_item_dispose; - object_class->finalize = slider_menu_item_finalize; - - DbusmenuMenuitemClass * mclass = DBUSMENU_MENUITEM_CLASS(klass); - mclass->handle_event = handle_event; - return; -} - -static void -slider_menu_item_init (SliderMenuItem *self) -{ - dbusmenu_menuitem_property_set( DBUSMENU_MENUITEM(self), - DBUSMENU_MENUITEM_PROP_TYPE, - DBUSMENU_VOLUME_MENUITEM_TYPE ); - - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - - priv->index = NOT_ACTIVE; - priv->name = NULL; - - return; -} - -static void -slider_menu_item_dispose (GObject *object) -{ - G_OBJECT_CLASS (slider_menu_item_parent_class)->dispose (object); - return; -} - -static void -slider_menu_item_finalize (GObject *object) -{ - G_OBJECT_CLASS (slider_menu_item_parent_class)->finalize (object); -} - -static void -handle_event (DbusmenuMenuitem * mi, - const gchar * name, - GVariant * value, - guint timestamp) -{ - g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)); - g_return_if_fail (IS_SLIDER_MENU_ITEM (mi)); - - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (SLIDER_MENU_ITEM (mi)); - gdouble volume_input = g_variant_get_double (value); - -/* - g_debug ("slider menu item handle event with value %f on name %s", - volume_input, - name); -*/ - - slider_menu_item_update_volume (SLIDER_MENU_ITEM (mi), volume_input); - if (volume_input > 0) - device_ensure_sink_is_unmuted (priv->a_sink); -} - - -void -slider_menu_item_populate (SliderMenuItem* self, const pa_sink_info* update) -{ - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - priv->name = g_strdup (update->name); - priv->index = update->index; - priv->volume = slider_menu_item_construct_mono_volume (&update->volume); - priv->base_volume = update->base_volume; - priv->channel_map = update->channel_map; - priv->mute = update->mute; - - pa_volume_t vol = pa_cvolume_max (&update->volume); - gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM; - GVariant* new_volume = g_variant_new_double (volume_percent); - dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(self), - DBUSMENU_VOLUME_MENUITEM_LEVEL, - new_volume); - GVariant* new_mute_update = g_variant_new_boolean (update->mute == 1); - dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(self), - DBUSMENU_VOLUME_MENUITEM_MUTE, - new_mute_update); - - slider_menu_item_enable (self, TRUE); -} - -// From the UI -static void -slider_menu_item_update_volume (SliderMenuItem* self, gdouble percent) -{ - g_return_if_fail (IS_SLIDER_MENU_ITEM (self)); - - pa_cvolume mono_new_volume; - pa_cvolume_init(&mono_new_volume); - mono_new_volume.channels = 1; - pa_volume_t new_volume_value = (pa_volume_t) ((percent * PA_VOLUME_NORM) / 100); - - if (new_volume_value == PA_VOLUME_INVALID || new_volume_value >= PA_VOLUME_MAX){ - g_warning ("slider_menu_item_update_volume - volume is out of range !"); - return; - } - - pa_cvolume_set(&mono_new_volume, 1, new_volume_value); - - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - if (!pa_cvolume_valid (&mono_new_volume)){ - g_warning ("Invalid volume - ignore it!"); - return; - } - if (!pa_channel_map_valid(&priv->channel_map)){ - g_warning ("Invalid channel map - ignore update volume!"); - return; - } - pa_cvolume_set(&priv->volume, priv->channel_map.channels, new_volume_value); - pm_update_volume (priv->index, mono_new_volume); -} - -// To the UI -void -slider_menu_item_update (SliderMenuItem* self, const pa_sink_info* update) -{ - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - - priv->volume = slider_menu_item_construct_mono_volume (&update->volume); - priv->base_volume = update->base_volume; - priv->channel_map = update->channel_map; - - pa_volume_t vol = pa_cvolume_max (&update->volume); - gdouble volume_percent = ((gdouble) vol * 100) / PA_VOLUME_NORM; - - GVariant* new_volume = g_variant_new_double (volume_percent); - -/* - g_debug ("slider menu item update - volume update to ui to %f", volume_percent); -*/ - - dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(self), - DBUSMENU_VOLUME_MENUITEM_LEVEL, - new_volume); - - if (priv->mute != update->mute){ - priv->mute = update->mute; -/* - g_debug ("volume menu item - update - mute on ui = %i", update->mute); -*/ - GVariant* new_mute_update = g_variant_new_boolean (update->mute == 1); - dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(self), - DBUSMENU_VOLUME_MENUITEM_MUTE, - new_mute_update); - } -} - -/* - * Enable/Disabled can be considered the equivalent of whether we have an active - * sink or not, let the widget have inherent state. - */ -void -slider_menu_item_enable (SliderMenuItem* self, gboolean active) -{ - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - - dbusmenu_menuitem_property_set_bool (DBUSMENU_MENUITEM(self), - DBUSMENU_MENUITEM_PROP_ENABLED, - active); - if(active == FALSE){ - priv->index = NOT_ACTIVE; - if(priv->name != NULL){ - g_free(priv->name); - priv->name = NULL; - } - } -} - -gint -slider_menu_item_get_sink_index (SliderMenuItem* self) -{ - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - return priv->index; -} - -static pa_cvolume -slider_menu_item_construct_mono_volume (const pa_cvolume* vol) -{ - pa_cvolume new_volume; - pa_cvolume_init(&new_volume); - new_volume.channels = 1; - pa_volume_t max_vol = pa_cvolume_max(vol); - pa_cvolume_set(&new_volume, 1, max_vol); - return new_volume; -} - -SliderMenuItem* -slider_menu_item_new (Device* sink) -{ - SliderMenuItem *self = g_object_new(SLIDER_MENU_ITEM_TYPE, NULL); - SliderMenuItemPrivate* priv = SLIDER_MENU_ITEM_GET_PRIVATE (self); - priv->a_sink = sink; - return self; -} diff --git a/src/slider-menu-item.h b/src/slider-menu-item.h deleted file mode 100644 index 4375971..0000000 --- a/src/slider-menu-item.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 . -*/ -#ifndef __SLIDER_MENU_ITEM_H__ -#define __SLIDER_MENU_ITEM_H__ - -#include -#include - -#include -#include "device.h" - -G_BEGIN_DECLS - -#define SLIDER_MENU_ITEM_TYPE (slider_menu_item_get_type ()) -#define SLIDER_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SLIDER_MENU_ITEM_TYPE, SliderMenuItem)) -#define SLIDER_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SLIDER_MENU_ITEM_TYPE, SliderMenuItemClass)) -#define IS_SLIDER_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SLIDER_MENU_ITEM_TYPE)) -#define IS_SLIDER_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SLIDER_MENU_ITEM_TYPE)) -#define SLIDER_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SLIDER_MENU_ITEM_TYPE, SliderMenuItemClass)) - -typedef struct _SliderMenuItem SliderMenuItem; -typedef struct _SliderMenuItemClass SliderMenuItemClass; - -struct _SliderMenuItemClass { - DbusmenuMenuitemClass parent_class; -}; - -struct _SliderMenuItem { - DbusmenuMenuitem parent; -}; - -GType slider_menu_item_get_type (void); - -void slider_menu_item_update(SliderMenuItem* item, const pa_sink_info* update); -void slider_menu_item_enable(SliderMenuItem* item, gboolean active); -void slider_menu_item_populate (SliderMenuItem* self, const pa_sink_info* update); -//void -//active_sink_update (ActiveSink* sink, -// const pa_sink_info* update) - -gint slider_menu_item_get_sink_index (SliderMenuItem* self); - -SliderMenuItem* slider_menu_item_new (Device* sink); - -G_END_DECLS - -#endif - diff --git a/src/sound-state.c b/src/sound-state.c deleted file mode 100644 index 72e411a..0000000 --- a/src/sound-state.c +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 "config.h" - -#include "sound-state.h" - -SoundState -sound_state_get_from_volume (int volume_percent) -{ - SoundState state = LOW_LEVEL; - - if (volume_percent < 30 && volume_percent > 0) { - state = LOW_LEVEL; - } - else if (volume_percent < 70 && volume_percent >= 30) { - state = MEDIUM_LEVEL; - } - else if (volume_percent >= 70) { - state = HIGH_LEVEL; - } - else if (volume_percent <= 0) { - state = ZERO_LEVEL; - } - return state; -} - diff --git a/src/sound-state.h b/src/sound-state.h deleted file mode 100644 index 9527c8e..0000000 --- a/src/sound-state.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2011 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 . -*/ - -#ifndef _SOUND_STATE_H_ -#define _SOUND_STATE_H_ - -#include -#include "common-defs.h" - -/* Helper functions for determining SOUNDSTATE */ - -SoundState sound_state_get_from_volume (int volume_percent); - -#endif /* _SOUND_STATE_H_ */ - diff --git a/src/specific-items-manager.vala b/src/specific-items-manager.vala deleted file mode 100644 index 923cf3f..0000000 --- a/src/specific-items-manager.vala +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2011 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 . -*/ - -using Dbusmenu; -using Gee; - -public class SpecificItemsManager : GLib.Object -{ - public enum category{ - TRACK, - PLAYER - } - - private PlayerController owner {get; set;} - private string dbus_path; - private Dbusmenu.Client client; - public Gee.ArrayList proxy_items {get; construct;} - private int of_type; - - public SpecificItemsManager (PlayerController controller, - string path, - category which_type) - { - this.of_type = which_type; - this.owner = controller; - this.dbus_path = path; - this.client = new Dbusmenu.Client (this.owner.dbus_name, this.dbus_path); - this.client.root_changed.connect (on_root_changed); - } - construct{ - this.proxy_items = new ArrayList(); - } - - private int figure_out_positioning() - { - int result = 0 ; - if (this.of_type == category.TRACK){ - result = this.owner.menu_offset + this.owner.WIDGET_QUANTITY + this.proxy_items.size; - } - else if (this.of_type == category.PLAYER){ - int pos = this.owner.menu_offset + this.owner.WIDGET_QUANTITY + this.owner.track_specific_count(); - //Surely the playlists item is there whether its visible or not ? - //TODO test playlists and player specific item positioning. - pos += this.owner.use_playlists == true ? 1 : 0; - result = pos; - } - debug ("!!!!! Menu pos of type %i is = %i", this.of_type, result); - return result; - } - - private void on_root_changed (GLib.Object? newroot) - { - if (newroot == null){ - debug ("root disappeared -remove proxyitems"); - foreach(var p in proxy_items){ - this.owner.root_menu.child_delete (p); - } - this.proxy_items.clear(); - debug ("array list size is now %i", this.proxy_items.size); - //this.proxy_items = new ArrayList(); - return; - } - - Dbusmenu.Menuitem? root = this.client.get_root(); - root.child_added.connect (on_child_added); - root.child_removed.connect (on_child_removed); - - // Fetch what children are there already. - GLib.List children = root.get_children().copy(); - - foreach (void* child in children) { - int pos = figure_out_positioning(); - unowned Dbusmenu.Menuitem item = (Dbusmenu.Menuitem)child; - Dbusmenu.MenuitemProxy proxy = new Dbusmenu.MenuitemProxy(item); - proxy_items.add (proxy); - debug ("Proxy item of label = %s added to collection", - item.property_get (MENUITEM_PROP_LABEL)); - this.owner.root_menu.child_add_position (proxy, pos); - - } - } - - private void on_child_added (GLib.Object child, uint position) - { - debug ("On child added Specific root node"); - } - - private void on_child_removed (GLib.Object child) - { - debug ("On child removed Specific root node"); - } - -} diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala deleted file mode 100644 index 4ec7dad..0000000 --- a/src/transport-menu-item.vala +++ /dev/null @@ -1,97 +0,0 @@ -/* -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 . -*/ - -using Dbusmenu; -using Gee; -using DbusmenuTransport; -using Transport; - -public class TransportMenuitem : PlayerItem -{ - private Transport.Action cached_action; - - private bool running { - get{ - return this.owner.current_state == PlayerController.state.CONNECTED; - } - } - - public TransportMenuitem(PlayerController parent) - { - Object(item_type: MENUITEM_TYPE, owner: parent); - } - construct{ - this.property_set_int(MENUITEM_PLAY_STATE, (int)Transport.State.PAUSED); - this.property_set (MENUITEM_PROP_LABEL, this.owner.app_info.get_name()); - this.cached_action = Transport.Action.NO_ACTION; - } - - /** - Please remove this timeout when the default player can handle mpris commands - immediately once it raises its dbus interface - **/ - public void handle_cached_action() - { - if (this.cached_action != Transport.Action.NO_ACTION){ - Timeout.add_seconds (1, send_cached_action); - } - } - - private bool send_cached_action() - { - this.owner.mpris_bridge.transport_update(this.cached_action); - this.cached_action = Transport.Action.NO_ACTION; - return false; - } - - public void change_play_state (Transport.State update) - { - int temp = (int)update; - this.property_set_int(MENUITEM_PLAY_STATE, temp); - } - - public override void handle_event(string name, - Variant input_value, - uint timestamp) - { - Variant v = input_value; - if ( input_value.is_of_type (VariantType.VARIANT)){ - v = input_value.get_variant(); - } - - int32 input = v.get_int32(); - - if (this.running == true){ - this.owner.mpris_bridge.transport_update((Transport.Action)input); - } - else{ - this.cached_action = (Transport.Action)input; - this.owner.instantiate(); - this.property_set_int (MENUITEM_PLAY_STATE, (int)Transport.State.LAUNCHING); - } - } - - public static HashSet attributes_format() - { - HashSet attrs = new HashSet(); - attrs.add(MENUITEM_PLAY_STATE); - return attrs; - } - -} diff --git a/src/voip-input-menu-item.c b/src/voip-input-menu-item.c deleted file mode 100644 index 7cbdd45..0000000 --- a/src/voip-input-menu-item.c +++ /dev/null @@ -1,278 +0,0 @@ -/* -Copyright 2011 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 . -*/ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "voip-input-menu-item.h" -#include "common-defs.h" -#include "pulseaudio-mgr.h" - -typedef struct _VoipInputMenuItemPrivate VoipInputMenuItemPrivate; - -struct _VoipInputMenuItemPrivate { - Device* a_sink; - pa_cvolume volume; - gint mute; - guint32 volume_steps; - pa_channel_map channel_map; - pa_volume_t base_volume; - gint source_index; - gint source_output_index; - gint client_index; -}; - -#define VOIP_INPUT_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VOIP_INPUT_MENU_ITEM_TYPE, VoipInputMenuItemPrivate)) - -/* Prototypes */ -static void voip_input_menu_item_class_init (VoipInputMenuItemClass *klass); -static void voip_input_menu_item_init (VoipInputMenuItem *self); -static void voip_input_menu_item_dispose (GObject *object); -static void voip_input_menu_item_finalize (GObject *object); -static void handle_event (DbusmenuMenuitem * mi, const gchar * name, - GVariant * value, guint timestamp); -// TODO: -// This method should really be shared between this and the volume slider obj -// perfectly static - wait until the device mgr wrapper is properly sorted and -// then consolidate -static pa_cvolume voip_input_menu_item_construct_mono_volume (const pa_cvolume* vol); - -G_DEFINE_TYPE (VoipInputMenuItem, voip_input_menu_item, DBUSMENU_TYPE_MENUITEM); - -static void -voip_input_menu_item_class_init (VoipInputMenuItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (VoipInputMenuItemPrivate)); - - object_class->dispose = voip_input_menu_item_dispose; - object_class->finalize = voip_input_menu_item_finalize; - - DbusmenuMenuitemClass * mclass = DBUSMENU_MENUITEM_CLASS(klass); - mclass->handle_event = handle_event; -} - -static void -voip_input_menu_item_init (VoipInputMenuItem *self) -{ - dbusmenu_menuitem_property_set( DBUSMENU_MENUITEM(self), - DBUSMENU_MENUITEM_PROP_TYPE, - DBUSMENU_VOIP_INPUT_MENUITEM_TYPE ); - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (self); - dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(self), - DBUSMENU_MENUITEM_PROP_VISIBLE, - FALSE ); - - priv->source_index = NOT_ACTIVE; - priv->source_output_index = NOT_ACTIVE; - priv->client_index = NOT_ACTIVE; - priv->mute = NOT_ACTIVE; -} - -static void -voip_input_menu_item_dispose (GObject *object) -{ - G_OBJECT_CLASS (voip_input_menu_item_parent_class)->dispose (object); - return; -} - -static void -voip_input_menu_item_finalize (GObject *object) -{ - G_OBJECT_CLASS (voip_input_menu_item_parent_class)->finalize (object); -} - -static void -handle_event (DbusmenuMenuitem * mi, - const gchar * name, - GVariant * value, - guint timestamp) -{ - GVariant* input = NULL; - input = value; - if (g_variant_is_of_type(value, G_VARIANT_TYPE_VARIANT) == TRUE) { - input = g_variant_get_variant(value); - } - - gdouble percent = g_variant_get_double(input); - if (value != NULL){ - if (IS_VOIP_INPUT_MENU_ITEM (mi)) { - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (VOIP_INPUT_MENU_ITEM (mi)); -/* - g_debug ("Handle event in the voip input level backend instance - %f", percent); -*/ - pa_cvolume new_volume; - pa_cvolume_init(&new_volume); - new_volume.channels = 1; - pa_volume_t new_volume_value = (pa_volume_t) ((percent * PA_VOLUME_NORM) / 100); - pa_cvolume_set(&new_volume, 1, new_volume_value); - - pm_update_mic_gain (priv->source_index, new_volume); - // finally unmute if needed - if (priv->mute == 1) { - pm_update_mic_mute (priv->source_index, 0); - } - } - } -} - -static pa_cvolume -voip_input_menu_item_construct_mono_volume (const pa_cvolume* vol) -{ - pa_cvolume new_volume; - pa_cvolume_init(&new_volume); - new_volume.channels = 1; - pa_volume_t max_vol = pa_cvolume_max(vol); - pa_cvolume_set(&new_volume, 1, max_vol); - return new_volume; -} - -void -voip_input_menu_item_update (VoipInputMenuItem* item, - const pa_source_info* source) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - // only overwrite the constants of each source if the device has changed - if (priv->source_index == NOT_ACTIVE){ - priv->base_volume = source->base_volume; - priv->volume_steps = source->n_volume_steps; - priv->channel_map = source->channel_map; - priv->source_index = source->index; - } - priv->volume = voip_input_menu_item_construct_mono_volume (&source->volume); - pa_volume_t vol = pa_cvolume_max (&source->volume); - gdouble update = ((gdouble) vol * 100) / PA_VOLUME_NORM; - - GVariant* new_volume = g_variant_new_double(update); - dbusmenu_menuitem_property_set_variant(DBUSMENU_MENUITEM(item), - DBUSMENU_VOIP_INPUT_MENUITEM_LEVEL, - new_volume); - // Only send over the mute updates if the state has changed. - // in this order - volume first mute last!! - if (priv->mute != source->mute){ -/* - g_debug ("voip menu item - update - mute = %i", priv->mute); -*/ - GVariant* new_mute_update = g_variant_new_int32 (source->mute); - dbusmenu_menuitem_property_set_variant (DBUSMENU_MENUITEM(item), - DBUSMENU_VOIP_INPUT_MENUITEM_MUTE, - new_mute_update); - } - - priv->mute = source->mute; - -} - -gboolean -voip_input_menu_item_is_interested (VoipInputMenuItem* item, - gint source_output_index, - gint client_index) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - // Check to make sure we are not handling another voip beforehand and that we - // have an active sink (might need to match up at start up) - if (priv->source_output_index != NOT_ACTIVE && - priv->source_index != NOT_ACTIVE){ - return FALSE; - } - - priv->source_output_index = source_output_index; - priv->client_index = client_index; - - return TRUE; -} - -gboolean -voip_input_menu_item_is_active (VoipInputMenuItem* item) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - return (priv->source_output_index != NOT_ACTIVE && priv->client_index != NOT_ACTIVE); -} - - -gboolean -voip_input_menu_item_is_populated (VoipInputMenuItem* item) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - return priv->source_index != NOT_ACTIVE; -} - -gint -voip_input_menu_item_get_index (VoipInputMenuItem* item) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - return priv->source_index; -} - -gint -voip_input_menu_item_get_source_output_index (VoipInputMenuItem* item) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - - return priv->source_output_index; -} - -/** - * If the pulse server informs of a default source change - * or the source in question is removed. - * @param item - */ -void -voip_input_menu_item_deactivate_source (VoipInputMenuItem* item, gboolean visible) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - priv->source_index = NOT_ACTIVE; - dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item), - DBUSMENU_MENUITEM_PROP_VISIBLE, - visible ); -} - -void -voip_input_menu_item_deactivate_voip_client (VoipInputMenuItem* item) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - priv->client_index = NOT_ACTIVE; - priv->source_output_index = NOT_ACTIVE; - voip_input_menu_item_enable (item, FALSE); -} - -void -voip_input_menu_item_enable (VoipInputMenuItem* item, - gboolean active) -{ - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (item); - if (priv->source_index == NOT_ACTIVE && active == TRUE) { - g_warning ("Tried to enable the VOIP menuitem but we don't have an active source ??"); - active = FALSE; - } - dbusmenu_menuitem_property_set_bool( DBUSMENU_MENUITEM(item), - DBUSMENU_MENUITEM_PROP_VISIBLE, - active ); -} - -VoipInputMenuItem* -voip_input_menu_item_new (Device* sink) -{ - VoipInputMenuItem *self = g_object_new(VOIP_INPUT_MENU_ITEM_TYPE, NULL); - VoipInputMenuItemPrivate* priv = VOIP_INPUT_MENU_ITEM_GET_PRIVATE (self); - priv->a_sink = sink; - return self; -} \ No newline at end of file -- cgit v1.2.3 From e2d1ed2cb0b066a7b34db15e42af0a4b49626ec8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Jun 2013 15:00:27 -0400 Subject: Allow setting the microphone volume if an app using it is running --- src/service.vala | 38 ++++++++++++++++++++ src/volume-control.vala | 93 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 126 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 269ddd7..83d0d17 100644 --- a/src/service.vala +++ b/src/service.vala @@ -25,6 +25,7 @@ public class IndicatorSound.Service { this.settings = new Settings ("com.canonical.indicator.sound"); this.volume_control = new VolumeControl (); + this.volume_control.notify["active-mic"].connect (active_mic_changed); this.players = new MediaPlayerList (); this.players.player_added.connect (this.player_added); @@ -34,6 +35,7 @@ public class IndicatorSound.Service { this.actions.add_entries (action_entries, this); this.actions.add_action (this.create_mute_action ()); this.actions.add_action (this.create_volume_action ()); + this.actions.add_action (this.create_mic_volume_action ()); this.menu = create_menu (); this.root_menu = create_root_menu (this.menu); @@ -112,6 +114,26 @@ public class IndicatorSound.Service { return menu; } + void active_mic_changed () { + var volume_section = this.menu.get_item_link (0, "section") as Menu; + if (this.volume_control.active_mic) { + if (volume_section.get_n_items () < 3) { + var slider = new MenuItem (null, "indicator.mic-volume"); + slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); + slider.set_attribute_value ("min-icon", g_icon_serialize (new ThemedIcon ("audio-input-microphone-low-zero-panel"))); + slider.set_attribute_value ("max-icon", g_icon_serialize (new ThemedIcon ("audio-input-microphone-high-panel"))); + slider.set_attribute ("min-value", "d", 0.0); + slider.set_attribute ("max-value", "d", 1.0); + slider.set_attribute ("step", "d", 0.01); + volume_section.append_item (slider); + } + } + else { + if (volume_section.get_n_items () > 2) + volume_section.remove (2); + } + } + Action create_mute_action () { var mute_action = new SimpleAction.stateful ("mute", null, this.volume_control.mute); @@ -146,6 +168,22 @@ public class IndicatorSound.Service { return volume_action; } + Action create_mic_volume_action () { + var volume_action = new SimpleAction.stateful ("mic-volume", null, this.volume_control.get_mic_volume ()); + + volume_action.change_state.connect ( (action, val) => { + volume_control.set_mic_volume (val.get_double ()); + }); + + this.volume_control.mic_volume_changed.connect ( (volume) => { + volume_action.set_state (volume); + }); + + this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE); + + return volume_action; + } + void bus_acquired (DBusConnection connection, string name) { try { connection.export_action_group ("/com/canonical/indicator/sound", this.actions); diff --git a/src/volume-control.vala b/src/volume-control.vala index 5ce5a05..9475f53 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -30,12 +30,17 @@ public class VolumeControl : Object private PulseAudio.Context context; private bool _mute = true; private double _volume = 0.0; + private double _mic_volume = 0.0; public signal void volume_changed (double v); + public signal void mic_volume_changed (double v); /** true when connected to the pulse server */ public bool ready { get; set; } + /** true when a microphone is active **/ + public bool active_mic { get; private set; default = false; } + public VolumeControl () { if (loop == null) @@ -61,9 +66,28 @@ public class VolumeControl : Object /* PulseAudio logic*/ private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) { - if ((t & Context.SubscriptionEventType.FACILITY_MASK) == Context.SubscriptionEventType.SINK) + switch (t & Context.SubscriptionEventType.FACILITY_MASK) { - get_properties (); + case Context.SubscriptionEventType.SINK: + update_sink (); + break; + + case Context.SubscriptionEventType.SOURCE: + update_source (); + break; + + case Context.SubscriptionEventType.SOURCE_OUTPUT: + switch (t & Context.SubscriptionEventType.TYPE_MASK) + { + case Context.SubscriptionEventType.NEW: + c.get_source_output_info (index, source_output_info_cb); + break; + + case Context.SubscriptionEventType.REMOVE: + this.active_mic = false; + break; + } + break; } } @@ -85,6 +109,18 @@ public class VolumeControl : Object } } + private void source_info_cb (Context c, SourceInfo? i, int eol) + { + if (i == null) + return; + + if (_mic_volume != volume_to_double (i.volume.values[0])) + { + _mic_volume = volume_to_double (i.volume.values[0]); + mic_volume_changed (_mic_volume); + } + } + private void server_info_cb_for_props (Context c, ServerInfo? i) { if (i == null) @@ -92,18 +128,39 @@ public class VolumeControl : Object context.get_sink_info_by_name (i.default_sink_name, sink_info_cb_for_props); } - private void get_properties () + private void update_sink () { context.get_server_info (server_info_cb_for_props); } + private void update_source () + { + context.get_server_info ( (c, i) => { + if (i != null) + context.get_source_info_by_name (i.default_source_name, source_info_cb); + }); + } + + private void source_output_info_cb (Context c, SourceOutputInfo? i, int eol) + { + if (i == null) + return; + + var role = i.proplist.gets (PulseAudio.Proplist.PROP_MEDIA_ROLE); + if (role == "phone" || role == "production") + this.active_mic = true; + } + private void context_state_callback (Context c) { if (c.get_state () == Context.State.READY) { - c.subscribe (PulseAudio.Context.SubscriptionMask.SINK); + c.subscribe (PulseAudio.Context.SubscriptionMask.SINK | + PulseAudio.Context.SubscriptionMask.SOURCE | + PulseAudio.Context.SubscriptionMask.SOURCE_OUTPUT); c.set_subscribe_callback (context_events_cb); - get_properties (); + update_sink (); + update_source (); this.ready = true; } else @@ -182,8 +239,34 @@ public class VolumeControl : Object context.get_server_info (server_info_cb_for_set_volume); } + void set_mic_volume_success_cb (Context c, int success) + { + if ((bool)success) + mic_volume_changed (_mic_volume); + } + + public void set_mic_volume (double volume) + { + return_if_fail (context.get_state () == Context.State.READY); + + _mic_volume = volume; + + context.get_server_info ( (c, i) => { + if (i != null) { + unowned CVolume cvol = CVolume (); + cvol = vol_set (cvol, 1, double_to_volume (_mic_volume)); + c.set_source_volume_by_name (i.default_source_name, cvol, set_mic_volume_success_cb); + } + }); + } + public double get_volume () { return _volume; } + + public double get_mic_volume () + { + return _mic_volume; + } } -- cgit v1.2.3 From d3ee0f57c526d7825eac40f416a72502793d6c05 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 18 Jun 2013 18:07:58 -0400 Subject: Update indicator icon when volume changes --- src/service.vala | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 83d0d17..2196135 100644 --- a/src/service.vala +++ b/src/service.vala @@ -134,6 +134,24 @@ public class IndicatorSound.Service { } } + void update_root_icon () { + double volume = this.volume_control.get_volume (); + string icon; + if (this.volume_control.mute) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.0) + icon = "audio-volume-low-zero-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + + var root_action = this.actions.lookup ("root") as SimpleAction; + root_action.set_state (new Variant.parsed ("{ 'icon': <%s> }", icon)); + } + Action create_mute_action () { var mute_action = new SimpleAction.stateful ("mute", null, this.volume_control.mute); @@ -147,11 +165,19 @@ public class IndicatorSound.Service { this.volume_control.notify["mute"].connect ( () => { mute_action.set_state (this.volume_control.mute); + this.update_root_icon (); }); return mute_action; } + void volume_changed (double volume) { + var volume_action = this.actions.lookup ("volume") as SimpleAction; + volume_action.set_state (volume); + + this.update_root_icon (); + } + Action create_volume_action () { var volume_action = new SimpleAction.stateful ("volume", null, this.volume_control.get_volume ()); @@ -159,9 +185,7 @@ public class IndicatorSound.Service { volume_control.set_volume (val.get_double ()); }); - this.volume_control.volume_changed.connect ( (volume) => { - volume_action.set_state (volume); - }); + this.volume_control.volume_changed.connect (volume_changed); this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE); -- cgit v1.2.3 From 6c65b1d8e63ab351ea4da525ecdfc5226161bfc6 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Jun 2013 12:06:21 -0400 Subject: Mark translatable strings as translatable --- src/Makefile.am | 4 +++- src/service.vala | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 84ee0f1..15a5cbf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,8 @@ indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ $(SOUNDSERVICE_CFLAGS) \ $(GCONF_CFLAGS) \ $(COVERAGE_CFLAGS) \ - -DLIBEXECDIR=\"$(libexecdir)\" -Wall + -DLIBEXECDIR=\"$(libexecdir)\" -Wall \ + -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" + indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS) indicator_sound_service_LDFLAGS = $(COVERAGE_LDFLAGS) diff --git a/src/service.vala b/src/service.vala index 2196135..add5658 100644 --- a/src/service.vala +++ b/src/service.vala @@ -96,7 +96,7 @@ public class IndicatorSound.Service { static Menu create_menu () { var volume_section = new Menu (); - volume_section.append ("Mute", "indicator.mute"); + volume_section.append (_("Mute"), "indicator.mute"); var slider = new MenuItem (null, "indicator.volume"); slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider"); @@ -109,7 +109,7 @@ public class IndicatorSound.Service { var menu = new Menu (); menu.append_section (null, volume_section); - menu.append ("Sound Settings…", "indicator.settings"); + menu.append (_("Sound Settings…"), "indicator.settings"); return menu; } -- cgit v1.2.3 From 356fd4ed9c9d394a8e4128accc93628df1b931d8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Jun 2013 14:58:12 -0400 Subject: Install indicator file --- src/com.canonical.indicator.sound | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/com.canonical.indicator.sound (limited to 'src') diff --git a/src/com.canonical.indicator.sound b/src/com.canonical.indicator.sound deleted file mode 100644 index 825fd30..0000000 --- a/src/com.canonical.indicator.sound +++ /dev/null @@ -1,6 +0,0 @@ -[Indicator Service] -Name=indicator-sound -ObjectPath=/com/canonical/indicator/sound - -[desktop] -ObjectPath=/com/canonical/indicator/sound/desktop -- cgit v1.2.3 From a0773c53b13546f4957f2b2ac1f0455fedc05525 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Jun 2013 15:29:12 -0400 Subject: Disable warnings for vala-generated C code --- src/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 15a5cbf..5958b51 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,11 +23,13 @@ indicator_sound_service_VALAFLAGS = \ --pkg libpulse-mainloop-glib \ --target-glib=2.36 +# -w to disable warnings for vala-generated code indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \ $(SOUNDSERVICE_CFLAGS) \ $(GCONF_CFLAGS) \ $(COVERAGE_CFLAGS) \ - -DLIBEXECDIR=\"$(libexecdir)\" -Wall \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -w \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS) -- cgit v1.2.3 From 9d78a433b18fd14af4d3cfa01684c32b93f91966 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 19 Jun 2013 18:40:31 -0400 Subject: Fold all patches into source These patches didn't apply to the current source. They added support for sound settings panels in various desktop environments. We need a better solution for that, which doesn't require patching the source when a desktop environment wants a different command line. --- src/service.vala | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index add5658..d638b10 100644 --- a/src/service.vala +++ b/src/service.vala @@ -76,8 +76,17 @@ public class IndicatorSound.Service { uint player_action_update_id; void activate_settings (SimpleAction action, Variant? param) { + var env = Environment.get_variable ("DESKTOP_SESSION"); + string cmd; + if (env == "unity") + cmd = "gnome-control-center sound-nua"; + else if (env == "xubuntu" || env == "ubuntustudio") + cmd = "pavucontrol"; + else + cmd = "gnome-control-center sound"; + try { - Process.spawn_command_line_async ("gnome-control-center sound"); + Process.spawn_command_line_async (cmd); } catch (Error e) { warning ("unable to launch sound settings: %s", e.message); } -- cgit v1.2.3 From 040e9977f7b83221c85d8b279d9537ad46303e20 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 28 Jun 2013 15:51:48 -0400 Subject: service.vala: make removing a player from the menu more readable --- src/service.vala | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index d638b10..7f89c66 100644 --- a/src/service.vala +++ b/src/service.vala @@ -308,23 +308,30 @@ public class IndicatorSound.Service { this.update_preferred_players (); } - void player_removed (MediaPlayer player) { - this.actions.remove (player.id); - this.actions.remove ("play." + player.id); - this.actions.remove ("next." + player.id); - this.actions.remove ("previous." + player.id); - + /* returns the position in this.menu of the section that's associated with @player */ + int find_player_section (MediaPlayer player) { int n = this.menu.get_n_items (); for (int i = 0; i < n; i++) { var section = this.menu.get_item_link (i, Menu.LINK_SECTION); string action; section.get_item_attribute (0, "action", "s", out action); - if (action == player.id) { - this.menu.remove (i); - break; - } + if (action == player.id) + return i; } + return -1; + } + + void player_removed (MediaPlayer player) { + this.actions.remove (player.id); + this.actions.remove ("play." + player.id); + this.actions.remove ("next." + player.id); + this.actions.remove ("previous." + player.id); + + int index = this.find_player_section (player); + if (index >= 0) + this.menu.remove (index); + this.update_preferred_players (); } } -- cgit v1.2.3 From a1bb4f83f6da959ede2c7e486d26304c63cfae40 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 28 Jun 2013 15:58:32 -0400 Subject: media-player-list, mpris2-watcher: remove unneded `public` from methods --- src/media-player-list.vala | 4 ++-- src/mpris2-watcher.vala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/media-player-list.vala b/src/media-player-list.vala index 6d37b4d..6eb5fc9 100644 --- a/src/media-player-list.vala +++ b/src/media-player-list.vala @@ -56,7 +56,7 @@ public class MediaPlayerList { /** * Adds the player associated with @desktop_id. Does nothing if such a player already exists. */ - public MediaPlayer? insert (string desktop_id) { + MediaPlayer? insert (string desktop_id) { var id = desktop_id.has_suffix (".desktop") ? desktop_id : desktop_id + ".desktop"; MediaPlayer? player = this._players.lookup (id); @@ -78,7 +78,7 @@ public class MediaPlayerList { /** * Removes the player associated with @desktop_id, unless it is currently running. */ - public void remove (string desktop_id) { + void remove (string desktop_id) { MediaPlayer? player = this._players.lookup (desktop_id); if (player != null && !player.is_running) { diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 740089d..4a1ab6e 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -69,7 +69,7 @@ public class Mpris2Watcher : GLib.Object // At startup check to see if there are clients up that we are interested in // More relevant for development and daemon's like mpd. - public async void check_for_active_clients() + async void check_for_active_clients() { Variant interfaces; @@ -99,7 +99,7 @@ public class Mpris2Watcher : GLib.Object } } - public void name_owner_changed (DBusConnection con, string sender, string object_path, + void name_owner_changed (DBusConnection con, string sender, string object_path, string interface_name, string signal_name, Variant parameters) { string name, previous_owner, current_owner; -- cgit v1.2.3 From 7ca667494e32d110766c2b4a013b6a2c37b8a084 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Jul 2013 16:09:09 -0400 Subject: Put service into $pkglibexecdir --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5958b51..da3df5b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -libexec_PROGRAMS = indicator-sound-service +pkglibexec_PROGRAMS = indicator-sound-service indicator_sound_service_SOURCES = \ service.vala \ -- cgit v1.2.3 From 99b12cdd16a1fea2740b812d697bea887df2b5bd Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 4 Jul 2013 16:54:55 -0400 Subject: Set locale and bind text domain --- src/main.vala | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/main.vala b/src/main.vala index 1ec7d2c..97f311f 100644 --- a/src/main.vala +++ b/src/main.vala @@ -1,5 +1,12 @@ +[CCode (cheader_filename="libintl.h", type="char *")] +extern unowned string bind_textdomain_codeset (string domainname, string codeset); + static int main (string[] args) { + bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); + Intl.setlocale (LocaleCategory.ALL, ""); + Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); + var service = new IndicatorSound.Service (); return service.run (); } -- cgit v1.2.3 From 5d9874b1df7ea7c0f7e1c2764b4d8cbd2bad7684 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sat, 6 Jul 2013 17:00:57 -0400 Subject: Launch player when the play action is activated --- src/media-player.vala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index 5b59efb..68a0b38 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -133,8 +133,13 @@ public class MediaPlayer: Object { * Toggles playing status. */ public void play_pause () { - if (this.proxy != null) + if (this.proxy != null) { this.proxy.PlayPause.begin (); + } + else if (this.state != "Launching") { + this.play_when_attached = true; + this.launch (); + } } /** @@ -156,6 +161,7 @@ public class MediaPlayer: Object { DesktopAppInfo appinfo; MprisPlayer? proxy; string _dbus_name; + bool play_when_attached = false; void got_proxy (Object? obj, AsyncResult res) { try { @@ -169,6 +175,13 @@ public class MediaPlayer: Object { this.notify_property ("is-running"); this.state = this.proxy.PlaybackStatus; this.update_current_track (gproxy.get_cached_property ("Metadata")); + + if (this.play_when_attached) { + /* wait a little before calling PlayPause, some players need some time to + set themselves up */ + Timeout.add (1000, () => { proxy.PlayPause.begin (); return false; } ); + this.play_when_attached = false; + } } catch (Error e) { this._dbus_name = null; -- cgit v1.2.3 From 503210d8ee47e21370fc2751ca88b89715221449 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Sat, 6 Jul 2013 18:48:48 -0400 Subject: Don't set player state to "Launching" when the player is already running --- src/media-player.vala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index 68a0b38..9c18f10 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -126,7 +126,9 @@ public class MediaPlayer: Object { catch (Error e) { warning ("unable to launch %s: %s", appinfo.get_name (), e.message); } - this.state = "Launching"; + + if (this.proxy == null) + this.state = "Launching"; } /** -- cgit v1.2.3 From 859b8bb032ac6cb4f1118b869cd115c561d4f2f5 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 10 Jul 2013 17:06:52 +0200 Subject: Expose playlists in the menu if the player exports them --- src/media-player.vala | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/service.vala | 47 +++++++++++++++++++++++++++++++++--- 2 files changed, 110 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/media-player.vala b/src/media-player.vala index 9c18f10..7326708 100644 --- a/src/media-player.vala +++ b/src/media-player.vala @@ -84,6 +84,8 @@ public class MediaPlayer: Object { get; set; } + public signal void playlists_changed (); + /** * Attach this object to a process of the associated media player. The player must own @dbus_name and * implement the org.mpris.MediaPlayer2.Player interface. @@ -98,6 +100,8 @@ public class MediaPlayer: Object { this._dbus_name = dbus_name; Bus.get_proxy.begin (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_proxy); + Bus.get_proxy.begin (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2", + DBusProxyFlags.GET_INVALIDATED_PROPERTIES, null, got_playlists_proxy); } /** @@ -160,10 +164,31 @@ public class MediaPlayer: Object { this.proxy.Previous.begin (); } + public uint get_n_playlists () { + return this.playlists != null ? this.playlists.length : 0; + } + + public string get_playlist_id (int index) { + return_val_if_fail (index < this.playlists.length, ""); + return this.playlists[index].path; + } + + public string get_playlist_name (int index) { + return_val_if_fail (index < this.playlists.length, ""); + return this.playlists[index].name; + } + + public void activate_playlist_by_name (string name) { + if (this.playlists_proxy != null) + this.playlists_proxy.ActivatePlaylist.begin (new ObjectPath (name)); + } + DesktopAppInfo appinfo; MprisPlayer? proxy; + MprisPlaylists ?playlists_proxy; string _dbus_name; bool play_when_attached = false; + PlaylistDetails[] playlists = null; void got_proxy (Object? obj, AsyncResult res) { try { @@ -191,6 +216,42 @@ public class MediaPlayer: Object { } } + void fetch_playlists () { + /* The proxy is created even when the interface is not supported. GDBusProxy will + return 0 for the PlaylistCount property in that case. */ + if (this.playlists_proxy != null && this.playlists_proxy.PlaylistCount > 0) { + this.playlists_proxy.GetPlaylists.begin (0, 100, "Alphabetical", false, (obj, res) => { + try { + this.playlists = playlists_proxy.GetPlaylists.end (res); + this.playlists_changed (); + } + catch (Error e) { + warning ("could not fetch playlists: %s", e.message); + this.playlists = null; + } + }); + } + else { + this.playlists = null; + this.playlists_changed (); + } + } + + void got_playlists_proxy (Object? obj, AsyncResult res) { + try { + this.playlists_proxy = Bus.get_proxy.end (res); + + var gproxy = this.proxy as DBusProxy; + gproxy.g_properties_changed.connect (this.playlists_proxy_properties_changed); + } + catch (Error e) { + warning ("unable to create mpris plalists proxy: %s", e.message); + return; + } + + Timeout.add (500, () => { this.fetch_playlists (); return false; } ); + } + /* some players (e.g. Spotify) don't follow the spec closely and pass single strings in metadata fields * where an array of string is expected */ static string sanitize_metadata_value (Variant? v) { @@ -215,6 +276,11 @@ public class MediaPlayer: Object { this.update_current_track (metadata); } + void playlists_proxy_properties_changed (DBusProxy proxy, Variant changed_properties, string[] invalidated_properties) { + if (changed_properties.lookup ("PlaylistCount", "u", null)) + this.fetch_playlists (); + } + void update_current_track (Variant metadata) { if (metadata != null) { this.current_track = new Track ( diff --git a/src/service.vala b/src/service.vala index 7f89c66..6a330f2 100644 --- a/src/service.vala +++ b/src/service.vala @@ -266,6 +266,37 @@ public class IndicatorSound.Service { this.settings.set_value ("preferred-media-players", builder.end ()); } + void update_playlists (MediaPlayer player) { + int index = find_player_section (player); + if (index < 0) + return; + + var section = this.menu.get_item_link (index, Menu.LINK_SECTION) as Menu; + + /* if a section has three items, the playlists menu is in it */ + if (section.get_n_items () == 3) + section.remove (2); + + if (!player.is_running) + return; + + var count = player.get_n_playlists (); + if (count == 0) + return; + + var playlists_section = new Menu (); + for (int i = 0; i < count; i++) { + var playlist_id = player.get_playlist_id (i); + playlists_section.append (player.get_playlist_name (i), + @"indicator.play-playlist.$(player.id)::$playlist_id"); + + } + + var submenu = new Menu (); + submenu.append_section (null, playlists_section); + section.append_submenu ("Choose Playlist", submenu); + } + void player_added (MediaPlayer player) { var player_item = new MenuItem (player.name, "indicator." + player.id); player_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player"); @@ -303,19 +334,28 @@ public class IndicatorSound.Service { prev_action.activate.connect ( () => player.previous () ); this.actions.insert (prev_action); + var playlist_action = new SimpleAction ("play-playlist." + player.id, VariantType.STRING); + playlist_action.activate.connect ( (parameter) => player.activate_playlist_by_name (parameter.get_string ()) ); + this.actions.insert (playlist_action); + player.notify.connect (this.eventually_update_player_actions); + player.playlists_changed.connect (this.update_playlists); + player.notify["is-running"].connect ( () => this.update_playlists (player) ); + update_playlists (player); + this.update_preferred_players (); } /* returns the position in this.menu of the section that's associated with @player */ int find_player_section (MediaPlayer player) { - int n = this.menu.get_n_items (); - for (int i = 0; i < n; i++) { + string action_name = @"indicator.$(player.id)"; + int n = this.menu.get_n_items () -1; + for (int i = 1; i < n; i++) { var section = this.menu.get_item_link (i, Menu.LINK_SECTION); string action; section.get_item_attribute (0, "action", "s", out action); - if (action == player.id) + if (action == action_name) return i; } @@ -327,6 +367,7 @@ public class IndicatorSound.Service { this.actions.remove ("play." + player.id); this.actions.remove ("next." + player.id); this.actions.remove ("previous." + player.id); + this.actions.remove ("play-playlist." + player.id); int index = this.find_player_section (player); if (index >= 0) -- cgit v1.2.3 From 9507f278131fc855c03a037cdba1053f958d2987 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 11 Jul 2013 13:26:56 +0200 Subject: Use interested-media-players instead of preferred-media-players gsettings key --- src/service.vala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/service.vala b/src/service.vala index 6a330f2..3d14d5e 100644 --- a/src/service.vala +++ b/src/service.vala @@ -40,9 +40,9 @@ public class IndicatorSound.Service { this.menu = create_menu (); this.root_menu = create_root_menu (this.menu); - this.players.sync (settings.get_strv ("preferred-media-players")); - this.settings.changed["preferred-media-players"].connect ( () => { - this.players.sync (settings.get_strv ("preferred-media-players")); + this.players.sync (settings.get_strv ("interested-media-players")); + this.settings.changed["interested-media-players"].connect ( () => { + this.players.sync (settings.get_strv ("interested-media-players")); }); } @@ -263,7 +263,7 @@ public class IndicatorSound.Service { var builder = new VariantBuilder (VariantType.STRING_ARRAY); foreach (var player in this.players) builder.add ("s", player.id); - this.settings.set_value ("preferred-media-players", builder.end ()); + this.settings.set_value ("interested-media-players", builder.end ()); } void update_playlists (MediaPlayer player) { -- cgit v1.2.3