Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-mpris-impl"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-mpris-impl.h"
12 : : #include "valent-mpris-utils.h"
13 : :
14 : :
15 : : struct _ValentMPRISImpl
16 : : {
17 : : GObject parent_instance;
18 : :
19 : : ValentMediaPlayer *player;
20 : : GDBusConnection *connection;
21 : : char *bus_name;
22 : : unsigned int bus_name_id;
23 : :
24 : : GHashTable *cache;
25 : : GHashTable *pending;
26 : : unsigned int flush_id;
27 : :
28 : : /* org.mpris.MediaPlayer2 */
29 : : unsigned int application_id;
30 : : GDBusInterfaceVTable application_vtable;
31 : :
32 : : /* org.mpris.MediaPlayer2.Player */
33 : : unsigned int player_id;
34 : : GDBusInterfaceVTable player_vtable;
35 : : };
36 : :
37 [ + + + - ]: 368 : G_DEFINE_FINAL_TYPE (ValentMPRISImpl, valent_mpris_impl, G_TYPE_OBJECT)
38 : :
39 : :
40 : : enum {
41 : : PROP_0,
42 : : PROP_PLAYER,
43 : : N_PROPERTIES
44 : : };
45 : :
46 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
47 : :
48 : :
49 : : /*
50 : : * org.mpris.MediaPlayer2
51 : : */
52 : : static void
53 : 0 : application_method_call (GDBusConnection *connection,
54 : : const char *sender,
55 : : const char *object_path,
56 : : const char *interface_name,
57 : : const char *method_name,
58 : : GVariant *parameters,
59 : : GDBusMethodInvocation *invocation,
60 : : gpointer user_data)
61 : : {
62 [ # # ]: 0 : g_assert (VALENT_IS_MPRIS_IMPL (user_data));
63 [ # # ]: 0 : g_assert (method_name != NULL);
64 : :
65 [ # # ]: 0 : if (g_str_equal (method_name, "Raise"))
66 : : {
67 : 0 : GApplication *application = g_application_get_default ();
68 : :
69 [ # # ]: 0 : if (application != NULL)
70 : : {
71 : 0 : g_action_group_activate_action (G_ACTION_GROUP (application),
72 : : "media-remote",
73 : : NULL);
74 : : }
75 : : }
76 : :
77 : : /* Silently ignore method calls */
78 : 0 : g_dbus_method_invocation_return_value (invocation, NULL);
79 : 0 : }
80 : :
81 : : static GVariant *
82 : 45 : application_get_property (GDBusConnection *connection,
83 : : const char *sender,
84 : : const char *object_path,
85 : : const char *interface_name,
86 : : const char *property_name,
87 : : GError **error,
88 : : gpointer user_data)
89 : : {
90 : 45 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
91 : 45 : GVariant *value = NULL;
92 : :
93 [ + - ]: 45 : g_assert (VALENT_IS_MPRIS_IMPL (self));
94 [ - + ]: 45 : g_assert (property_name != NULL);
95 : :
96 [ - + ]: 45 : if ((value = g_hash_table_lookup (self->cache, property_name)) != NULL)
97 : 0 : return g_variant_ref_sink (value);
98 : :
99 [ + + ]: 45 : if (g_str_equal (property_name, "Identity"))
100 : 5 : value = g_variant_new_string (valent_media_player_get_name (self->player));
101 [ + + ]: 40 : else if (g_str_equal (property_name, "CanQuit"))
102 : 5 : value = g_variant_new_boolean (FALSE);
103 [ + + ]: 35 : else if (g_str_equal (property_name, "CanRaise"))
104 : 5 : value = g_variant_new_boolean (TRUE);
105 [ + + ]: 30 : else if (g_str_equal (property_name, "CanSetFullscreen"))
106 : 5 : value = g_variant_new_boolean (FALSE);
107 [ + + ]: 25 : else if (g_str_equal (property_name, "DesktopEntry"))
108 : 5 : value = g_variant_new_string (APPLICATION_ID".desktop");
109 [ + + ]: 20 : else if (g_str_equal (property_name, "Fullscreen") ||
110 [ + + ]: 15 : g_str_equal (property_name, "HasTrackList"))
111 : 10 : value = g_variant_new_boolean (FALSE);
112 [ + + ]: 10 : else if (g_str_equal (property_name, "SupportedMimeTypes") ||
113 [ + - ]: 5 : g_str_equal (property_name, "SupportedUriSchemes"))
114 : 10 : value = g_variant_new_strv (NULL, 0);
115 : :
116 [ + - ]: 45 : if (value != NULL)
117 : : {
118 : 90 : g_hash_table_replace (self->cache,
119 : 45 : g_strdup (property_name),
120 [ - + ]: 45 : g_variant_ref_sink (value));
121 : :
122 : 45 : return g_variant_ref_sink (value);
123 : : }
124 : :
125 : 0 : g_set_error (error,
126 : : G_DBUS_ERROR,
127 : : G_DBUS_ERROR_UNKNOWN_PROPERTY,
128 : : "Unknown property \"%s\"", property_name);
129 : :
130 : 0 : return NULL;
131 : : }
132 : :
133 : : /* LCOV_EXCL_START */
134 : : static gboolean
135 : : application_set_property (GDBusConnection *connection,
136 : : const char *sender,
137 : : const char *object_path,
138 : : const char *interface_name,
139 : : const char *property_name,
140 : : GVariant *value,
141 : : GError **error,
142 : : gpointer user_data)
143 : : {
144 : : /* Silently ignore property setters */
145 : : return TRUE;
146 : : }
147 : : /* LCOV_EXCL_STOP */
148 : :
149 : : /*
150 : : * org.mpris.MediaPlayer2.Player
151 : : */
152 : : static void
153 : 20 : player_method_call (GDBusConnection *connection,
154 : : const char *sender,
155 : : const char *object_path,
156 : : const char *interface_name,
157 : : const char *method_name,
158 : : GVariant *parameters,
159 : : GDBusMethodInvocation *invocation,
160 : : gpointer user_data)
161 : : {
162 : 20 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
163 : :
164 [ + - ]: 20 : g_assert (VALENT_IS_MPRIS_IMPL (self));
165 [ - + ]: 20 : g_assert (method_name != NULL);
166 : :
167 [ + + ]: 20 : if (g_str_equal (method_name, "Next"))
168 : : {
169 : 3 : valent_media_player_next (self->player);
170 : : }
171 [ + + ]: 17 : else if (g_str_equal (method_name, "Pause"))
172 : : {
173 : 3 : valent_media_player_pause (self->player);
174 : : }
175 [ + + ]: 14 : else if (g_str_equal (method_name, "Play"))
176 : : {
177 : 3 : valent_media_player_play (self->player);
178 : : }
179 [ - + ]: 11 : else if (g_str_equal (method_name, "PlayPause"))
180 : : {
181 [ # # # # : 0 : valent_mpris_play_pause (self->player);
# # ]
182 : : }
183 [ + + ]: 11 : else if (g_str_equal (method_name, "Previous"))
184 : : {
185 : 3 : valent_media_player_previous (self->player);
186 : : }
187 [ + + ]: 8 : else if (g_str_equal (method_name, "Seek"))
188 : : {
189 : 3 : int64_t offset_us;
190 : :
191 : : /* Convert microseconds to seconds */
192 : 3 : g_variant_get (parameters, "(x)", &offset_us);
193 : 3 : valent_media_player_seek (self->player, offset_us / G_TIME_SPAN_SECOND);
194 : : }
195 [ + + ]: 5 : else if (g_str_equal (method_name, "SetPosition"))
196 : : {
197 : 2 : int64_t position_us;
198 : :
199 : : /* Convert microseconds to seconds */
200 : 2 : g_variant_get (parameters, "(&ox)", NULL, &position_us);
201 : 2 : valent_media_player_set_position (self->player, position_us / G_TIME_SPAN_SECOND);
202 : : }
203 [ + - ]: 3 : else if (g_str_equal (method_name, "Stop"))
204 : : {
205 : 3 : valent_media_player_stop (self->player);
206 : : }
207 [ # # ]: 0 : else if (g_str_equal (method_name, "OpenUri"))
208 : : {
209 : : /* Silently ignore method calls */
210 : : }
211 : : else
212 : : {
213 : 0 : g_dbus_method_invocation_return_error (invocation,
214 : : G_DBUS_ERROR,
215 : : G_DBUS_ERROR_UNKNOWN_METHOD,
216 : : "Unknown method \"%s\"",
217 : : method_name);
218 : 0 : return;
219 : : }
220 : :
221 : 20 : g_dbus_method_invocation_return_value (invocation, NULL);
222 : : }
223 : :
224 : : static GVariant *
225 : 79 : player_get_property (GDBusConnection *connection,
226 : : const char *sender,
227 : : const char *object_path,
228 : : const char *interface_name,
229 : : const char *property_name,
230 : : GError **error,
231 : : gpointer user_data)
232 : : {
233 : 79 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
234 : 79 : ValentMediaActions flags = VALENT_MEDIA_ACTION_NONE;
235 : 79 : GVariant *value;
236 : :
237 [ + - ]: 79 : g_assert (VALENT_IS_MPRIS_IMPL (self));
238 [ - + ]: 79 : g_assert (property_name != NULL);
239 : :
240 : : /* Check cache */
241 [ - + ]: 79 : if ((value = g_hash_table_lookup (self->cache, property_name)) != NULL)
242 : 0 : return g_variant_ref (value);
243 : :
244 : : /* The `Position` is not cached, because `PropertiesChanged` is not emitted */
245 [ + + ]: 79 : if (g_str_equal (property_name, "Position"))
246 : : {
247 : 9 : double position = valent_media_player_get_position (self->player);
248 : :
249 : : /* Convert seconds to microseconds */
250 : 9 : return g_variant_new_int64 ((int64_t)(position * G_TIME_SPAN_SECOND));
251 : : }
252 : :
253 : : /* Load properties */
254 [ + + ]: 70 : if (*property_name == 'C')
255 : 30 : flags = valent_media_player_get_flags (self->player);
256 : :
257 [ + + ]: 70 : if (g_str_equal (property_name, "CanControl"))
258 : : {
259 : 5 : value = g_variant_new_boolean (flags != 0);
260 : : }
261 [ + + ]: 65 : else if (g_str_equal (property_name, "CanGoNext"))
262 : : {
263 : 5 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_NEXT) != 0);
264 : : }
265 [ + + ]: 60 : else if (g_str_equal (property_name, "CanGoPrevious"))
266 : : {
267 : 5 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
268 : : }
269 [ + + ]: 55 : else if (g_str_equal (property_name, "CanPlay"))
270 : : {
271 : 5 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PLAY) != 0);
272 : : }
273 [ + + ]: 50 : else if (g_str_equal (property_name, "CanPause"))
274 : : {
275 : 5 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
276 : : }
277 [ + + ]: 45 : else if (g_str_equal (property_name, "CanSeek"))
278 : : {
279 : 5 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_SEEK) != 0);
280 : : }
281 [ + + ]: 40 : else if (g_str_equal (property_name, "Metadata"))
282 : : {
283 : 5 : value = valent_media_player_get_metadata (self->player);
284 : : }
285 [ + + ]: 35 : else if (g_str_equal (property_name, "LoopStatus"))
286 : : {
287 : 5 : ValentMediaRepeat repeat = valent_media_player_get_repeat (self->player);
288 : :
289 : 5 : value = g_variant_new_string (valent_mpris_repeat_to_string (repeat));
290 : : }
291 [ + + ]: 30 : else if (g_str_equal (property_name, "Shuffle"))
292 : : {
293 : 5 : gboolean shuffle = valent_media_player_get_shuffle (self->player);
294 : :
295 : 5 : value = g_variant_new_boolean (shuffle);
296 : : }
297 [ + + ]: 25 : else if (g_str_equal (property_name, "PlaybackStatus"))
298 : : {
299 : 5 : ValentMediaState state = valent_media_player_get_state (self->player);
300 : :
301 : 5 : value = g_variant_new_string (valent_mpris_state_to_string (state));
302 : : }
303 [ + + ]: 20 : else if (g_str_equal (property_name, "Volume"))
304 : : {
305 : 5 : double volume = valent_media_player_get_volume (self->player);
306 : :
307 : 5 : value = g_variant_new_double (volume);
308 : : }
309 [ + + ]: 15 : else if (g_str_equal (property_name, "Rate") ||
310 [ + + ]: 10 : g_str_equal (property_name, "MaximumRate") ||
311 [ + - ]: 5 : g_str_equal (property_name, "MinimumRate"))
312 : : {
313 : 15 : value = g_variant_new_double (1.0);
314 : : }
315 : :
316 [ + - ]: 70 : if (value != NULL)
317 : : {
318 : 140 : g_hash_table_replace (self->cache,
319 : 70 : g_strdup (property_name),
320 [ - + ]: 70 : g_variant_take_ref (value));
321 : :
322 : 70 : return g_variant_ref (value);
323 : : }
324 : :
325 : 0 : g_set_error (error,
326 : : G_DBUS_ERROR,
327 : : G_DBUS_ERROR_UNKNOWN_PROPERTY,
328 : : "Unknown property \"%s\"",
329 : : property_name);
330 : :
331 : 0 : return NULL;
332 : : }
333 : :
334 : : static gboolean
335 : 9 : player_set_property (GDBusConnection *connection,
336 : : const char *sender,
337 : : const char *object_path,
338 : : const char *interface_name,
339 : : const char *property_name,
340 : : GVariant *value,
341 : : GError **error,
342 : : gpointer user_data)
343 : : {
344 : 9 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
345 : :
346 [ + - ]: 9 : g_assert (VALENT_IS_MPRIS_IMPL (self));
347 [ - + ]: 9 : g_assert (property_name != NULL);
348 : :
349 [ + + ]: 9 : if (g_str_equal (property_name, "LoopStatus"))
350 : : {
351 : 3 : const char *loop_status = g_variant_get_string (value, NULL);
352 : 3 : ValentMediaRepeat repeat = valent_mpris_repeat_from_string (loop_status);
353 : :
354 : 3 : valent_media_player_set_repeat (self->player, repeat);
355 : 3 : return TRUE;
356 : : }
357 : :
358 [ + + ]: 6 : if (g_str_equal (property_name, "Shuffle"))
359 : : {
360 : 3 : gboolean shuffle = g_variant_get_boolean (value);
361 : :
362 : 3 : valent_media_player_set_shuffle (self->player, shuffle);
363 : 3 : return TRUE;
364 : : }
365 : :
366 [ + - ]: 3 : if (g_str_equal (property_name, "Volume"))
367 : : {
368 : 3 : double volume = g_variant_get_double (value);
369 : :
370 : 3 : valent_media_player_set_volume (self->player, volume);
371 : 3 : return TRUE;
372 : : }
373 : :
374 : : return TRUE;
375 : : }
376 : :
377 : : static gboolean
378 : 27 : valent_mpris_impl_flush (gpointer data)
379 : : {
380 : 27 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (data);
381 : 54 : g_autoptr (GError) error = NULL;
382 : 27 : GVariant *parameters;
383 : 27 : GVariantBuilder changed_props;
384 : 27 : GVariantBuilder invalidated_props;
385 : 27 : GHashTableIter iter;
386 : 27 : GVariant *value;
387 : 27 : char *name;
388 : :
389 [ + - ]: 27 : g_assert (VALENT_IS_MPRIS_IMPL (self));
390 : :
391 [ + - ]: 27 : if (self->connection != NULL)
392 : : {
393 : 27 : g_variant_builder_init (&changed_props, G_VARIANT_TYPE_VARDICT);
394 : 27 : g_variant_builder_init (&invalidated_props, G_VARIANT_TYPE_STRING_ARRAY);
395 : :
396 : 27 : g_hash_table_iter_init (&iter, self->pending);
397 : :
398 [ + + ]: 166 : while (g_hash_table_iter_next (&iter, (void**)&name, (void**)&value))
399 : : {
400 [ + - ]: 139 : if (value)
401 : 139 : g_variant_builder_add (&changed_props, "{sv}", name, value);
402 : : else
403 : 0 : g_variant_builder_add (&invalidated_props, "s", name);
404 : :
405 : 139 : g_hash_table_iter_remove (&iter);
406 : : }
407 : :
408 : 27 : parameters = g_variant_new ("(s@a{sv}@as)",
409 : : "org.mpris.MediaPlayer2.Player",
410 : : g_variant_builder_end (&changed_props),
411 : : g_variant_builder_end (&invalidated_props));
412 : :
413 : 27 : g_dbus_connection_emit_signal (self->connection,
414 : : NULL,
415 : : "/org/mpris/MediaPlayer2",
416 : : "org.freedesktop.DBus.Properties",
417 : : "PropertiesChanged",
418 : : parameters,
419 : : &error);
420 : :
421 [ - + ]: 27 : if (error != NULL)
422 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
423 : : }
424 : :
425 [ + - ]: 27 : g_clear_handle_id (&self->flush_id, g_source_remove);
426 : :
427 [ - + ]: 27 : return G_SOURCE_REMOVE;
428 : : }
429 : :
430 : : static void
431 : 150 : valent_mpris_impl_set_value (ValentMPRISImpl *self,
432 : : const char *name,
433 : : GVariant *value)
434 : : {
435 [ + - ]: 150 : g_assert (VALENT_IS_MPRIS_IMPL (self));
436 [ + - - + ]: 150 : g_assert (name != NULL && *name != '\0');
437 [ - + ]: 150 : g_assert (value != NULL);
438 : :
439 : 300 : g_hash_table_replace (self->cache,
440 : 150 : g_strdup (name),
441 [ - + ]: 150 : g_variant_ref_sink (value));
442 : 300 : g_hash_table_replace (self->pending,
443 : 150 : g_strdup (name),
444 [ - + ]: 150 : g_variant_ref_sink (value));
445 : :
446 [ + + ]: 150 : if (self->flush_id == 0)
447 : 27 : self->flush_id = g_idle_add (valent_mpris_impl_flush, self);
448 : 150 : }
449 : :
450 : : static void
451 : 7 : valent_mpris_impl_propagate_seeked (ValentMPRISImpl *self,
452 : : int64_t position)
453 : : {
454 : 7 : g_autoptr (GError) error = NULL;
455 : 7 : gboolean ret;
456 : :
457 [ - + ]: 7 : if (self->connection == NULL)
458 : 0 : return;
459 : :
460 : 7 : ret = g_dbus_connection_emit_signal (self->connection,
461 : : NULL,
462 : : "/org/mpris/MediaPlayer2",
463 : : "org.mpris.MediaPlayer2.Player",
464 : : "Seeked",
465 : : g_variant_new ("(x)", position),
466 : : &error);
467 : :
468 [ - + ]: 7 : if (!ret)
469 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
470 : : }
471 : :
472 : : static void
473 : 77 : valent_mpris_impl_propagate_notify (ValentMediaPlayer *player,
474 : : GParamSpec *pspec,
475 : : ValentMPRISImpl *self)
476 : : {
477 : 77 : const char *name = g_param_spec_get_name (pspec);
478 : 77 : GVariant *value = NULL;
479 : :
480 [ + + ]: 77 : if (g_str_equal (name, "flags"))
481 : : {
482 : 16 : ValentMediaActions flags = valent_media_player_get_flags (self->player);
483 : :
484 : 16 : value = g_variant_new_boolean (flags != 0);
485 : 16 : valent_mpris_impl_set_value (self, "CanControl", value);
486 : 16 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_NEXT) != 0);
487 : 16 : valent_mpris_impl_set_value (self, "CanGoNext", value);
488 : 16 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
489 : 16 : valent_mpris_impl_set_value (self, "CanPause", value);
490 : 16 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PLAY) != 0);
491 : 16 : valent_mpris_impl_set_value (self, "CanPlay", value);
492 : 16 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
493 : 16 : valent_mpris_impl_set_value (self, "CanGoPrevious", value);
494 : 16 : value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_SEEK) != 0);
495 : 16 : valent_mpris_impl_set_value (self, "CanSeek", value);
496 : : }
497 [ + + ]: 61 : else if (g_str_equal (name, "metadata"))
498 : : {
499 : 19 : value = valent_media_player_get_metadata (self->player);
500 : 19 : valent_mpris_impl_set_value (self, "Metadata", value);
501 : 19 : g_variant_unref (value);
502 : : }
503 [ - + ]: 42 : else if (g_str_equal (name, "name"))
504 : : {
505 : 0 : const char *identity = valent_media_player_get_name (self->player);
506 : :
507 : 0 : value = g_variant_new_string (identity);
508 : 0 : g_hash_table_replace (self->cache,
509 : 0 : g_strdup ("Identity"),
510 : 0 : g_variant_ref_sink (value));
511 : : }
512 [ + + ]: 42 : else if (g_str_equal (name, "position"))
513 : : {
514 : 7 : double position = valent_media_player_get_position (self->player);
515 : :
516 : : /* Convert seconds to microseconds */
517 : 7 : value = g_variant_new_int64 ((int64_t)(position * G_TIME_SPAN_SECOND));
518 : 14 : g_hash_table_replace (self->cache,
519 : 7 : g_strdup ("Position"),
520 : 7 : g_variant_ref_sink (value));
521 : :
522 : : /* Convert seconds to microseconds */
523 : 7 : valent_mpris_impl_propagate_seeked (self, (int64_t)(position * G_TIME_SPAN_SECOND));
524 : : }
525 [ + + ]: 35 : else if (g_str_equal (name, "repeat"))
526 : : {
527 : 3 : ValentMediaRepeat repeat = valent_media_player_get_repeat (self->player);
528 : :
529 : 3 : value = g_variant_new_string (valent_mpris_repeat_to_string (repeat));
530 : 3 : valent_mpris_impl_set_value (self, "LoopStatus", value);
531 : : }
532 [ + + ]: 32 : else if (g_str_equal (name, "shuffle"))
533 : : {
534 : 3 : gboolean shuffle = valent_media_player_get_shuffle (self->player);
535 : :
536 : 3 : value = g_variant_new_boolean (shuffle);
537 : 3 : valent_mpris_impl_set_value (self, "Shuffle", value);
538 : : }
539 [ + + ]: 29 : else if (g_str_equal (name, "state"))
540 : : {
541 : 26 : ValentMediaState state = valent_media_player_get_state (self->player);
542 : :
543 : 26 : value = g_variant_new_string (valent_mpris_state_to_string (state));
544 : 26 : valent_mpris_impl_set_value (self, "PlaybackStatus", value);
545 : : }
546 [ + - ]: 3 : else if (g_str_equal (name, "volume"))
547 : : {
548 : 3 : double volume = valent_media_player_get_volume (self->player);
549 : :
550 : 3 : value = g_variant_new_double (volume);
551 : 3 : valent_mpris_impl_set_value (self, "Volume", value);
552 : : }
553 : 77 : }
554 : :
555 : : /*
556 : : * GObject
557 : : */
558 : : static void
559 : 5 : valent_mpris_impl_constructed (GObject *object)
560 : : {
561 : 5 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
562 : :
563 [ + - ]: 5 : g_assert (VALENT_IS_MEDIA_PLAYER (self->player));
564 : :
565 : 5 : g_signal_connect_object (self->player,
566 : : "notify",
567 : : G_CALLBACK (valent_mpris_impl_propagate_notify),
568 : : self, 0);
569 : :
570 : 5 : G_OBJECT_CLASS (valent_mpris_impl_parent_class)->constructed (object);
571 : 5 : }
572 : :
573 : : static void
574 : 5 : valent_mpris_impl_dispose (GObject *object)
575 : : {
576 : 5 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
577 : :
578 : 5 : g_signal_handlers_disconnect_by_data (self->player, self);
579 : 5 : valent_mpris_impl_unexport (self);
580 : :
581 : 5 : G_OBJECT_CLASS (valent_mpris_impl_parent_class)->dispose (object);
582 : 5 : }
583 : :
584 : : static void
585 : 5 : valent_mpris_impl_finalize (GObject *object)
586 : : {
587 : 5 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
588 : :
589 [ + - ]: 5 : g_clear_pointer (&self->bus_name, g_free);
590 [ - + ]: 5 : g_clear_object (&self->connection);
591 [ + - ]: 5 : g_clear_object (&self->player);
592 : :
593 [ + - ]: 5 : g_clear_pointer (&self->cache, g_hash_table_unref);
594 [ + - ]: 5 : g_clear_pointer (&self->pending, g_hash_table_unref);
595 : :
596 : 5 : G_OBJECT_CLASS (valent_mpris_impl_parent_class)->finalize (object);
597 : 5 : }
598 : :
599 : : static void
600 : 1 : valent_mpris_impl_get_property (GObject *object,
601 : : guint prop_id,
602 : : GValue *value,
603 : : GParamSpec *pspec)
604 : : {
605 : 1 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
606 : :
607 [ + - ]: 1 : switch (prop_id)
608 : : {
609 : 1 : case PROP_PLAYER:
610 : 1 : g_value_set_object (value, self->player);
611 : 1 : break;
612 : :
613 : 0 : default:
614 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
615 : : }
616 : 1 : }
617 : :
618 : : static void
619 : 5 : valent_mpris_impl_set_property (GObject *object,
620 : : guint prop_id,
621 : : const GValue *value,
622 : : GParamSpec *pspec)
623 : : {
624 : 5 : ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
625 : :
626 [ + - ]: 5 : switch (prop_id)
627 : : {
628 : 5 : case PROP_PLAYER:
629 : 5 : self->player = g_value_dup_object (value);
630 : 5 : break;
631 : :
632 : 0 : default:
633 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
634 : : }
635 : 5 : }
636 : :
637 : : static void
638 : 2 : valent_mpris_impl_class_init (ValentMPRISImplClass *klass)
639 : : {
640 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
641 : :
642 : 2 : object_class->constructed = valent_mpris_impl_constructed;
643 : 2 : object_class->dispose = valent_mpris_impl_dispose;
644 : 2 : object_class->finalize = valent_mpris_impl_finalize;
645 : 2 : object_class->get_property = valent_mpris_impl_get_property;
646 : 2 : object_class->set_property = valent_mpris_impl_set_property;
647 : :
648 : : /**
649 : : * ValentMprisDevice:player:
650 : : *
651 : : * The [class@Valent.MediaPlayer] being exported.
652 : : */
653 : 4 : properties [PROP_PLAYER] =
654 : 2 : g_param_spec_object ("player", NULL, NULL,
655 : : VALENT_TYPE_MEDIA_PLAYER,
656 : : (G_PARAM_READWRITE |
657 : : G_PARAM_CONSTRUCT_ONLY |
658 : : G_PARAM_EXPLICIT_NOTIFY |
659 : : G_PARAM_STATIC_STRINGS));
660 : :
661 : 2 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
662 : 2 : }
663 : :
664 : : static void
665 : 5 : valent_mpris_impl_init (ValentMPRISImpl *self)
666 : : {
667 : 5 : self->application_vtable.method_call = application_method_call;
668 : 5 : self->application_vtable.get_property = application_get_property;
669 : 5 : self->application_vtable.set_property = application_set_property;
670 : :
671 : 5 : self->player_vtable.method_call = player_method_call;
672 : 5 : self->player_vtable.get_property = player_get_property;
673 : 5 : self->player_vtable.set_property = player_set_property;
674 : :
675 : 5 : self->bus_name = g_strdup (VALENT_MPRIS_DBUS_NAME);
676 : 5 : self->cache = g_hash_table_new_full (g_str_hash,
677 : : g_str_equal,
678 : : g_free,
679 : : (GDestroyNotify)g_variant_unref);
680 : 5 : self->pending = g_hash_table_new_full (g_str_hash,
681 : : g_str_equal,
682 : : g_free,
683 : : (GDestroyNotify)g_variant_unref);
684 : 5 : }
685 : :
686 : : /**
687 : : * valent_mpris_impl_new:
688 : : * @player: a `ValentMediaPlayer`
689 : : *
690 : : * Get the `ValentMPRISImpl` instance.
691 : : *
692 : : * Returns: (transfer full) (nullable): a `ValentMPRISImpl`
693 : : */
694 : : ValentMPRISImpl *
695 : 5 : valent_mpris_impl_new (ValentMediaPlayer *player)
696 : : {
697 [ + - ]: 5 : g_return_val_if_fail (VALENT_IS_MEDIA_PLAYER (player), NULL);
698 : :
699 : 5 : return g_object_new (VALENT_TYPE_MPRIS_IMPL,
700 : : "player", player,
701 : : NULL);
702 : : }
703 : :
704 : : /**
705 : : * valent_media_player_impl_export:
706 : : * @impl: a `ValentMPRISImpl`
707 : : * @connection: a `GDBusConnection`
708 : : * @error:
709 : : *
710 : : * Impl @impl on @connection.
711 : : */
712 : : gboolean
713 : 5 : valent_mpris_impl_export (ValentMPRISImpl *impl,
714 : : GDBusConnection *connection,
715 : : GError **error)
716 : : {
717 [ + - ]: 5 : g_return_val_if_fail (VALENT_IS_MPRIS_IMPL (impl), FALSE);
718 [ + - + - : 5 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
- + - - ]
719 [ + - - + ]: 5 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
720 : :
721 [ - + ]: 5 : if (impl->connection == connection)
722 : : return TRUE;
723 : :
724 : : /* Unexport from any existing connection */
725 : 5 : valent_mpris_impl_unexport (impl);
726 : 5 : impl->connection = g_object_ref (connection);
727 : :
728 : : /* Register org.mpris.MediaPlayer2 interface */
729 [ + - ]: 5 : if (impl->application_id == 0)
730 : : {
731 : 10 : impl->application_id =
732 : 5 : g_dbus_connection_register_object (impl->connection,
733 : : "/org/mpris/MediaPlayer2",
734 : : VALENT_MPRIS_APPLICATION_INFO,
735 : 5 : &impl->application_vtable,
736 : : impl, NULL,
737 : : error);
738 : :
739 [ - + ]: 5 : if (impl->application_id == 0)
740 : : {
741 : 0 : valent_mpris_impl_unexport (impl);
742 : 0 : return FALSE;
743 : : }
744 : : }
745 : :
746 : : /* Register org.mpris.MediaPlayer2.Player interface */
747 [ + - ]: 5 : if (impl->player_id == 0)
748 : : {
749 : 10 : impl->player_id =
750 : 5 : g_dbus_connection_register_object (impl->connection,
751 : : "/org/mpris/MediaPlayer2",
752 : : VALENT_MPRIS_PLAYER_INFO,
753 : 5 : &impl->player_vtable,
754 : : impl, NULL,
755 : : error);
756 : :
757 [ - + ]: 5 : if (impl->player_id == 0)
758 : : {
759 : 0 : valent_mpris_impl_unexport (impl);
760 : 0 : return FALSE;
761 : : }
762 : : }
763 : :
764 : : /* Own a well-known name on the connection */
765 [ - + ]: 5 : if (impl->bus_name_id == 0)
766 : : {
767 : 5 : impl->bus_name_id =
768 : 5 : g_bus_own_name_on_connection (impl->connection,
769 : 5 : impl->bus_name,
770 : : G_BUS_NAME_OWNER_FLAGS_NONE,
771 : : NULL, // NameAcquired
772 : : NULL, // NameLost
773 : : NULL,
774 : : NULL);
775 : : }
776 : :
777 : : return TRUE;
778 : : }
779 : :
780 : : static void
781 : 5 : valent_mpris_impl_export_full_cb (GObject *object,
782 : : GAsyncResult *result,
783 : : gpointer user_data)
784 : : {
785 : 5 : g_autoptr (GTask) task = G_TASK (user_data);
786 : 5 : ValentMPRISImpl *self = g_task_get_source_object (task);
787 [ + - - - ]: 5 : g_autoptr (GDBusConnection) connection = NULL;
788 [ - - ]: 5 : g_autoptr (GError) error = NULL;
789 : :
790 : 5 : connection = g_dbus_connection_new_for_address_finish (result, &error);
791 : :
792 [ - + ]: 5 : if (connection == NULL)
793 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
794 : :
795 [ - + ]: 5 : if (!valent_mpris_impl_export (self, connection, &error))
796 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
797 : :
798 [ - + ]: 5 : g_task_return_boolean (task, TRUE);
799 : : }
800 : :
801 : : /**
802 : : * valent_mpris_impl_export_full:
803 : : * @impl: a `ValentMPRISImpl`
804 : : * @bus_name: the well-known name to own
805 : : * @cancellable: (nullable): a `GCancellable`
806 : : * @callback: (scope async): a `GAsyncReadyCallback`
807 : : * @user_data: user supplied data
808 : : *
809 : : * Impl the test media player on the session bus.
810 : : */
811 : : void
812 : 5 : valent_mpris_impl_export_full (ValentMPRISImpl *impl,
813 : : const char *bus_name,
814 : : GCancellable *cancellable,
815 : : GAsyncReadyCallback callback,
816 : : gpointer user_data)
817 : : {
818 : 5 : g_autoptr (GTask) task = NULL;
819 [ - - ]: 5 : g_autofree char *address = NULL;
820 : 5 : g_autoptr (GError) error = NULL;
821 : :
822 [ + - ]: 5 : g_return_if_fail (VALENT_IS_MPRIS_IMPL (impl));
823 [ - + ]: 5 : g_return_if_fail (g_dbus_is_name (bus_name));
824 [ + + + - : 5 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
825 : :
826 : 5 : task = g_task_new (impl, cancellable, callback, user_data);
827 [ + - ]: 5 : g_task_set_source_tag (task, valent_mpris_impl_export_full);
828 : :
829 : : /* Set the new bus name */
830 : 5 : g_set_str (&impl->bus_name, bus_name);
831 : :
832 : : /* Set up a dedicated connection */
833 : 5 : address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION,
834 : : cancellable,
835 : : &error);
836 : :
837 [ - + ]: 5 : if (address == NULL)
838 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
839 : :
840 [ - + ]: 5 : g_dbus_connection_new_for_address (address,
841 : : G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
842 : : G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
843 : : NULL,
844 : : cancellable,
845 : : (GAsyncReadyCallback)valent_mpris_impl_export_full_cb,
846 : : g_steal_pointer (&task));
847 : : }
848 : :
849 : : /**
850 : : * valent_mpris_impl_export_finish:
851 : : * @impl: a `ValentMPRISImpl`
852 : : * @cancellable: (nullable): a `GCancellable`
853 : : * @error: (nullable): a `GError`
854 : : *
855 : : * Finish an operation started by valent_mpris_impl_export_full().
856 : : *
857 : : * Returns: %TRUE if successful, or %FALSE with @error set
858 : : */
859 : : gboolean
860 : 5 : valent_mpris_impl_export_finish (ValentMPRISImpl *impl,
861 : : GAsyncResult *result,
862 : : GError **error)
863 : : {
864 [ + - ]: 5 : g_return_val_if_fail (VALENT_IS_MPRIS_IMPL (impl), FALSE);
865 [ - + ]: 5 : g_return_val_if_fail (g_task_is_valid (result, impl), FALSE);
866 [ + - - + ]: 5 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
867 : :
868 : 5 : return g_task_propagate_boolean (G_TASK (result), error);
869 : : }
870 : :
871 : : /**
872 : : * valent_mpris_player_impl_unexport:
873 : : * @impl: a `ValentMPRISImpl`
874 : : *
875 : : * Unexport the player.
876 : : */
877 : : void
878 : 14 : valent_mpris_impl_unexport (ValentMPRISImpl *impl)
879 : : {
880 [ + - ]: 14 : g_return_if_fail (VALENT_IS_MPRIS_IMPL (impl));
881 : :
882 [ - + ]: 14 : g_clear_handle_id (&impl->flush_id, g_source_remove);
883 [ + + ]: 14 : g_clear_handle_id (&impl->bus_name_id, g_bus_unown_name);
884 : :
885 [ + + ]: 14 : if (impl->player_id > 0)
886 : : {
887 : 5 : g_dbus_connection_unregister_object (impl->connection,
888 : : impl->player_id);
889 : 5 : impl->player_id = 0;
890 : : }
891 : :
892 [ + + ]: 14 : if (impl->application_id > 0)
893 : : {
894 : 5 : g_dbus_connection_unregister_object (impl->connection,
895 : : impl->application_id);
896 : 5 : impl->application_id = 0;
897 : : }
898 : :
899 [ + + ]: 14 : g_clear_object (&impl->connection);
900 : : }
901 : :
|