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