diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/familiar-players-db.vala | 10 | ||||
-rw-r--r-- | src/metadata-widget.c | 33 | ||||
-rw-r--r-- | src/mpris2-controller.vala | 12 | ||||
-rw-r--r-- | src/music-player-bridge.vala | 40 | ||||
-rw-r--r-- | src/player-controller.vala | 49 | ||||
-rw-r--r-- | src/pulse-manager.c | 84 | ||||
-rw-r--r-- | src/title-widget.c | 5 | ||||
-rw-r--r-- | src/transport-menu-item.vala | 3 | ||||
-rw-r--r-- | src/transport-widget.c | 23 |
10 files changed, 177 insertions, 86 deletions
diff --git a/configure.ac b/configure.ac index 7f25bb3..300662b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ -AC_INIT(indicator-sound, 0.4.8, conor.curran@canonical.com) +AC_INIT(indicator-sound, 0.5.0, conor.curran@canonical.com) AC_PREREQ(2.53) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(indicator-sound, 0.4.8) +AM_INIT_AUTOMAKE(indicator-sound, 0.5.0) AM_MAINTAINER_MODE diff --git a/src/familiar-players-db.vala b/src/familiar-players-db.vala index 76cc2f1..af7d07f 100644 --- a/src/familiar-players-db.vala +++ b/src/familiar-players-db.vala @@ -29,6 +29,7 @@ public class FamiliarPlayersDB : GLib.Object { private const string GROUP_NAME = "Seen Database"; private const string KEY_NAME = "DesktopFiles"; + private const string DEFAULT_APP_DESKTOP = "/usr/share/applications/banshee-1.desktop"; private HashMap<string, bool> players_DB; private string file_name; private string dir_name; @@ -38,13 +39,18 @@ public class FamiliarPlayersDB : GLib.Object public FamiliarPlayersDB() { this.write_id = 0; - this.players_DB = new HashMap<string, bool>(); + this.players_DB = new HashMap<string, bool>(); + if ( !create_key_file() ){ + this.players_DB.set(DEFAULT_APP_DESKTOP, true); + this.write_db(); + } + this.dir_name = build_filename(get_user_cache_dir(), "indicators", "sound"); this.file_name = build_filename(this.dir_name, "familiar-players-db.keyfile"); if(create_key_file() && check_for_keys() && load_data_from_key_file()){ debug("keyfiles in place and ready for action"); } - else{ + else{ this.key_file = null; warning("FamiliarPlayersDB:: problems loading key file - can't go any further"); } diff --git a/src/metadata-widget.c b/src/metadata-widget.c index 38f2c4c..a1aa860 100644 --- a/src/metadata-widget.c +++ b/src/metadata-widget.c @@ -450,10 +450,35 @@ 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); + 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); + g_string_overwrite( priv->image_path, + 0, + dbusmenu_menuitem_property_get( priv->twin_item, + DBUSMENU_METADATA_MENUITEM_ARTURL )); + + // if its a remote image queue a redraw incase the download took too long + if (g_str_has_prefix (dbusmenu_menuitem_property_get (priv->twin_item, DBUSMENU_METADATA_MENUITEM_ARTURL ), + g_get_user_cache_dir())){ + gtk_widget_queue_draw(GTK_WIDGET(self)); + } } /** diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index 29dd4cf..f440c13 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -75,16 +75,16 @@ public class Mpris2Controller : GLib.Object construct{ try { var connection = DBus.Bus.get (DBus.BusType.SESSION); - this.mpris2_root = (MprisRoot) connection.get_object (root_interface.concat(".").concat(this.owner.name.down()), + this.mpris2_root = (MprisRoot) connection.get_object (root_interface.concat(".").concat(this.owner.mpris_name), "/org/mpris/MediaPlayer2", root_interface); - this.player = (MprisPlayer) connection.get_object (root_interface.concat(".").concat(this.owner.name.down()), + this.player = (MprisPlayer) connection.get_object (root_interface.concat(".").concat(this.owner.mpris_name), "/org/mpris/MediaPlayer2", root_interface.concat(".Player")); this.properties_interface = (FreeDesktopProperties) connection.get_object("org.freedesktop.Properties.PropertiesChanged", "/org/mpris/MediaPlayer2"); this.properties_interface.PropertiesChanged += property_changed_cb; - + } catch (DBus.Error e) { error("Problems connecting to the session bus - %s", e.message); } @@ -92,7 +92,7 @@ public class Mpris2Controller : GLib.Object public void property_changed_cb(string interface_source, HashTable<string, Value?> changed_properties, string[] invalid ) { - debug("properties-changed for interface %s and owner %s", interface_source, this.owner.name.down()); + debug("properties-changed for interface %s and owner %s", interface_source, this.owner.mpris_name); if(changed_properties == null || interface_source.has_prefix(this.root_interface) == false ){ warning("Property-changed hash is null or this is an interface that doesn't concerns us"); @@ -150,8 +150,8 @@ public class Mpris2Controller : GLib.Object } else{ update = determine_play_state(this.player.PlaybackStatus); - } - (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(update); + } + (this.owner.custom_items[PlayerController.widget_order.TRANSPORT] as TransportMenuitem).change_play_state(update); GLib.HashTable<string, Value?> cleaned_metadata = this.clean_metadata(); this.owner.custom_items[PlayerController.widget_order.METADATA].update(cleaned_metadata, MetadataMenuitem.attributes_format()); diff --git a/src/music-player-bridge.vala b/src/music-player-bridge.vala index 9ab1b3c..f13c2f4 100644 --- a/src/music-player-bridge.vala +++ b/src/music-player-bridge.vala @@ -50,18 +50,19 @@ public class MusicPlayerBridge : GLib.Object DesktopAppInfo info = new DesktopAppInfo.from_filename(app); if(info == null){ - warning("Could not create a desktopappinfo instance from app: %s", app); + warning("Could not create a desktopappinfo instance from app,: %s , moving on to the next client", app); continue; } GLib.AppInfo app_info = info as GLib.AppInfo; + var mpris_key = determine_key(app); PlayerController ctrl = new PlayerController(this.root_menu, - app_info, + app_info, + mpris_key, playersDB.fetch_icon_name(app), calculate_menu_position(), PlayerController.state.OFFLINE); - ctrl.app_info = app_info; - this.registered_clients.set(determine_key(app), ctrl); + this.registered_clients.set(mpris_key, ctrl); } } @@ -91,22 +92,29 @@ public class MusicPlayerBridge : GLib.Object { MusicPlayerBridge bridge = data as MusicPlayerBridge; AppInfo? app_info = create_app_info(path); - if(path.contains("/") && bridge.playersDB.already_familiar(path) == false){ - debug("About to store desktop file path: %s", path); + if ( app_info == null ){ + warning ( "Could not create app_info for path %s \n Getting out of here ", path); + return; + } + + var mpris_key = determine_key(path); + + if(bridge.playersDB.already_familiar(path) == false){ + debug("New client has registered that we have seen before: %s", path); bridge.playersDB.insert(path); - PlayerController ctrl = new PlayerController(bridge.root_menu, - app_info, - playersDB.fetch_icon_name(path), - bridge.calculate_menu_position(), - PlayerController.state.READY); - bridge.registered_clients.set(determine_key(path), ctrl); + PlayerController ctrl = new PlayerController ( bridge.root_menu, + app_info, + mpris_key, + playersDB.fetch_icon_name(path), + bridge.calculate_menu_position(), + PlayerController.state.READY ); + bridge.registered_clients.set(mpris_key, ctrl); debug("successfully created appinfo and instance from path and set it on the respective instance"); } else{ - var key = determine_key(path); - bridge.registered_clients[key].update_state(PlayerController.state.READY); - bridge.registered_clients[key].activate(); - debug("Ignoring desktop file path callback because the db cache file has it already: %s", path); + bridge.registered_clients[mpris_key].update_state(PlayerController.state.READY); + bridge.registered_clients[mpris_key].activate(); + debug("Ignoring desktop file path callback because the db cache file has it already: %s \n", path); } } diff --git a/src/player-controller.vala b/src/player-controller.vala index 06a5b4e..537eccc 100644 --- a/src/player-controller.vala +++ b/src/player-controller.vala @@ -45,6 +45,7 @@ public class PlayerController : GLib.Object private Dbusmenu.Menuitem root_menu; public string name { get; set;} + public string mpris_name { get; set;} public ArrayList<PlayerItem> custom_items; public Mpris2Controller mpris_bridge; public AppInfo? app_info { get; set;} @@ -52,15 +53,17 @@ public class PlayerController : GLib.Object public string icon_name { get; set; } public PlayerController(Dbusmenu.Menuitem root, - AppInfo app_info, + GLib.AppInfo app, + string mpris_name, string icon_name, int offset, state initial_state) { this.root_menu = root; - this.app_info = app_info; + this.app_info = app; + this.name = format_player_name(this.app_info.get_name()); + this.mpris_name = mpris_name; this.icon_name = icon_name; - this.name = format_client_name(truncate_player_name(app_info.get_name())); this.custom_items = new ArrayList<PlayerItem>(); this.current_state = initial_state; this.menu_offset = offset; @@ -162,16 +165,20 @@ public class PlayerController : GLib.Object } } - private static string format_client_name(string client_name) - { - string formatted = client_name; - if(formatted.length > 1){ - formatted = client_name.up(1).concat(client_name.slice(1, client_name.length)); - debug("PlayerController->format_client_name - : %s", formatted); + private static string format_player_name(owned string app_info_name) + { + string result = app_info_name.down().strip(); + var tokens = result.split(" "); + if(tokens.length > 1){ + result = tokens[0]; + } + if(result.length > 1){ + result = result.up(1).concat(result.slice(1, result.length)); + debug("PlayerController->format_player_name - : %s", result); } - return formatted; - } - + return result; + } + // Temporarily we will need to handle to different mpris implemenations // Do it for now - a couple of weeks should see this messy carry on out of // the codebase. @@ -180,26 +187,12 @@ public class PlayerController : GLib.Object if(this.mpris_bridge.connected() == true){ this.update_state(state.CONNECTED); TitleMenuitem title = this.custom_items[widget_order.TITLE] as TitleMenuitem; - title.toggle_active_triangle(true); - TransportMenuitem transport = this.custom_items[widget_order.TRANSPORT] as TransportMenuitem; - transport.change_play_state(TransportMenuitem.state.PAUSED); + title.toggle_active_triangle(true); + this.mpris_bridge.initial_update(); } else{ this.update_state(state.DISCONNECTED); } } - - private static string truncate_player_name(owned string app_info_name) - { - string result = app_info_name.down().strip(); - - var tokens = result.split(" "); - - if(tokens.length > 1){ - result = tokens[0]; - } - debug("truncate player name %s", result); - return result; - } }
\ No newline at end of file diff --git a/src/pulse-manager.c b/src/pulse-manager.c index 20238e7..14633fe 100644 --- a/src/pulse-manager.c +++ b/src/pulse-manager.c @@ -20,7 +20,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include <pulse/glib-mainloop.h> #include <pulse/error.h> #include <pulse/gccmacro.h> @@ -28,14 +27,18 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include "pulse-manager.h" #include "dbus-menu-manager.h" +#define RECONNECT_DELAY 5 + static GHashTable *sink_hash = NULL; static SoundServiceDbus *dbus_service = NULL; static gint DEFAULT_SINK_INDEX = -1; static gboolean pa_server_available = FALSE; -// PA related +static gint reconnect_idle_id = 0; static pa_context *pulse_context = NULL; static pa_glib_mainloop *pa_main_loop = NULL; + static void context_state_callback(pa_context *c, void *userdata); +static gboolean reconnect_to_pulse(); static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink_info, int eol, void *userdata); static void context_success_callback(pa_context *c, int success, void *userdata); static void pulse_sink_input_info_callback(pa_context *c, const pa_sink_input_info *info, int eol, void *userdata); @@ -61,15 +64,19 @@ void establish_pulse_activities(SoundServiceDbus *service) dbus_service = service; pa_main_loop = pa_glib_mainloop_new(g_main_context_default()); g_assert(pa_main_loop); - pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), "ayatana.indicator.sound"); + pulse_context = pa_context_new(pa_glib_mainloop_get_api(pa_main_loop), + "ayatana.indicator.sound"); g_assert(pulse_context); - sink_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, destroy_sink_info); + sink_hash = g_hash_table_new_full(g_direct_hash, + g_direct_equal, + NULL, + destroy_sink_info); // Establish event callback registration - pa_context_set_state_callback(pulse_context, context_state_callback, NULL); - dbus_menu_manager_update_pa_state(FALSE, FALSE, FALSE, 0); - pa_context_connect(pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); + pa_context_set_state_callback (pulse_context, context_state_callback, NULL); + dbus_menu_manager_update_pa_state (FALSE, FALSE, FALSE, 0); + pa_context_connect (pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); } /** @@ -81,6 +88,41 @@ pa_context* get_context() return pulse_context; } +static gboolean +reconnect_to_pulse() +{ + g_debug("Attempt to reconnect to pulse"); + // reset + if (pulse_context != NULL) { + pa_context_unref(pulse_context); + pulse_context = NULL; + } + + if (sink_hash != NULL) { + g_hash_table_destroy(sink_hash); + sink_hash = NULL; + } + pulse_context = pa_context_new( pa_glib_mainloop_get_api( pa_main_loop ), + "ayatana.indicator.sound" ); + g_assert(pulse_context); + sink_hash = g_hash_table_new_full( g_direct_hash, g_direct_equal, + NULL, + destroy_sink_info ); + // Establish event callback registration + pa_context_set_state_callback (pulse_context, context_state_callback, NULL); + int result = pa_context_connect (pulse_context, NULL, PA_CONTEXT_NOFAIL, NULL); + + if (result < 0) { + g_warning ("Failed to connect context: %s", + pa_strerror (pa_context_errno (pulse_context))); + } + // we always want to cancel any continious callbacks with the existing timeout + // if the connection failed the new context created above will catch any updates + // to do with the state of pulse and thus take care of business. + reconnect_idle_id = 0; + return FALSE; +} + /** close_pulse_activites() Gracefully close our connection with the Pulse async library. @@ -294,9 +336,6 @@ static void pulse_sink_info_callback(pa_context *c, const pa_sink_info *sink, in static void pulse_default_sink_info_callback(pa_context *c, const pa_sink_info *info, int eol, void *userdata) { if (eol > 0) { - if (pa_context_errno(c) == PA_ERR_NOENTITY) - return; - g_warning("Default Sink info callback failure"); return; } else { DEFAULT_SINK_INDEX = info->index; @@ -416,7 +455,9 @@ static gboolean has_volume_changed(const pa_sink_info* new_sink, sink_info* cach } -static void pulse_server_info_callback(pa_context *c, const pa_server_info *info, void *userdata) +static void pulse_server_info_callback(pa_context *c, + const pa_server_info *info, + void *userdata) { /* g_debug("server info callback");*/ pa_operation *operation; @@ -428,7 +469,10 @@ static void pulse_server_info_callback(pa_context *c, const pa_server_info *info } pa_server_available = TRUE; if (info->default_sink_name != NULL) { - if (!(operation = pa_context_get_sink_info_by_name(c, info->default_sink_name, pulse_default_sink_info_callback, userdata))) { + if (!(operation = pa_context_get_sink_info_by_name(c, + info->default_sink_name, + pulse_default_sink_info_callback, + userdata))) { g_warning("pa_context_get_sink_info_by_name() failed"); } else { pa_operation_unref(operation); @@ -502,16 +546,26 @@ static void context_state_callback(pa_context *c, void *userdata) /* g_debug("context setting name");*/ break; case PA_CONTEXT_FAILED: - g_warning("FAILED to retrieve context - Is PulseAudio Daemon running ?"); + g_warning("PA_CONTEXT_FAILED - Is PulseAudio Daemon running ?"); pa_server_available = FALSE; + dbus_menu_manager_update_pa_state(TRUE, + pa_server_available, + default_sink_is_muted(), + get_default_sink_volume()); + + if (reconnect_idle_id == 0){ + reconnect_idle_id = g_timeout_add_seconds (RECONNECT_DELAY, + reconnect_to_pulse, + NULL); + } break; case PA_CONTEXT_TERMINATED: /* g_debug("context terminated");*/ break; case PA_CONTEXT_READY: - g_debug("PA daemon is ready"); + g_debug("PA_CONTEXT_READY"); pa_operation *o; - + pa_context_set_subscribe_callback(c, subscribed_events_callback, userdata); if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) diff --git a/src/title-widget.c b/src/title-widget.c index 344d0a7..5bc2a93 100644 --- a/src/title-widget.c +++ b/src/title-widget.c @@ -83,8 +83,8 @@ title_widget_set_icon(TitleWidget *self) { TitleWidgetPrivate *priv = TITLE_WIDGET_GET_PRIVATE(self); - gchar* icon_name = dbusmenu_menuitem_property_get(priv->twin_item, - DBUSMENU_TITLE_MENUITEM_ICON); + gchar* icon_name = g_strdup(dbusmenu_menuitem_property_get(priv->twin_item, + DBUSMENU_TITLE_MENUITEM_ICON)); gint padding = 0; gtk_widget_style_get(GTK_WIDGET(self), "horizontal-padding", &padding, NULL); @@ -101,6 +101,7 @@ title_widget_set_icon(TitleWidget *self) gtk_misc_set_alignment(GTK_MISC(icon), 0.5 /* right aligned */, 0); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(self), GTK_WIDGET(icon)); gtk_widget_show(icon); + g_free(icon_name); } static void diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala index 7faadb5..36429e8 100644 --- a/src/transport-menu-item.vala +++ b/src/transport-menu-item.vala @@ -42,6 +42,8 @@ public class TransportMenuitem : PlayerItem public void change_play_state(state update) { + debug("UPDATING THE TRANSPORT DBUSMENUITEM PLAY STATE WITH VALUE %i", + (int)update); this.property_set_int(MENUITEM_PLAY_STATE, update); } @@ -60,5 +62,4 @@ public class TransportMenuitem : PlayerItem return attrs; } - }
\ No newline at end of file diff --git a/src/transport-widget.c b/src/transport-widget.c index ad044da..26b7a98 100644 --- a/src/transport-widget.c +++ b/src/transport-widget.c @@ -105,7 +105,7 @@ static void transport_widget_property_update ( DbusmenuMenuitem* item, gpointer userdata ); static void transport_widget_menu_hidden ( GtkWidget *menu, TransportWidget *transport); -static void transport_widget_notify ( TransportWidget *item, +static void transport_widget_notify ( GObject *item, GParamSpec *pspec, gpointer user_data ); static TransportWidgetEvent transport_widget_determine_button_event ( TransportWidget* button, @@ -174,7 +174,7 @@ transport_widget_init (TransportWidget *self) gtk_widget_set_size_request(GTK_WIDGET(self), 200, 43); g_signal_connect (G_OBJECT(self), - "notify::parent", + "notify", G_CALLBACK (transport_widget_notify), NULL); } @@ -203,7 +203,7 @@ transport_widget_expose (GtkWidget *button, GdkEventExpose *event) event->area.width, event->area.height); cairo_clip(cr); - draw (button, cr); + draw (button, cr); cairo_destroy (cr); return FALSE; @@ -220,16 +220,16 @@ transport_widget_toggle_play_pause(TransportWidget* button, } static void -transport_widget_notify (TransportWidget *item, - GParamSpec *pspec, - gpointer user_data) +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); + item ); } } } @@ -238,7 +238,6 @@ static void transport_widget_menu_hidden ( GtkWidget *menu, TransportWidget *transport) { - //g_debug("Transport Widget's menu hidden method called"); g_return_if_fail(IS_TRANSPORT_WIDGET(transport)); transport_widget_react_to_button_release(transport, TRANSPORT_NADA); } @@ -1262,6 +1261,11 @@ transport_widget_set_twin_item(TransportWidget* 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, + (TransportWidgetState)initial_state); } /** @@ -1280,8 +1284,7 @@ transport_widget_property_update(DbusmenuMenuitem* item, gchar* property, { int update_value = g_value_get_int(value); //g_debug("transport_widget_update_state - with value %i", update_value); - transport_widget_toggle_play_pause(bar, - (TransportWidgetState)update_value); + transport_widget_toggle_play_pause(bar, (TransportWidgetState)update_value); } } |