Merge lp:~larsu/indicator-sound/use-bus-watch-namespace into lp:indicator-sound/13.10
- use-bus-watch-namespace
- Merge into trunk.13.10
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 |
Related bugs: |
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 |
Commit message
Description of the change
Use bus_watch_
PS Jenkins bot (ps-jenkins) wrote : | # |
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_
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-
- 366. By Lars Karlitski
-
bus-watch-
namespace: free name_space - 367. By Lars Karlitski
-
bus-watch-
namespace: remove stray semicolon
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_
> 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-
:)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:367
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 368. By Lars Karlitski
-
Merge trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:368
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Charles Kerr (charlesk) : | # |
Preview Diff
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 | +} |
PASSED: Continuous integration, rev:365 jenkins. qa.ubuntu. com/job/ indicator- sound-ci/ 35/ jenkins. qa.ubuntu. com/job/ indicator- sound-saucy- amd64-ci/ 16 jenkins. qa.ubuntu. com/job/ indicator- sound-saucy- armhf-ci/ 16
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ indicator- sound-ci/ 35/rebuild
http://