Merge lp:~larsu/indicator-sound/use-bus-watch-namespace into lp:indicator-sound/13.10

Proposed by Lars Karlitski
Status: Merged
Approved by: Charles Kerr
Approved revision: 368
Merged at revision: 371
Proposed branch: lp:~larsu/indicator-sound/use-bus-watch-namespace
Merge into: lp:indicator-sound/13.10
Diff against target: 756 lines (+464/-218)
7 files modified
src/CMakeLists.txt (+3/-8)
src/Makefile.am.THIS (+39/-0)
src/bus-watch-namespace.c (+349/-0)
src/bus-watch-namespace.h (+34/-0)
src/media-player-list.vala (+14/-10)
src/mpris2-watcher.vala (+0/-200)
vapi/bus-watcher.vapi (+25/-0)
To merge this branch: bzr merge lp:~larsu/indicator-sound/use-bus-watch-namespace
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+178246@code.launchpad.net

Description of the change

Use bus_watch_namespace() for more robust monitoring of mpris players appearing or disappearing on the bus.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

bus-watch-namespace is a nice piece of work. I only have three issues -- a bug, a typo, and a question:

 * watcher->name_space is leaked

 * trivial style: ";;" used after g_cancellable_new()

 * the API contract is a little odd in that user_data can be
   destroyed before bus_unwatch_namespace() is called by the client.
   This isn't an issue in i-sound, which doesn't use the argument,
   but was it intentional in the contract?

The changes to media-player-list.vala are straightforward and fine.

review: Needs Fixing
366. By Lars Karlitski

bus-watch-namespace: free name_space

367. By Lars Karlitski

bus-watch-namespace: remove stray semicolon

Revision history for this message
Lars Karlitski (larsu) wrote :

> bus-watch-namespace is a nice piece of work. I only have three issues -- a
> bug, a typo, and a question:
>
> * watcher->name_space is leaked
>
> * trivial style: ";;" used after g_cancellable_new()

Nice catches, thanks! Fixed in revisions 366 and 367.

> * the API contract is a little odd in that user_data can be
> destroyed before bus_unwatch_namespace() is called by the client.
> This isn't an issue in i-sound, which doesn't use the argument,
> but was it intentional in the contract?

Yes, it is intentional. The contract is basically: as long as we call your callbacks, your user data will be alive. Since in some cases (e.g. no bus connection) there's no way those callbacks ever get called, I just free the user data directly.

I should probably document this better, thanks for raising it.

> The changes to media-player-list.vala are straightforward and fine.

:)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
368. By Lars Karlitski

Merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2013-08-16 03:58:55 +0000
3+++ src/CMakeLists.txt 2013-08-26 15:12:41 +0000
4@@ -20,6 +20,7 @@
5 --vapidir=${CMAKE_SOURCE_DIR}/vapi/
6 --vapidir=.
7 --target-glib=2.36
8+ --pkg=bus-watcher
9 )
10
11 vala_add(indicator-sound-service
12@@ -47,19 +48,12 @@
13 media-player-list.vala
14 DEPENDS
15 media-player
16- mpris2-watcher
17+ mpris2-interfaces
18 )
19 vala_add(indicator-sound-service
20 mpris2-interfaces.vala
21 )
22 vala_add(indicator-sound-service
23- mpris2-watcher.vala
24- DEPENDS
25- media-player
26- mpris2-interfaces
27- freedesktop-interfaces
28-)
29-vala_add(indicator-sound-service
30 freedesktop-interfaces.vala
31 )
32 vala_add(indicator-sound-service
33@@ -89,6 +83,7 @@
34 INDICATOR_SOUND_SOURCES
35 ${project_VALA_SOURCES}
36 ${project_VALA_C}
37+ bus-watch-namespace.c
38 ${SYMBOLS_PATH}
39 )
40
41
42=== added file 'src/Makefile.am.THIS'
43--- src/Makefile.am.THIS 1970-01-01 00:00:00 +0000
44+++ src/Makefile.am.THIS 2013-08-26 15:12:41 +0000
45@@ -0,0 +1,39 @@
46+pkglibexec_PROGRAMS = indicator-sound-service
47+
48+indicator_sound_service_SOURCES = \
49+ service.vala \
50+ main.vala \
51+ volume-control.vala \
52+ media-player.vala \
53+ media-player-list.vala \
54+ mpris2-interfaces.vala \
55+ freedesktop-interfaces.vala \
56+ sound-menu.vala \
57+ bus-watch-namespace.c \
58+ bus-watch-namespace.h
59+
60+indicator_sound_service_VALAFLAGS = \
61+ --ccode \
62+ --vapidir=$(top_srcdir)/vapi/ \
63+ --vapidir=./ \
64+ --thread \
65+ --pkg config \
66+ --pkg gio-2.0 \
67+ --pkg gio-unix-2.0 \
68+ --pkg libxml-2.0 \
69+ --pkg libpulse \
70+ --pkg libpulse-mainloop-glib \
71+ --pkg bus-watcher \
72+ --target-glib=2.36
73+
74+# -w to disable warnings for vala-generated code
75+indicator_sound_service_CFLAGS = $(PULSEAUDIO_CFLAGS) \
76+ $(SOUNDSERVICE_CFLAGS) \
77+ $(GCONF_CFLAGS) \
78+ $(COVERAGE_CFLAGS) \
79+ -DLIBEXECDIR=\"$(libexecdir)\" \
80+ -w \
81+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
82+
83+indicator_sound_service_LDADD = $(PULSEAUDIO_LIBS) $(SOUNDSERVICE_LIBS) $(GCONF_LIBS)
84+indicator_sound_service_LDFLAGS = $(COVERAGE_LDFLAGS)
85
86=== added file 'src/bus-watch-namespace.c'
87--- src/bus-watch-namespace.c 1970-01-01 00:00:00 +0000
88+++ src/bus-watch-namespace.c 2013-08-26 15:12:41 +0000
89@@ -0,0 +1,349 @@
90+/*
91+ * Copyright 2013 Canonical Ltd.
92+ *
93+ * This program is free software; you can redistribute it and/or modify
94+ * it under the terms of the GNU Lesser General Public License as
95+ * published by the Free Software Foundation; either version 2 of the
96+ * License, or (at your option) any later version.
97+ *
98+ * This program is distributed in the hope that it will be useful, but
99+ * WITHOUT ANY WARRANTY; without even the implied warranty of
100+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
101+ * Lesser General Public License for more details.
102+ *
103+ * You should have received a copy of the GNU Lesser General Public License
104+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
105+ *
106+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
107+ */
108+
109+#include <gio/gio.h>
110+#include <string.h>
111+#include "bus-watch-namespace.h"
112+
113+typedef struct
114+{
115+ guint id;
116+ gchar *name_space;
117+ GBusNameAppearedCallback appeared_handler;
118+ GBusNameVanishedCallback vanished_handler;
119+ gpointer user_data;
120+ GDestroyNotify user_data_destroy;
121+
122+ GDBusConnection *connection;
123+ GCancellable *cancellable;
124+ GHashTable *names;
125+ guint subscription_id;
126+} NamespaceWatcher;
127+
128+typedef struct
129+{
130+ NamespaceWatcher *watcher;
131+ gchar *name;
132+} GetNameOwnerData;
133+
134+static guint namespace_watcher_next_id;
135+static GHashTable *namespace_watcher_watchers;
136+
137+static void
138+namespace_watcher_stop (gpointer data)
139+{
140+ NamespaceWatcher *watcher = data;
141+
142+ g_cancellable_cancel (watcher->cancellable);
143+ g_object_unref (watcher->cancellable);
144+
145+ if (watcher->subscription_id)
146+ g_dbus_connection_signal_unsubscribe (watcher->connection, watcher->subscription_id);
147+
148+ if (watcher->vanished_handler)
149+ {
150+ GHashTableIter it;
151+ const gchar *name;
152+
153+ g_hash_table_iter_init (&it, watcher->names);
154+ while (g_hash_table_iter_next (&it, (gpointer *) &name, NULL))
155+ watcher->vanished_handler (watcher->connection, name, watcher->user_data);
156+ }
157+
158+ if (watcher->user_data_destroy)
159+ watcher->user_data_destroy (watcher->user_data);
160+
161+ if (watcher->connection)
162+ {
163+ g_signal_handlers_disconnect_by_func (watcher->connection, namespace_watcher_stop, watcher);
164+ g_object_unref (watcher->connection);
165+ }
166+
167+ g_hash_table_unref (watcher->names);
168+
169+ g_hash_table_remove (namespace_watcher_watchers, GUINT_TO_POINTER (watcher->id));
170+ if (g_hash_table_size (namespace_watcher_watchers) == 0)
171+ g_clear_pointer (&namespace_watcher_watchers, g_hash_table_destroy);
172+
173+ g_free (watcher->name_space);
174+
175+ g_free (watcher);
176+}
177+
178+static void
179+namespace_watcher_name_appeared (NamespaceWatcher *watcher,
180+ const gchar *name,
181+ const gchar *owner)
182+{
183+ /* There's a race between NameOwnerChanged signals arriving and the
184+ * ListNames/GetNameOwner sequence returning, so this function might
185+ * be called more than once for the same name. To ensure that
186+ * appeared_handler is only called once for each name, it is only
187+ * called when inserting the name into watcher->names (each name is
188+ * only inserted once there).
189+ */
190+ if (g_hash_table_contains (watcher->names, name))
191+ return;
192+
193+ g_hash_table_add (watcher->names, g_strdup (name));
194+
195+ if (watcher->appeared_handler)
196+ watcher->appeared_handler (watcher->connection, name, owner, watcher->user_data);
197+}
198+
199+static void
200+namespace_watcher_name_vanished (NamespaceWatcher *watcher,
201+ const gchar *name)
202+{
203+ if (g_hash_table_remove (watcher->names, name) && watcher->vanished_handler)
204+ watcher->vanished_handler (watcher->connection, name, watcher->user_data);
205+}
206+
207+static gboolean
208+dbus_name_has_namespace (const gchar *name,
209+ const gchar *name_space)
210+{
211+ gint len_name;
212+ gint len_namespace;
213+
214+ len_name = strlen (name);
215+ len_namespace = strlen (name_space);
216+
217+ if (len_name < len_namespace)
218+ return FALSE;
219+
220+ if (memcmp (name_space, name, len_namespace) != 0)
221+ return FALSE;
222+
223+ return len_namespace == len_name || name[len_namespace] == '.';
224+}
225+
226+static void
227+name_owner_changed (GDBusConnection *connection,
228+ const gchar *sender_name,
229+ const gchar *object_path,
230+ const gchar *interface_name,
231+ const gchar *signal_name,
232+ GVariant *parameters,
233+ gpointer user_data)
234+{
235+ NamespaceWatcher *watcher = user_data;
236+ const gchar *name;
237+ const gchar *old_owner;
238+ const gchar *new_owner;
239+
240+ g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
241+
242+ if (old_owner[0] != '\0')
243+ namespace_watcher_name_vanished (watcher, name);
244+
245+ if (new_owner[0] != '\0')
246+ namespace_watcher_name_appeared (watcher, name, new_owner);
247+}
248+
249+static void
250+got_name_owner (GObject *object,
251+ GAsyncResult *result,
252+ gpointer user_data)
253+{
254+ GetNameOwnerData *data = user_data;
255+ GError *error = NULL;
256+ GVariant *reply;
257+ const gchar *owner;
258+
259+ reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
260+
261+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
262+ {
263+ g_error_free (error);
264+ goto out;
265+ }
266+
267+ if (reply == NULL)
268+ {
269+ if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER))
270+ g_warning ("bus_watch_namespace: error calling org.freedesktop.DBus.GetNameOwner: %s", error->message);
271+ g_error_free (error);
272+ goto out;
273+ }
274+
275+ g_variant_get (reply, "(&s)", &owner);
276+ namespace_watcher_name_appeared (data->watcher, data->name, owner);
277+
278+ g_variant_unref (reply);
279+
280+out:
281+ g_free (data->name);
282+ g_slice_free (GetNameOwnerData, data);
283+}
284+
285+static void
286+names_listed (GObject *object,
287+ GAsyncResult *result,
288+ gpointer user_data)
289+{
290+ NamespaceWatcher *watcher;
291+ GError *error = NULL;
292+ GVariant *reply;
293+ GVariantIter *iter;
294+ const gchar *name;
295+
296+ reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
297+
298+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
299+ {
300+ g_error_free (error);
301+ return;
302+ }
303+
304+ watcher = user_data;
305+
306+ if (reply == NULL)
307+ {
308+ g_warning ("bus_watch_namespace: error calling org.freedesktop.DBus.ListNames: %s", error->message);
309+ g_error_free (error);
310+ return;
311+ }
312+
313+ g_variant_get (reply, "(as)", &iter);
314+ while (g_variant_iter_next (iter, "&s", &name))
315+ {
316+ if (dbus_name_has_namespace (name, watcher->name_space))
317+ {
318+ GetNameOwnerData *data = g_slice_new (GetNameOwnerData);
319+ data->watcher = watcher;
320+ data->name = g_strdup (name);
321+ g_dbus_connection_call (watcher->connection, "org.freedesktop.DBus", "/",
322+ "org.freedesktop.DBus", "GetNameOwner",
323+ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(s)"),
324+ G_DBUS_CALL_FLAGS_NONE, -1, watcher->cancellable,
325+ got_name_owner, data);
326+ }
327+ }
328+
329+ g_variant_iter_free (iter);
330+ g_variant_unref (reply);
331+}
332+
333+static void
334+connection_closed (GDBusConnection *connection,
335+ gboolean remote_peer_vanished,
336+ GError *error,
337+ gpointer user_data)
338+{
339+ NamespaceWatcher *watcher = user_data;
340+
341+ namespace_watcher_stop (watcher);
342+}
343+
344+static void
345+got_bus (GObject *object,
346+ GAsyncResult *result,
347+ gpointer user_data)
348+{
349+ GDBusConnection *connection;
350+ NamespaceWatcher *watcher;
351+ GError *error = NULL;
352+
353+ connection = g_bus_get_finish (result, &error);
354+
355+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
356+ {
357+ g_error_free (error);
358+ return;
359+ }
360+
361+ watcher = user_data;
362+
363+ if (connection == NULL)
364+ {
365+ namespace_watcher_stop (watcher);
366+ return;
367+ }
368+
369+ watcher->connection = connection;
370+ g_signal_connect (watcher->connection, "closed", G_CALLBACK (connection_closed), watcher);
371+
372+ watcher->subscription_id =
373+ g_dbus_connection_signal_subscribe (watcher->connection, "org.freedesktop.DBus",
374+ "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus",
375+ watcher->name_space, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
376+ name_owner_changed, watcher, NULL);
377+
378+ g_dbus_connection_call (watcher->connection, "org.freedesktop.DBus", "/",
379+ "org.freedesktop.DBus", "ListNames", NULL, G_VARIANT_TYPE ("(as)"),
380+ G_DBUS_CALL_FLAGS_NONE, -1, watcher->cancellable,
381+ names_listed, watcher);
382+}
383+
384+guint
385+bus_watch_namespace (GBusType bus_type,
386+ const gchar *name_space,
387+ GBusNameAppearedCallback appeared_handler,
388+ GBusNameVanishedCallback vanished_handler,
389+ gpointer user_data,
390+ GDestroyNotify user_data_destroy)
391+{
392+ NamespaceWatcher *watcher;
393+
394+ /* same rules for interfaces and well-known names */
395+ g_return_val_if_fail (name_space != NULL && g_dbus_is_interface_name (name_space), 0);
396+ g_return_val_if_fail (appeared_handler || vanished_handler, 0);
397+
398+ watcher = g_new0 (NamespaceWatcher, 1);
399+ watcher->id = namespace_watcher_next_id++;
400+ watcher->name_space = g_strdup (name_space);
401+ watcher->appeared_handler = appeared_handler;
402+ watcher->vanished_handler = vanished_handler;
403+ watcher->user_data = user_data;
404+ watcher->user_data_destroy = user_data_destroy;
405+ watcher->cancellable = g_cancellable_new ();
406+ watcher->names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
407+
408+ if (namespace_watcher_watchers == NULL)
409+ namespace_watcher_watchers = g_hash_table_new (g_direct_hash, g_direct_equal);
410+ g_hash_table_insert (namespace_watcher_watchers, GUINT_TO_POINTER (watcher->id), watcher);
411+
412+ g_bus_get (bus_type, watcher->cancellable, got_bus, watcher);
413+
414+ return watcher->id;
415+}
416+
417+void
418+bus_unwatch_namespace (guint id)
419+{
420+ /* namespace_watcher_stop() might have already removed the watcher
421+ * with @id in the case of a connection error. Thus, this function
422+ * doesn't warn when @id is absent from the hash table.
423+ */
424+
425+ if (namespace_watcher_watchers)
426+ {
427+ NamespaceWatcher *watcher;
428+
429+ watcher = g_hash_table_lookup (namespace_watcher_watchers, GUINT_TO_POINTER (id));
430+ if (watcher)
431+ {
432+ /* make sure vanished() is not called as a result of this function */
433+ g_hash_table_remove_all (watcher->names);
434+
435+ namespace_watcher_stop (watcher);
436+ }
437+ }
438+}
439
440=== added file 'src/bus-watch-namespace.h'
441--- src/bus-watch-namespace.h 1970-01-01 00:00:00 +0000
442+++ src/bus-watch-namespace.h 2013-08-26 15:12:41 +0000
443@@ -0,0 +1,34 @@
444+/*
445+ * Copyright 2013 Canonical Ltd.
446+ *
447+ * This program is free software; you can redistribute it and/or modify
448+ * it under the terms of the GNU Lesser General Public License as
449+ * published by the Free Software Foundation; either version 2 of the
450+ * License, or (at your option) any later version.
451+ *
452+ * This program is distributed in the hope that it will be useful, but
453+ * WITHOUT ANY WARRANTY; without even the implied warranty of
454+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
455+ * Lesser General Public License for more details.
456+ *
457+ * You should have received a copy of the GNU Lesser General Public License
458+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
459+ *
460+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
461+ */
462+
463+#ifndef __BUS_WATCH_NAMESPACE_H__
464+#define __BUS_WATCH_NAMESPACE_H__
465+
466+#include <gio/gio.h>
467+
468+guint bus_watch_namespace (GBusType bus_type,
469+ const gchar *name_space,
470+ GBusNameAppearedCallback appeared_handler,
471+ GBusNameVanishedCallback vanished_handler,
472+ gpointer user_data,
473+ GDestroyNotify user_data_destroy);
474+
475+void bus_unwatch_namespace (guint id);
476+
477+#endif
478
479=== modified file 'src/media-player-list.vala'
480--- src/media-player-list.vala 2013-06-28 19:58:32 +0000
481+++ src/media-player-list.vala 2013-08-26 15:12:41 +0000
482@@ -26,9 +26,7 @@
483 public MediaPlayerList () {
484 this._players = new HashTable<string, MediaPlayer> (str_hash, str_equal);
485
486- this.mpris_watcher = new Mpris2Watcher ();
487- this.mpris_watcher.client_appeared.connect (this.player_appeared);
488- this.mpris_watcher.client_disappeared.connect (this.player_disappeared);
489+ BusWatcher.watch_namespace (BusType.SESSION, "org.mpris.MediaPlayer2", this.player_appeared, this.player_disappeared);
490 }
491
492 /* only valid while the list is not changed */
493@@ -113,15 +111,21 @@
494 public signal void player_removed (MediaPlayer player);
495
496 HashTable<string, MediaPlayer> _players;
497- Mpris2Watcher mpris_watcher;
498-
499- void player_appeared (string desktop_id, string dbus_name, bool use_playlists) {
500- var player = this.insert (desktop_id);
501- if (player != null)
502- player.attach (dbus_name);
503+
504+ void player_appeared (DBusConnection connection, string name, string owner) {
505+ try {
506+ MprisRoot mpris2_root = Bus.get_proxy_sync (BusType.SESSION, name, MPRIS_MEDIA_PLAYER_PATH);
507+
508+ var player = this.insert (mpris2_root.DesktopEntry);
509+ if (player != null)
510+ player.attach (name);
511+ }
512+ catch (Error e) {
513+ warning ("unable to create mpris proxy for '%s': %s", name, e.message);
514+ }
515 }
516
517- void player_disappeared (string dbus_name) {
518+ void player_disappeared (DBusConnection connection, string dbus_name) {
519 MediaPlayer? player = this._players.find ( (name, player) => {
520 return player.dbus_name == dbus_name;
521 });
522
523=== removed file 'src/mpris2-watcher.vala'
524--- src/mpris2-watcher.vala 2013-06-28 19:58:32 +0000
525+++ src/mpris2-watcher.vala 1970-01-01 00:00:00 +0000
526@@ -1,200 +0,0 @@
527-/*
528-Copyright 2010 Canonical Ltd.
529-
530-Authors:
531- Conor Curran <conor.curran@canonical.com>
532-
533-This program is free software: you can redistribute it and/or modify it
534-under the terms of the GNU General Public License version 3, as published
535-by the Free Software Foundation.
536-
537-This program is distributed in the hope that it will be useful, but
538-WITHOUT ANY WARRANTY; without even the implied warranties of
539-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
540-PURPOSE. See the GNU General Public License for more details.
541-
542-You should have received a copy of the GNU General Public License along
543-with this program. If not, see <http://www.gnu.org/licenses/>.
544-*/
545-
546-using Xml;
547-
548-public class Mpris2Watcher : GLib.Object
549-{
550- DBusConnection session_bus;
551-
552- public signal void client_appeared ( string desktop_file_name,
553- string dbus_name,
554- bool use_playlists );
555- public signal void client_disappeared ( string dbus_name );
556-
557- public Mpris2Watcher ()
558- {
559- }
560-
561- construct
562- {
563- const string match_rule = "type='signal'," +
564- "sender='org.freedesktop.DBus'," +
565- "interface='org.freedesktop.DBus'," +
566- "member='NameOwnerChanged'," +
567- "path='/org/freedesktop/DBus'," +
568- "arg0namespace='org.mpris.MediaPlayer2'";
569- try {
570- this.session_bus = Bus.get_sync (BusType.SESSION);
571-
572- this.session_bus.call_sync ("org.freedesktop.DBus",
573- "/",
574- "org.freedesktop.DBus",
575- "AddMatch",
576- new Variant ("(s)", match_rule),
577- VariantType.TUPLE,
578- DBusCallFlags.NONE,
579- -1);
580-
581- this.session_bus.signal_subscribe ("org.freedesktop.DBus",
582- "org.freedesktop.DBus",
583- "NameOwnerChanged",
584- "/org/freedesktop/DBus",
585- null,
586- DBusSignalFlags.NO_MATCH_RULE,
587- this.name_owner_changed);
588-
589- this.check_for_active_clients.begin();
590- }
591- catch (GLib.Error e) {
592- warning ("unable to set up name watch for mrpis clients: %s", e.message);
593- }
594- }
595-
596- // At startup check to see if there are clients up that we are interested in
597- // More relevant for development and daemon's like mpd.
598- async void check_for_active_clients()
599- {
600- Variant interfaces;
601-
602- try {
603- interfaces = yield this.session_bus.call ("org.freedesktop.DBus",
604- "/",
605- "org.freedesktop.DBus",
606- "ListNames",
607- null,
608- new VariantType ("(as)"),
609- DBusCallFlags.NONE,
610- -1);
611- }
612- catch (GLib.Error e) {
613- warning ("unable to search for existing mpris clients: %s ", e.message);
614- return;
615- }
616-
617- foreach (var val in interfaces.get_child_value (0)) {
618- var address = (string) val;
619- if (address.has_prefix (MPRIS_PREFIX)){
620- MprisRoot? mpris2_root = this.create_mpris_root(address);
621- if (mpris2_root == null) return;
622- bool use_playlists = this.supports_playlists ( address );
623- client_appeared (mpris2_root.DesktopEntry + ".desktop", address, use_playlists);
624- }
625- }
626- }
627-
628- void name_owner_changed (DBusConnection con, string sender, string object_path,
629- string interface_name, string signal_name, Variant parameters)
630- {
631- string name, previous_owner, current_owner;
632-
633- parameters.get ("(sss)", out name, out previous_owner, out current_owner);
634-
635- MprisRoot? mpris2_root = this.create_mpris_root (name);
636- if (mpris2_root == null) return;
637-
638- if (previous_owner != "" && current_owner == "") {
639- debug ("Client '%s' gone down", name);
640- client_disappeared (name);
641- }
642- else if (previous_owner == "" && current_owner != "") {
643- debug ("Client '%s' has appeared", name);
644- bool use_playlists = this.supports_playlists ( name );
645- client_appeared (mpris2_root.DesktopEntry + ".desktop", name, use_playlists);
646- }
647- }
648-
649- private MprisRoot? create_mpris_root ( string name ){
650- MprisRoot mpris2_root = null;
651- if ( name.has_prefix (MPRIS_PREFIX) ){
652- try {
653- mpris2_root = Bus.get_proxy_sync ( BusType.SESSION,
654- name,
655- MPRIS_MEDIA_PLAYER_PATH );
656- }
657- catch (IOError e){
658- warning( "Mpris2watcher could not create a root interface: %s",
659- e.message );
660- }
661- }
662- return mpris2_root;
663- }
664-
665- private bool supports_playlists ( string name )
666- {
667- FreeDesktopIntrospectable introspectable;
668-
669- try {
670- /* The dbusproxy flag parameter is needed to ensure Banshee does not
671- blow up. I suspect the issue is that if you
672- try to instantiate a dbus object which does not have any properties
673- associated with it, gdbus will attempt to fetch the properties (this is
674- in the documentation) but the banshee mpris dbus object more than likely
675- causes a crash because it doesn't check for the presence of properties
676- before attempting to access them.
677- */
678- introspectable = Bus.get_proxy_sync ( BusType.SESSION,
679- name,
680- MPRIS_MEDIA_PLAYER_PATH,
681- GLib.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
682- var results = introspectable.Introspect();
683- return this.parse_interfaces (results);
684- }
685- catch (IOError e){
686- warning( "Could not create an introspectable object: %s",
687- e.message );
688- }
689- return false;
690- }
691-
692- private bool parse_interfaces( string interface_info )
693- {
694- //parse the document from path
695- bool result = false;
696- Xml.Doc* xml_doc = Parser.parse_doc (interface_info);
697- if (xml_doc == null) {
698- warning ("Mpris2Watcher - parse-interfaces - failed to instantiate xml doc");
699- return false;
700- }
701- //get the root node. notice the dereferencing operator -> instead of .
702- Xml.Node* root_node = xml_doc->get_root_element ();
703- if (root_node == null) {
704- //free the document manually before throwing because the garbage collector can't work on pointers
705- delete xml_doc;
706- warning ("Mpris2Watcher - the interface info xml is empty");
707- return false;
708- }
709-
710- //let's parse those nodes
711- for (Xml.Node* iter = root_node->children; iter != null; iter = iter->next) {
712- //spaces btw. tags are also nodes, discard them
713- if (iter->type != ElementType.ELEMENT_NODE){
714- continue;
715- }
716- Xml.Attr* attributes = iter->properties; //get the node's name
717- string interface_name = attributes->children->content;
718- debug ( "this dbus object has interface %s ", interface_name );
719- if ( interface_name == MPRIS_PREFIX.concat("Playlists")){
720- result = true;
721- }
722- }
723- delete xml_doc;
724- return result;
725- }
726-}
727
728=== added file 'vapi/bus-watcher.vapi'
729--- vapi/bus-watcher.vapi 1970-01-01 00:00:00 +0000
730+++ vapi/bus-watcher.vapi 2013-08-26 15:12:41 +0000
731@@ -0,0 +1,25 @@
732+/*
733+ * Copyright 2013 Canonical Ltd.
734+ *
735+ * This program is free software; you can redistribute it and/or modify
736+ * it under the terms of the GNU Lesser General Public License as
737+ * published by the Free Software Foundation; version 3.
738+ *
739+ * This program is distributed in the hope that it will be useful,
740+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
741+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
742+ * GNU Lesser General Public License for more details.
743+ *
744+ * You should have received a copy of the GNU Lesser General Public License
745+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
746+ *
747+ * Authors:
748+ * Lars Uebernickel <lars.uebernickel@canonical.com>
749+ */
750+
751+namespace BusWatcher {
752+ [CCode (cheader_filename = "bus-watch-namespace.h", cname = "bus_watch_namespace")]
753+ public static uint watch_namespace (GLib.BusType bus_type, string name_space,
754+ [CCode (delegate_target_pos = 4.9)] owned GLib.BusNameAppearedCallback? name_appeared,
755+ [CCode (delegate_target_pos = 4.9)] owned GLib.BusNameVanishedCallback? name_vanished);
756+}

Subscribers

People subscribed via source and target branches