2
Copyright 2010 Canonical Ltd.
5
Conor Curran <conor.curran@canonical.com>
7
This program is free software: you can redistribute it and/or modify it
8
under the terms of the GNU General Public License version 3, as published
9
by the Free Software Foundation.
11
This program is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranties of
13
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
PURPOSE. See the GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License along
17
with this program. If not, see <http://www.gnu.org/licenses/>.
22
public class Mpris2Watcher : GLib.Object
24
DBusConnection session_bus;
26
public signal void client_appeared ( string desktop_file_name,
29
public signal void client_disappeared ( string dbus_name );
31
public Mpris2Watcher ()
37
const string match_rule = "type='signal'," +
38
"sender='org.freedesktop.DBus'," +
39
"interface='org.freedesktop.DBus'," +
40
"member='NameOwnerChanged'," +
41
"path='/org/freedesktop/DBus'," +
42
"arg0namespace='org.mpris.MediaPlayer2'";
44
this.session_bus = Bus.get_sync (BusType.SESSION);
46
this.session_bus.call_sync ("org.freedesktop.DBus",
48
"org.freedesktop.DBus",
50
new Variant ("(s)", match_rule),
55
this.session_bus.signal_subscribe ("org.freedesktop.DBus",
56
"org.freedesktop.DBus",
58
"/org/freedesktop/DBus",
60
DBusSignalFlags.NO_MATCH_RULE,
61
this.name_owner_changed);
63
this.check_for_active_clients.begin();
65
catch (GLib.Error e) {
66
warning ("unable to set up name watch for mrpis clients: %s", e.message);
70
// At startup check to see if there are clients up that we are interested in
71
// More relevant for development and daemon's like mpd.
72
async void check_for_active_clients()
77
interfaces = yield this.session_bus.call ("org.freedesktop.DBus",
79
"org.freedesktop.DBus",
82
new VariantType ("(as)"),
86
catch (GLib.Error e) {
87
warning ("unable to search for existing mpris clients: %s ", e.message);
91
foreach (var val in interfaces.get_child_value (0)) {
92
var address = (string) val;
93
if (address.has_prefix (MPRIS_PREFIX)){
94
MprisRoot? mpris2_root = this.create_mpris_root(address);
95
if (mpris2_root == null) return;
96
bool use_playlists = this.supports_playlists ( address );
97
client_appeared (mpris2_root.DesktopEntry + ".desktop", address, use_playlists);
102
void name_owner_changed (DBusConnection con, string sender, string object_path,
103
string interface_name, string signal_name, Variant parameters)
105
string name, previous_owner, current_owner;
107
parameters.get ("(sss)", out name, out previous_owner, out current_owner);
109
MprisRoot? mpris2_root = this.create_mpris_root (name);
110
if (mpris2_root == null) return;
112
if (previous_owner != "" && current_owner == "") {
113
debug ("Client '%s' gone down", name);
114
client_disappeared (name);
116
else if (previous_owner == "" && current_owner != "") {
117
debug ("Client '%s' has appeared", name);
118
bool use_playlists = this.supports_playlists ( name );
119
client_appeared (mpris2_root.DesktopEntry + ".desktop", name, use_playlists);
123
private MprisRoot? create_mpris_root ( string name ){
124
MprisRoot mpris2_root = null;
125
if ( name.has_prefix (MPRIS_PREFIX) ){
127
mpris2_root = Bus.get_proxy_sync ( BusType.SESSION,
129
MPRIS_MEDIA_PLAYER_PATH );
132
warning( "Mpris2watcher could not create a root interface: %s",
139
private bool supports_playlists ( string name )
141
FreeDesktopIntrospectable introspectable;
144
/* The dbusproxy flag parameter is needed to ensure Banshee does not
145
blow up. I suspect the issue is that if you
146
try to instantiate a dbus object which does not have any properties
147
associated with it, gdbus will attempt to fetch the properties (this is
148
in the documentation) but the banshee mpris dbus object more than likely
149
causes a crash because it doesn't check for the presence of properties
150
before attempting to access them.
152
introspectable = Bus.get_proxy_sync ( BusType.SESSION,
154
MPRIS_MEDIA_PLAYER_PATH,
155
GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
156
var results = introspectable.Introspect();
157
return this.parse_interfaces (results);
160
warning( "Could not create an introspectable object: %s",
166
private bool parse_interfaces( string interface_info )
168
//parse the document from path
170
Xml.Doc* xml_doc = Parser.parse_doc (interface_info);
171
if (xml_doc == null) {
172
warning ("Mpris2Watcher - parse-interfaces - failed to instantiate xml doc");
175
//get the root node. notice the dereferencing operator -> instead of .
176
Xml.Node* root_node = xml_doc->get_root_element ();
177
if (root_node == null) {
178
//free the document manually before throwing because the garbage collector can't work on pointers
180
warning ("Mpris2Watcher - the interface info xml is empty");
184
//let's parse those nodes
185
for (Xml.Node* iter = root_node->children; iter != null; iter = iter->next) {
186
//spaces btw. tags are also nodes, discard them
187
if (iter->type != ElementType.ELEMENT_NODE){
190
Xml.Attr* attributes = iter->properties; //get the node's name
191
string interface_name = attributes->children->content;
192
debug ( "this dbus object has interface %s ", interface_name );
193
if ( interface_name == MPRIS_PREFIX.concat("Playlists")){