diff options
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/freedesktop-interfaces.vala | 47 | ||||
-rw-r--r-- | src/mpris2-controller.vala | 55 | ||||
-rw-r--r-- | src/mpris2-interfaces.vala | 4 | ||||
-rw-r--r-- | src/mpris2-watcher.vala | 125 | ||||
-rw-r--r-- | src/music-player-bridge.vala | 17 | ||||
-rw-r--r-- | src/player-controller.vala | 19 |
8 files changed, 190 insertions, 84 deletions
diff --git a/configure.ac b/configure.ac index c9227cd..928ffff 100644 --- a/configure.ac +++ b/configure.ac @@ -55,7 +55,8 @@ PKG_CHECK_MODULES(SOUNDSERVICE, dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION indicator >= $INDICATOR_REQUIRED_VERSION indicate >= $INDICATE_REQUIRED_VERSION gee-1.0 - gio-unix-2.0) + gio-unix-2.0 + libxml-2.0) AC_SUBST(SOUNDSERVICE_CFLAGS) AC_SUBST(SOUNDERVICE_LIBS) diff --git a/src/Makefile.am b/src/Makefile.am index 82830e1..7fe7e0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,6 +66,7 @@ music_bridge_VALASOURCES = \ player-item.vala \ settings-manager.vala \ playlists-menu-item.vala \ + freedesktop-interfaces.vala \ fetch-file.vala @@ -81,7 +82,8 @@ music_bridge_VALAFLAGS = \ --pkg common-defs \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ - --pkg gdk-pixbuf-2.0 + --pkg gdk-pixbuf-2.0 \ + --pkg libxml-2.0 $(MAINTAINER_VALAFLAGS) diff --git a/src/freedesktop-interfaces.vala b/src/freedesktop-interfaces.vala new file mode 100644 index 0000000..4d75044 --- /dev/null +++ b/src/freedesktop-interfaces.vala @@ -0,0 +1,47 @@ +/* +Copyright 2010 Canonical Ltd. + +Authors: + Conor Curran <conor.curran@canonical.com> + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +[DBus (name = "org.freedesktop.DBus")] +public interface FreeDesktopObject: Object { + public abstract async string[] list_names() throws IOError; + public abstract signal void name_owner_changed ( string name, + string old_owner, + string new_owner ); +} + +[DBus (name = "org.freedesktop.DBus.Introspectable")] +public interface FreeDesktopIntrospectable: Object { + public abstract string Introspect() throws IOError; +} + +[DBus (name = "org.freedesktop.DBus.Properties")] +public interface FreeDesktopProperties : Object{ + public signal void PropertiesChanged (string source, HashTable<string, Variant?> changed_properties, + string[] invalid ); +} + +public errordomain XmlError { + FILE_NOT_FOUND, + XML_DOCUMENT_EMPTY +} + +const string FREEDESKTOP_SERVICE = "org.freedesktop.DBus"; +const string FREEDESKTOP_OBJECT = "/org/freedesktop/DBus"; + + diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index 5f98541..fc61c12 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -18,12 +18,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>. */ using Dbusmenu; -[DBus (name = "org.freedesktop.DBus.Properties")] -public interface FreeDesktopProperties : Object{ - public signal void PropertiesChanged (string source, HashTable<string, Variant?> changed_properties, - string[] invalid ); -} - /* This class will entirely replace mpris-controller.vala hence why there is no point in trying to get encorporate both into the same object model. @@ -50,10 +44,11 @@ public class Mpris2Controller : GLib.Object this.player = Bus.get_proxy_sync ( BusType.SESSION, this.owner.dbus_name, "/org/mpris/MediaPlayer2" ); - this.playlists = Bus.get_proxy_sync ( BusType.SESSION, - this.owner.dbus_name, - "/org/mpris/MediaPlayer2" ); - + if ( this.owner.use_playlists == true ){ + this.playlists = 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" ); @@ -70,7 +65,7 @@ public class Mpris2Controller : GLib.Object { debug("properties-changed for interface %s and owner %s", interface_source, this.owner.dbus_name); if ( changed_properties == null || - interface_source.has_prefix ( Mpris2Watcher.MPRIS_PREFIX ) == false ){ + interface_source.has_prefix ( MPRIS_PREFIX ) == false ){ warning("Property-changed hash is null or this is an interface that doesn't concerns us"); return; } @@ -95,19 +90,10 @@ public class Mpris2Controller : GLib.Object metadata.populated(MetadataMenuitem.attributes_format())); } Variant? playlist_v = changed_properties.lookup("ActivePlaylist"); - if ( playlist_v != null ){ + if ( playlist_v != null && this.owner.use_playlists == true ){ this.fetch_active_playlist(); - //Timeout.add ( 200, fetch_active_playlist ); } } - - public bool playlists_support_exist() - { - if (this.playlists == null) return false; - uint32? count = this.playlists.PlaylistCount; - if ( count == null || count <= 0 ) return false; - return true; - } private bool ensure_correct_playback_status(){ debug("TEST playback status = %s", this.player.PlaybackStatus); @@ -154,8 +140,11 @@ public class Mpris2Controller : GLib.Object GLib.HashTable<string, Value?>? cleaned_metadata = this.clean_metadata(); this.owner.custom_items[PlayerController.widget_order.METADATA].update(cleaned_metadata, MetadataMenuitem.attributes_format()); - this.fetch_playlists(); - this.fetch_active_playlist(); + + if ( this.owner.use_playlists == true ){ + this.fetch_playlists(); + this.fetch_active_playlist(); + } } public void transport_update(TransportMenuitem.action command) @@ -174,8 +163,6 @@ public class Mpris2Controller : GLib.Object public void fetch_playlists() { - if (this.playlists == null) return; - PlaylistDetails[] current_playlists = this.playlists.GetPlaylists(0, 10, "Alphabetical", @@ -185,13 +172,17 @@ public class Mpris2Controller : GLib.Object PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; playlists_item.update(current_playlists); } + else{ + warning(" Playlists are on but its returning no current_playlists" ); + this.owner.use_playlists = false; + } return; } private void fetch_active_playlist() - { - if (this.playlists == null && this.playlists.ActivePlaylist.valid == false){ - warning("Playlists object is null or we don't have an active playlist"); + { + if (this.playlists.ActivePlaylist.valid == false){ + debug("We don't have an active playlist"); } PlaylistsMenuitem playlists_item = this.owner.custom_items[PlayerController.widget_order.PLAYLISTS] as PlaylistsMenuitem; playlists_item.update_active_playlist ( this.playlists.ActivePlaylist.details ); @@ -202,7 +193,7 @@ public class Mpris2Controller : GLib.Object { return (this.player != null && this.mpris2_root != null); } - + public void expose() { if(this.connected() == true){ @@ -212,15 +203,11 @@ public class Mpris2Controller : GLib.Object public void activate_playlist (ObjectPath path) { - if(this.playlists == null){ - warning("playlists mpris instance is null !"); - return; - } try{ this.playlists.ActivatePlaylist.begin(path); } catch(IOError e){ debug("Could not activate playlist %s because %s", (string)path, e.message); } - } + } }
\ No newline at end of file diff --git a/src/mpris2-interfaces.vala b/src/mpris2-interfaces.vala index 88610e6..9d0b8c9 100644 --- a/src/mpris2-interfaces.vala +++ b/src/mpris2-interfaces.vala @@ -16,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +const string MPRIS_PREFIX = "org.mpris.MediaPlayer2."; +const string MPRIS_MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2"; [DBus (name = "org.mpris.MediaPlayer2")] public interface MprisRoot : Object { @@ -24,7 +26,7 @@ public interface MprisRoot : Object { public abstract bool CanQuit{owned get; set;} public abstract bool CanRaise{owned get; set;} public abstract string Identity{owned get; set;} - public abstract string DesktopEntry{owned get; set;} + public abstract string DesktopEntry{owned get; set;} // methods public abstract async void Quit() throws IOError; public abstract async void Raise() throws IOError; diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 7814975..c20b04b 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -17,30 +17,21 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -[DBus (name = "org.freedesktop.DBus")] -public interface FreeDesktopObject: Object { - public abstract async string[] list_names() throws IOError; - public abstract signal void name_owner_changed ( string name, - string old_owner, - string new_owner ); -} +using Xml; public class Mpris2Watcher : GLib.Object { - private const string FREEDESKTOP_SERVICE = "org.freedesktop.DBus"; - private const string FREEDESKTOP_OBJECT = "/org/freedesktop/DBus"; - public const string MPRIS_PREFIX = "org.mpris.MediaPlayer2."; - private const string MPRIS_MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2"; - FreeDesktopObject fdesktop_obj; - public signal void client_appeared ( string desktop_file_name, string dbus_name ); + public signal void client_appeared ( string desktop_file_name, + string dbus_name, + bool use_playlists ); public signal void client_disappeared ( string dbus_name ); public Mpris2Watcher () { } - + construct { try { @@ -57,6 +48,29 @@ 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. + private async void check_for_active_clients() + { + string[] interfaces; + try{ + interfaces = yield this.fdesktop_obj.list_names(); + } + catch ( IOError e) { + warning( "Mpris2watcher could fetch active interfaces at startup: %s", + e.message ); + return; + } + foreach (var address in interfaces) { + if (address.has_prefix (MPRIS_PREFIX)){ + 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); + } + } + } + private void name_changes_detected ( FreeDesktopObject dbus_obj, string name, string previous_owner, @@ -65,24 +79,25 @@ public class Mpris2Watcher : GLib.Object MprisRoot? mpris2_root = this.create_mpris_root(name); if (mpris2_root == null) return; - + if (previous_owner != "" && current_owner == "") { debug ("Client '%s' gone down", name); client_disappeared (name); } else if (previous_owner == "" && current_owner != "") { debug ("Client '%s' has appeared", name); - client_appeared (mpris2_root.DesktopEntry, name); + bool use_playlists = this.supports_playlists ( name ); + client_appeared (mpris2_root.DesktopEntry, name, use_playlists); } } - private MprisRoot? create_mpris_root(string name){ + private MprisRoot? create_mpris_root ( string name ){ MprisRoot mpris2_root = null; if ( name.has_prefix (MPRIS_PREFIX) ){ try { mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, - name, - MPRIS_MEDIA_PLAYER_PATH ); + name, + MPRIS_MEDIA_PLAYER_PATH ); } catch (IOError e){ warning( "Mpris2watcher could not create a root interface: %s", @@ -91,26 +106,66 @@ public class Mpris2Watcher : GLib.Object } return mpris2_root; } - - // At startup check to see if there are clients up that we are interested in - // More relevant for development and daemon's like mpd. - private async void check_for_active_clients() + + private bool supports_playlists ( string name ) { - string[] interfaces; - try{ - interfaces = yield this.fdesktop_obj.list_names(); + FreeDesktopIntrospectable introspectable; + + try { + /* The dbusproxy flag parameter is needed to ensure Banshee does not + blow up. I suspect the issue is that if you + try to instantiate a dbus object which does not have any properties + associated with it, gdbus will attempt to fetch the properties (this is + in the documentation) but the banshee mpris dbus object more than likely + causes a crash because it doesn't check for the presence of properties + before attempting to access them. + */ + introspectable = Bus.get_proxy_sync ( BusType.SESSION, + name, + MPRIS_MEDIA_PLAYER_PATH, + GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES); + var results = introspectable.Introspect(); + return this.parse_interfaces (results); } - catch ( IOError e) { - warning( "Mpris2watcher could fetch active interfaces at startup: %s", + catch (IOError e){ + warning( "Could not create an introspectable object: %s", e.message ); - return; } - foreach (var address in interfaces) { - if (address.has_prefix (MPRIS_PREFIX)){ - MprisRoot? mpris2_root = this.create_mpris_root(address); - if (mpris2_root == null) return; - client_appeared (mpris2_root.DesktopEntry, address); + return false; + } + + private bool parse_interfaces( string interface_info ) + { + //parse the document from path + bool result = false; + Xml.Doc* xml_doc = Parser.parse_doc (interface_info); + if (xml_doc == null) { + warning ("Mpris2Watcher - parse-interfaces - failed to instantiate xml doc"); + return false; + } + //get the root node. notice the dereferencing operator -> instead of . + Xml.Node* root_node = xml_doc->get_root_element (); + if (root_node == null) { + //free the document manually before throwing because the garbage collector can't work on pointers + delete xml_doc; + warning ("Mpris2Watcher - the interface info xml is empty"); + return false; + } + + //let's parse those nodes + for (Xml.Node* iter = root_node->children; iter != null; iter = iter->next) { + //spaces btw. tags are also nodes, discard them + if (iter->type != ElementType.ELEMENT_NODE){ + continue; + } + Xml.Attr* attributes = iter->properties; //get the node's name + string interface_name = attributes->children->content; + debug ( "this dbus object has interface %s ", interface_name ); + if ( interface_name == MPRIS_PREFIX.concat("Playlists")){ + result = true; } } - } + delete xml_doc; + return result; + } }
\ No newline at end of file diff --git a/src/music-player-bridge.vala b/src/music-player-bridge.vala index c7391cf..7587684 100644 --- a/src/music-player-bridge.vala +++ b/src/music-player-bridge.vala @@ -59,8 +59,9 @@ public class MusicPlayerBridge : GLib.Object null, this.fetch_icon_name(desktop), calculate_menu_position(), + null, PlayerController.state.OFFLINE ); - this.registered_clients.set(mpris_key, ctrl); + this.registered_clients.set(mpris_key, ctrl); } } @@ -74,7 +75,9 @@ public class MusicPlayerBridge : GLib.Object } } - public void client_has_become_available ( string desktop, string dbus_name ) + 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", @@ -82,8 +85,9 @@ public class MusicPlayerBridge : GLib.Object return; } if (desktop in this.settings_manager.fetch_blacklist()) { - debug ("Client %s attempting to register but it has been blacklisted", + debug ("Client %s attempting to register but I'm afraid it is blacklisted", desktop); + return; } debug ( "client_has_become_available %s", desktop ); @@ -103,6 +107,7 @@ public class MusicPlayerBridge : GLib.Object dbus_name, this.fetch_icon_name(desktop), this.calculate_menu_position(), + use_playlists, PlayerController.state.READY ); this.registered_clients.set ( mpris_key, ctrl ); debug ( "Have not seen this %s before, new controller created.", desktop ); @@ -110,6 +115,7 @@ public class MusicPlayerBridge : GLib.Object 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 \n", dbus_name ); @@ -118,7 +124,8 @@ public class MusicPlayerBridge : GLib.Object public void client_has_vanished ( string mpris_root_interface ) { - debug("MusicPlayerBridge -> on_server_removed with value %s", mpris_root_interface); + debug("MusicPlayerBridge -> client with dbus interface %s has vanished", + mpris_root_interface ); if (root_menu != null){ debug("attempt to remove %s", mpris_root_interface); var mpris_key = determine_key ( mpris_root_interface ); @@ -128,7 +135,7 @@ public class MusicPlayerBridge : GLib.Object } } } - + public void set_root_menu_item ( Dbusmenu.Menuitem menu ) { this.root_menu = menu; diff --git a/src/player-controller.vala b/src/player-controller.vala index 3922ecf..adefb65 100644 --- a/src/player-controller.vala +++ b/src/player-controller.vala @@ -45,19 +45,22 @@ public class PlayerController : GLib.Object private Dbusmenu.Menuitem root_menu; public string dbus_name { get; set;} - public ArrayList<PlayerItem> custom_items; + public ArrayList<PlayerItem> 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 PlayerController(Dbusmenu.Menuitem root, GLib.AppInfo app, string? dbus_name, string icon_name, int offset, + bool? use_playlists, state initial_state) { + this.use_playlists = use_playlists; this.root_menu = root; this.app_info = app; this.dbus_name = dbus_name; @@ -67,7 +70,7 @@ public class PlayerController : GLib.Object this.menu_offset = offset; this.construct_widgets(); this.establish_mpris_connection(); - this.update_layout(); + this.update_layout(); } public void update_state(state new_state) @@ -81,7 +84,7 @@ public class PlayerController : GLib.Object public void activate( string dbus_name ) { this.dbus_name = dbus_name; - this.establish_mpris_connection(); + this.establish_mpris_connection(); } /* @@ -109,6 +112,9 @@ public class PlayerController : GLib.Object 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(); } @@ -148,7 +154,7 @@ public class PlayerController : GLib.Object this.custom_items[widget_order.TRANSPORT].property_set_bool(MENUITEM_PROP_VISIBLE, true); playlists_menuitem.root_item.property_set_bool ( MENUITEM_PROP_VISIBLE, - this.mpris_bridge.playlists_support_exist() ); + this.use_playlists ); } private void construct_widgets() @@ -181,9 +187,8 @@ public class PlayerController : GLib.Object root_menu.child_add_position(playlists_menuitem.root_item, this.menu_offset + this.custom_items.index_of(item)); } } - } + } - private static string format_player_name(owned string app_info_name) { string result = app_info_name.down().strip(); |