1 /*
2 * Copyright (c) 2015-2019, 2021 gnome-mpv
3 *
4 * This file is part of Celluloid.
5 *
6 * Celluloid is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Celluloid is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Celluloid. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <gio/gio.h>
21 #include <glib.h>
22 #include <glib-object.h>
23 #include <mpv/client.h>
24 #include <string.h>
25
26 #include "celluloid-mpris-player.h"
27 #include "celluloid-common.h"
28 #include "celluloid-main-window.h"
29 #include "celluloid-mpris.h"
30 #include "celluloid-mpris-player.h"
31 #include "celluloid-mpris-gdbus.h"
32 #include "celluloid-def.h"
33
34 enum
35 {
36 PROP_0,
37 PROP_CONTROLLER,
38 N_PROPERTIES
39 };
40
41 struct _CelluloidMprisPlayer
42 {
43 CelluloidMprisModule parent;
44 CelluloidController *controller;
45 GHashTable *readonly_table;
46 guint reg_id;
47 };
48
49 struct _CelluloidMprisPlayerClass
50 {
51 CelluloidMprisModuleClass parent_class;
52 };
53
54 static void
55 register_interface(CelluloidMprisModule *module);
56
57 static void
58 unregister_interface(CelluloidMprisModule *module);
59
60 static void
61 set_property( GObject *object,
62 guint property_id,
63 const GValue *value,
64 GParamSpec *pspec );
65
66 static void
67 get_property( GObject *object,
68 guint property_id,
69 GValue *value,
70 GParamSpec *pspec );
71
72 static void
73 append_metadata_tags(GVariantBuilder *builder, GPtrArray *list);
74
75 static void
76 method_handler( GDBusConnection *connection,
77 const gchar *sender,
78 const gchar *object_path,
79 const gchar *interface_name,
80 const gchar *method_name,
81 GVariant *parameters,
82 GDBusMethodInvocation *invocation,
83 gpointer data );
84
85 static GVariant *
86 get_prop_handler( GDBusConnection *connection,
87 const gchar *sender,
88 const gchar *object_path,
89 const gchar *interface_name,
90 const gchar *property_name,
91 GError **error,
92 gpointer data );
93
94 static gboolean
95 set_prop_handler( GDBusConnection *connection,
96 const gchar *sender,
97 const gchar *object_path,
98 const gchar *interface_name,
99 const gchar *property_name,
100 GVariant *value,
101 GError **error,
102 gpointer data );
103
104 static void
105 update_playback_status(CelluloidMprisPlayer *player);
106
107 static void
108 update_playlist_state(CelluloidMprisPlayer *player);
109
110 static void
111 update_speed(CelluloidMprisPlayer *player);
112
113 static void
114 update_loop(CelluloidMprisPlayer *player);
115
116 static void
117 update_metadata(CelluloidMprisPlayer *player);
118
119 static void
120 update_volume(CelluloidMprisPlayer *player);
121
122 static void
123 idle_active_handler( GObject *object,
124 GParamSpec *pspec,
125 gpointer data );
126
127 static void
128 core_idle_handler( GObject *object,
129 GParamSpec *pspec,
130 gpointer data );
131
132 static void
133 playlist_pos_handler( GObject *object,
134 GParamSpec *pspec,
135 gpointer data );
136
137 static void
138 playlist_count_handler( GObject *object,
139 GParamSpec *pspec,
140 gpointer data );
141 static void
142 speed_handler( GObject *object,
143 GParamSpec *pspec,
144 gpointer data );
145
146 static void
147 loop_handler( GObject *object,
148 GParamSpec *pspec,
149 gpointer data );
150
151 static void
152 metadata_handler( GObject *object,
153 GParamSpec *pspec,
154 gpointer data );
155
156 static void
157 volume_handler( GObject *object,
158 GParamSpec *pspec,
159 gpointer data );
160
161 static void
162 playback_restart_handler(CelluloidModel *model, gpointer data);
163
164 static void
165 celluloid_mpris_player_class_init(CelluloidMprisPlayerClass *klass);
166
167 static void
168 celluloid_mpris_player_init(CelluloidMprisPlayer *player);
169
170 G_DEFINE_TYPE(CelluloidMprisPlayer, celluloid_mpris_player, CELLULOID_TYPE_MPRIS_MODULE);
171
172 static void
register_interface(CelluloidMprisModule * module)173 register_interface(CelluloidMprisModule *module)
174 {
175 CelluloidMprisPlayer *player = CELLULOID_MPRIS_PLAYER(module);
176 CelluloidModel *model = celluloid_controller_get_model
177 (player->controller);
178
179 GDBusConnection *conn;
180 GDBusInterfaceInfo *iface;
181 GDBusInterfaceVTable vtable;
182
183 g_object_get(module, "conn", &conn, "iface", &iface, NULL);
184
185 celluloid_mpris_module_connect_signal
186 ( module,
187 model,
188 "notify::core-idle",
189 G_CALLBACK(core_idle_handler),
190 player );
191 celluloid_mpris_module_connect_signal
192 ( module,
193 model,
194 "notify::idle-active",
195 G_CALLBACK(idle_active_handler),
196 player );
197 celluloid_mpris_module_connect_signal
198 ( module,
199 model,
200 "notify::playlist-pos",
201 G_CALLBACK(playlist_pos_handler),
202 player );
203 celluloid_mpris_module_connect_signal
204 ( module,
205 model,
206 "notify::playlist-count",
207 G_CALLBACK(playlist_count_handler),
208 player );
209 celluloid_mpris_module_connect_signal
210 ( module,
211 model,
212 "notify::speed",
213 G_CALLBACK(speed_handler),
214 player );
215 celluloid_mpris_module_connect_signal
216 ( module,
217 model,
218 "notify::loop-file",
219 G_CALLBACK(loop_handler),
220 player );
221 celluloid_mpris_module_connect_signal
222 ( module,
223 model,
224 "notify::loop-playlist",
225 G_CALLBACK(loop_handler),
226 player );
227 celluloid_mpris_module_connect_signal
228 ( module,
229 model,
230 "notify::metadata",
231 G_CALLBACK(metadata_handler),
232 player );
233 celluloid_mpris_module_connect_signal
234 ( module,
235 model,
236 "notify::volume",
237 G_CALLBACK(volume_handler),
238 player );
239 celluloid_mpris_module_connect_signal
240 ( module,
241 model,
242 "playback-restart",
243 G_CALLBACK(playback_restart_handler),
244 player );
245
246 celluloid_mpris_module_set_properties
247 ( module,
248 "PlaybackStatus", g_variant_new_string("Stopped"),
249 "LoopStatus", g_variant_new_string("None"),
250 "Rate", g_variant_new_double(1.0),
251 "Metadata", g_variant_new("a{sv}", NULL),
252 "Volume", g_variant_new_double(1.0),
253 "MinimumRate", g_variant_new_double(0.01),
254 "MaximumRate", g_variant_new_double(100.0),
255 "CanGoNext", g_variant_new_boolean(FALSE),
256 "CanGoPrevious", g_variant_new_boolean(FALSE),
257 "CanPlay", g_variant_new_boolean(TRUE),
258 "CanPause", g_variant_new_boolean(TRUE),
259 "CanSeek", g_variant_new_boolean(FALSE),
260 "CanControl", g_variant_new_boolean(TRUE),
261 NULL );
262
263 vtable.method_call = (GDBusInterfaceMethodCallFunc)method_handler;
264 vtable.get_property = (GDBusInterfaceGetPropertyFunc)get_prop_handler;
265 vtable.set_property = (GDBusInterfaceSetPropertyFunc)set_prop_handler;
266
267 player->reg_id = g_dbus_connection_register_object
268 ( conn,
269 MPRIS_OBJ_ROOT_PATH,
270 iface,
271 &vtable,
272 player,
273 NULL,
274 NULL );
275
276 update_playback_status(player);
277 update_playlist_state(player);
278 update_speed(player);
279 update_metadata(player);
280 update_volume(player);
281 }
282
283 static void
unregister_interface(CelluloidMprisModule * module)284 unregister_interface(CelluloidMprisModule *module)
285 {
286 CelluloidMprisPlayer *player = CELLULOID_MPRIS_PLAYER(module);
287 GDBusConnection *conn = NULL;
288
289 g_object_get(module, "conn", &conn, NULL);
290 g_dbus_connection_unregister_object(conn, player->reg_id);
291 }
292
293 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)294 set_property( GObject *object,
295 guint property_id,
296 const GValue *value,
297 GParamSpec *pspec )
298 {
299 CelluloidMprisPlayer *self = CELLULOID_MPRIS_PLAYER(object);
300
301 switch(property_id)
302 {
303 case PROP_CONTROLLER:
304 self->controller = g_value_get_pointer(value);
305 break;
306
307 default:
308 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
309 break;
310 }
311 }
312
313 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)314 get_property( GObject *object,
315 guint property_id,
316 GValue *value,
317 GParamSpec *pspec )
318 {
319 CelluloidMprisPlayer *self = CELLULOID_MPRIS_PLAYER(object);
320
321 switch(property_id)
322 {
323 case PROP_CONTROLLER:
324 g_value_set_pointer(value, self->controller);
325 break;
326
327 default:
328 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
329 break;
330 }
331 }
332
333 static void
append_metadata_tags(GVariantBuilder * builder,GPtrArray * list)334 append_metadata_tags(GVariantBuilder *builder, GPtrArray *list)
335 {
336 const struct
337 {
338 const gchar *mpv_name;
339 const gchar *tag_name;
340 const gboolean is_array;
341 }
342 tag_map[] = { {"Album", "xesam:album", FALSE},
343 {"Album_Artist", "xesam:albumArtist", TRUE},
344 {"Artist", "xesam:artist", TRUE},
345 {"Comment", "xesam:comment", TRUE},
346 {"Composer", "xesam:composer", FALSE},
347 {"Genre", "xesam:genre", TRUE},
348 {"Title", "xesam:title", FALSE},
349 {NULL, NULL, FALSE} };
350
351 const guint list_len = list?list->len:0;
352
353 for(guint i = 0; i < list_len; i++)
354 {
355 const gchar *tag_name;
356 GVariant *tag_value;
357 CelluloidMetadataEntry *entry = g_ptr_array_index(list, i);
358 gboolean is_array = TRUE;
359 gint j = -1;
360
361 /* Translate applicable mpv tag names to MPRIS2-compatible tag
362 * names.
363 */
364 while( tag_map[++j].mpv_name &&
365 g_ascii_strcasecmp(entry->key, tag_map[j].mpv_name) != 0 );
366 tag_name = tag_map[j].mpv_name?tag_map[j].tag_name:entry->key;
367 is_array = tag_map[j].mpv_name?tag_map[j].is_array:FALSE;
368
369 if(is_array)
370 {
371 GVariantBuilder tag_builder;
372 GVariant *elem_value;
373
374 elem_value = g_variant_new_string(entry->value);
375
376 g_variant_builder_init
377 (&tag_builder, G_VARIANT_TYPE("as"));
378 g_variant_builder_add_value
379 (&tag_builder, elem_value);
380
381 tag_value = g_variant_new("as", &tag_builder);
382 }
383 else
384 {
385 tag_value = g_variant_new_string(entry->value);
386 }
387
388 g_debug("Adding metadata tag \"%s\"", tag_name);
389 g_variant_builder_add(builder, "{sv}", tag_name, tag_value);
390 }
391 }
392
393 static void
method_handler(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer data)394 method_handler( GDBusConnection *connection,
395 const gchar *sender,
396 const gchar *object_path,
397 const gchar *interface_name,
398 const gchar *method_name,
399 GVariant *parameters,
400 GDBusMethodInvocation *invocation,
401 gpointer data )
402 {
403 CelluloidMprisPlayer *player = data;
404 CelluloidModel *model = celluloid_controller_get_model
405 (player->controller);
406 gboolean unknown_method = FALSE;
407
408 if(g_strcmp0(method_name, "Next") == 0)
409 {
410 celluloid_model_next_playlist_entry(model);
411 }
412 else if(g_strcmp0(method_name, "Previous") == 0)
413 {
414 celluloid_model_previous_playlist_entry(model);
415 }
416 else if(g_strcmp0(method_name, "Pause") == 0)
417 {
418 celluloid_model_pause(model);
419 }
420 else if(g_strcmp0(method_name, "PlayPause") == 0)
421 {
422 gboolean pause = FALSE;
423
424 g_object_get(model, "pause", &pause, NULL);
425 g_object_set(model, "pause", !pause, NULL);
426 }
427 else if(g_strcmp0(method_name, "Stop") == 0)
428 {
429 celluloid_model_stop(model);
430 }
431 else if(g_strcmp0(method_name, "Play") == 0)
432 {
433 celluloid_model_play(model);
434 }
435 else if(g_strcmp0(method_name, "Seek") == 0)
436 {
437 gint64 offset_us;
438
439 g_variant_get(parameters, "(x)", &offset_us);
440 celluloid_model_seek_offset(model, (gdouble)offset_us/1.0e6);
441 }
442 else if(g_strcmp0(method_name, "SetPosition") == 0)
443 {
444 const gchar *prefix = MPRIS_TRACK_ID_PREFIX;
445 const gsize prefix_len = strlen(prefix);
446 gint64 time_us = -1;
447 const gchar *track_id = NULL;
448
449 g_variant_get(parameters, "(&ox)", &track_id, &time_us);
450
451 if(strncmp(track_id, prefix, prefix_len) == 0)
452 {
453 gint64 index = g_ascii_strtoll
454 (track_id+prefix_len, NULL, 0);
455
456 celluloid_model_set_playlist_position(model, index);
457 celluloid_model_seek(model, (gdouble)time_us/1.0e6);
458 }
459 }
460 else if(g_strcmp0(method_name, "OpenUri") == 0)
461 {
462 const gchar *uri;
463
464 g_variant_get(parameters, "(&s)", &uri);
465 celluloid_model_load_file(model, uri, FALSE);
466 }
467 else
468 {
469 unknown_method = TRUE;
470 }
471
472 if(unknown_method)
473 {
474 g_dbus_method_invocation_return_error
475 ( invocation,
476 CELLULOID_MPRIS_ERROR,
477 CELLULOID_MPRIS_ERROR_UNKNOWN_METHOD,
478 "Attempted to call unknown method \"%s\"",
479 method_name );
480 }
481 else
482 {
483 g_dbus_method_invocation_return_value
484 (invocation, g_variant_new("()", NULL));
485 }
486 }
487
488 static GVariant *
get_prop_handler(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GError ** error,gpointer data)489 get_prop_handler( GDBusConnection *connection,
490 const gchar *sender,
491 const gchar *object_path,
492 const gchar *interface_name,
493 const gchar *property_name,
494 GError **error,
495 gpointer data )
496 {
497 CelluloidMprisPlayer *player = CELLULOID_MPRIS_PLAYER(data);
498 CelluloidMprisModule *module = CELLULOID_MPRIS_MODULE(data);
499 GVariant *value = NULL;
500
501 if(!g_hash_table_contains(player->readonly_table, property_name))
502 {
503 g_set_error
504 ( error,
505 CELLULOID_MPRIS_ERROR,
506 CELLULOID_MPRIS_ERROR_UNKNOWN_PROPERTY,
507 "Failed to get value of unknown property \"%s\"",
508 property_name );
509 }
510 else if(g_strcmp0(property_name, "Position") == 0)
511 {
512 CelluloidModel *model;
513 gdouble position;
514
515 model = celluloid_controller_get_model(player->controller);
516 position = celluloid_model_get_time_position(model);
517 value = g_variant_new_int64((gint64)(position*1e6));
518 }
519 else
520 {
521 celluloid_mpris_module_get_properties
522 ( module,
523 property_name, &value,
524 NULL );
525 }
526
527 return value?g_variant_ref(value):NULL;
528 }
529
530 static gboolean
set_prop_handler(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GVariant * value,GError ** error,gpointer data)531 set_prop_handler( GDBusConnection *connection,
532 const gchar *sender,
533 const gchar *object_path,
534 const gchar *interface_name,
535 const gchar *property_name,
536 GVariant *value,
537 GError **error,
538 gpointer data )
539 {
540 CelluloidMprisPlayer *player = CELLULOID_MPRIS_PLAYER(data);
541 CelluloidModel *model = celluloid_controller_get_model
542 (player->controller);
543 gboolean result = TRUE;
544
545 if(!g_hash_table_contains(player->readonly_table, property_name))
546 {
547 result = FALSE;
548
549 g_set_error
550 ( error,
551 CELLULOID_MPRIS_ERROR,
552 CELLULOID_MPRIS_ERROR_UNKNOWN_PROPERTY,
553 "Failed to set value of unknown property \"%s\"",
554 property_name );
555 }
556 else if(GPOINTER_TO_INT(g_hash_table_lookup(player->readonly_table, property_name)))
557 {
558 g_set_error
559 ( error,
560 CELLULOID_MPRIS_ERROR,
561 CELLULOID_MPRIS_ERROR_SET_READONLY,
562 "Attempted to set value of readonly property \"%s\"",
563 property_name );
564 }
565 else if(g_strcmp0(property_name, "LoopStatus") == 0)
566 {
567 const gchar *loop = g_variant_get_string(value, NULL);
568 const gchar *loop_file = g_strcmp0(loop, "Track") == 0 ?
569 "inf":"no";
570 const gchar *loop_playlist = g_strcmp0(loop, "Playlist") == 0 ?
571 "inf":"no";
572
573 g_object_set( G_OBJECT(model),
574 "loop-file", loop_file,
575 "loop-playlist", loop_playlist,
576 NULL );
577 }
578 else if(g_strcmp0(property_name, "Rate") == 0)
579 {
580 g_object_set( G_OBJECT(model),
581 "speed", g_variant_get_double(value),
582 NULL );
583 }
584 else if(g_strcmp0(property_name, "Volume") == 0)
585 {
586 g_object_set( G_OBJECT(model),
587 "volume", 100*g_variant_get_double(value),
588 NULL );
589 }
590
591 return result;
592 }
593
594 static void
update_playback_status(CelluloidMprisPlayer * player)595 update_playback_status(CelluloidMprisPlayer *player)
596 {
597 CelluloidModel *model = celluloid_controller_get_model
598 (player->controller);
599 const gchar *state;
600 gint idle_active;
601 gint core_idle;
602 gboolean can_seek;
603
604 g_object_get( G_OBJECT(model),
605 "idle-active", &idle_active,
606 "core-idle", &core_idle,
607 NULL );
608
609 if(!core_idle && !idle_active)
610 {
611 state = "Playing";
612 can_seek = TRUE;
613 }
614 else if(core_idle && idle_active)
615 {
616 state = "Stopped";
617 can_seek = FALSE;
618 }
619 else
620 {
621 state = "Paused";
622 can_seek = TRUE;
623 }
624
625 celluloid_mpris_module_set_properties( CELLULOID_MPRIS_MODULE(player),
626 "PlaybackStatus",
627 g_variant_new_string(state),
628 "CanSeek",
629 g_variant_new_boolean(can_seek),
630 NULL );
631 }
632
633 static void
update_playlist_state(CelluloidMprisPlayer * player)634 update_playlist_state(CelluloidMprisPlayer *player)
635 {
636 CelluloidModel *model = celluloid_controller_get_model
637 (player->controller);
638 gboolean can_prev;
639 gboolean can_next;
640 gint64 playlist_count;
641 gint64 playlist_pos;
642 gint rc = 0;
643
644 g_object_get( G_OBJECT(model),
645 "playlist-count", &playlist_count,
646 "playlist-pos", &playlist_pos,
647 NULL );
648
649 can_prev = (rc >= 0 && playlist_pos > 0);
650 can_next = (rc >= 0 && playlist_pos < playlist_count-1);
651
652 celluloid_mpris_module_set_properties( CELLULOID_MPRIS_MODULE(player),
653 "CanGoPrevious",
654 g_variant_new_boolean(can_prev),
655 "CanGoNext",
656 g_variant_new_boolean(can_next),
657 NULL );
658 }
659
660 static void
update_speed(CelluloidMprisPlayer * player)661 update_speed(CelluloidMprisPlayer *player)
662 {
663 CelluloidModel *model = celluloid_controller_get_model
664 (player->controller);
665 gdouble speed = 1.0;
666
667 g_object_get(G_OBJECT(model), "speed", &speed, NULL);
668
669 celluloid_mpris_module_set_properties( CELLULOID_MPRIS_MODULE(player),
670 "Rate",
671 g_variant_new_double(speed),
672 NULL );
673 }
674
675 static void
update_loop(CelluloidMprisPlayer * player)676 update_loop(CelluloidMprisPlayer *player)
677 {
678 CelluloidModel *model = celluloid_controller_get_model
679 (player->controller);
680 gchar *loop_file = NULL;
681 gchar *loop_playlist = NULL;
682 const gchar *loop = NULL;
683
684 g_object_get( model,
685 "loop-file", &loop_file,
686 "loop-playlist", &loop_playlist,
687 NULL );
688
689 loop = g_strcmp0(loop_file, "inf") == 0 ? "Track" :
690 g_strcmp0(loop_playlist, "inf") == 0 ? "Playlist" :
691 "None";
692
693 celluloid_mpris_module_set_properties( CELLULOID_MPRIS_MODULE(player),
694 "LoopStatus",
695 g_variant_new_string(loop),
696 NULL );
697
698 g_free(loop_file);
699 g_free(loop_playlist);
700 }
701
702 static void
update_metadata(CelluloidMprisPlayer * player)703 update_metadata(CelluloidMprisPlayer *player)
704 {
705 CelluloidModel *model = celluloid_controller_get_model
706 (player->controller);
707 GPtrArray *metadata = NULL;
708 GVariantBuilder builder;
709 gchar *path;
710 gchar *uri;
711 gchar *playlist_pos_str;
712 gchar *trackid;
713 gdouble duration = 0;
714 gint64 playlist_pos = 0;
715
716 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
717 path = celluloid_model_get_current_path(model)?:g_strdup("");
718 uri = g_filename_to_uri(path, NULL, NULL)?:g_strdup(path);
719
720 if(uri)
721 {
722 g_variant_builder_add( &builder,
723 "{sv}",
724 "xesam:url",
725 g_variant_new_string(uri) );
726
727 }
728
729 g_object_get( model,
730 "duration", &duration,
731 "playlist-pos", &playlist_pos,
732 "metadata", &metadata,
733 NULL );
734
735 g_variant_builder_add( &builder,
736 "{sv}",
737 "mpris:length",
738 g_variant_new_int64
739 ((gint64)(duration*1e6)) );
740
741 playlist_pos_str = g_strdup_printf("%" G_GINT64_FORMAT, playlist_pos);
742 trackid = g_strconcat( MPRIS_TRACK_ID_PREFIX,
743 playlist_pos_str,
744 NULL );
745 g_variant_builder_add( &builder,
746 "{sv}",
747 "mpris:trackid",
748 g_variant_new_object_path(trackid) );
749
750 append_metadata_tags(&builder, metadata);
751
752 celluloid_mpris_module_set_properties( CELLULOID_MPRIS_MODULE(player),
753 "Metadata",
754 g_variant_new("a{sv}", &builder),
755 NULL );
756
757 g_free(path);
758 g_free(uri);
759 g_free(playlist_pos_str);
760 g_free(trackid);
761 }
762
763 static void
update_volume(CelluloidMprisPlayer * player)764 update_volume(CelluloidMprisPlayer *player)
765 {
766 CelluloidModel *model = celluloid_controller_get_model
767 (player->controller);
768 gdouble volume = 0.0;
769
770 g_object_get(G_OBJECT(model), "volume", &volume, NULL);
771
772 celluloid_mpris_module_set_properties
773 ( CELLULOID_MPRIS_MODULE(player),
774 "Volume",
775 g_variant_new_double(volume/100.0),
776 NULL );
777 }
778
779 static void
idle_active_handler(GObject * object,GParamSpec * pspec,gpointer data)780 idle_active_handler( GObject *object,
781 GParamSpec *pspec,
782 gpointer data )
783 {
784 update_playback_status(data);
785 }
786
787 static void
core_idle_handler(GObject * object,GParamSpec * pspec,gpointer data)788 core_idle_handler( GObject *object,
789 GParamSpec *pspec,
790 gpointer data )
791 {
792 update_playback_status(data);
793 }
794
795 static void
playlist_pos_handler(GObject * object,GParamSpec * pspec,gpointer data)796 playlist_pos_handler( GObject *object,
797 GParamSpec *pspec,
798 gpointer data )
799 {
800 update_playlist_state(data);
801 }
802
803 static void
playlist_count_handler(GObject * object,GParamSpec * pspec,gpointer data)804 playlist_count_handler( GObject *object,
805 GParamSpec *pspec,
806 gpointer data )
807 {
808 update_playlist_state(data);
809 }
810
811 static void
speed_handler(GObject * object,GParamSpec * pspec,gpointer data)812 speed_handler( GObject *object,
813 GParamSpec *pspec,
814 gpointer data )
815 {
816 update_speed(data);
817 }
818
819 static void
loop_handler(GObject * object,GParamSpec * pspec,gpointer data)820 loop_handler( GObject *object,
821 GParamSpec *pspec,
822 gpointer data )
823 {
824 update_loop(data);
825 }
826
827 static void
metadata_handler(GObject * object,GParamSpec * pspec,gpointer data)828 metadata_handler( GObject *object,
829 GParamSpec *pspec,
830 gpointer data )
831 {
832 update_metadata(data);
833 }
834
835 static void
volume_handler(GObject * object,GParamSpec * pspec,gpointer data)836 volume_handler( GObject *object,
837 GParamSpec *pspec,
838 gpointer data )
839 {
840 update_volume(data);
841 }
842
843 static void
playback_restart_handler(CelluloidModel * model,gpointer data)844 playback_restart_handler(CelluloidModel *model, gpointer data)
845 {
846 GDBusConnection *conn;
847 GDBusInterfaceInfo *iface;
848 gdouble position;
849
850 position = celluloid_model_get_time_position(model);
851
852 g_object_get( CELLULOID_MPRIS_MODULE(data),
853 "conn", &conn,
854 "iface", &iface,
855 NULL );
856
857 g_dbus_connection_emit_signal
858 ( conn,
859 NULL,
860 MPRIS_OBJ_ROOT_PATH,
861 iface->name,
862 "Seeked",
863 g_variant_new("(x)", (gint64)(position*1e6)),
864 NULL );
865 }
866
867 static void
celluloid_mpris_player_class_init(CelluloidMprisPlayerClass * klass)868 celluloid_mpris_player_class_init(CelluloidMprisPlayerClass *klass)
869 {
870 CelluloidMprisModuleClass *module_class =
871 CELLULOID_MPRIS_MODULE_CLASS(klass);
872 GObjectClass *object_class = G_OBJECT_CLASS(klass);
873 GParamSpec *pspec = NULL;
874
875 module_class->register_interface = register_interface;
876 module_class->unregister_interface = unregister_interface;
877 object_class->set_property = set_property;
878 object_class->get_property = get_property;
879
880 pspec = g_param_spec_pointer
881 ( "controller",
882 "Controller",
883 "The CelluloidApplication to use",
884 G_PARAM_CONSTRUCT_ONLY|G_PARAM_READWRITE );
885 g_object_class_install_property(object_class, PROP_CONTROLLER, pspec);
886 }
887
888 static void
celluloid_mpris_player_init(CelluloidMprisPlayer * player)889 celluloid_mpris_player_init(CelluloidMprisPlayer *player)
890 {
891 const struct
892 {
893 const gchar *name;
894 gboolean readonly;
895 }
896 properties[] =
897 {
898 {"PlaybackStatus", TRUE},
899 {"LoopStatus", FALSE},
900 {"Rate", FALSE},
901 {"Metadata", TRUE},
902 {"Volume", FALSE},
903 {"MinimumRate", TRUE},
904 {"MaximumRate", TRUE},
905 {"CanGoNext", TRUE},
906 {"CanGoPrevious", TRUE},
907 {"CanPlay", TRUE},
908 {"CanPause", TRUE},
909 {"CanSeek", TRUE},
910 {"CanControl", TRUE},
911 {NULL, FALSE}
912 };
913
914 player->controller =
915 NULL;
916 player->readonly_table =
917 g_hash_table_new_full(g_str_hash, g_int_equal, g_free, NULL);
918 player->reg_id =
919 0;
920
921 for(gint i = 0; properties[i].name; i++)
922 {
923 g_hash_table_replace
924 ( player->readonly_table,
925 g_strdup(properties[i].name),
926 GINT_TO_POINTER(properties[i].readonly) );
927 }
928 }
929
930 CelluloidMprisModule *
celluloid_mpris_player_new(CelluloidController * controller,GDBusConnection * conn)931 celluloid_mpris_player_new( CelluloidController *controller,
932 GDBusConnection *conn )
933 {
934 GType type;
935 GDBusInterfaceInfo *iface;
936
937 type = celluloid_mpris_player_get_type();
938 iface = celluloid_mpris_org_mpris_media_player2_player_interface_info();
939
940 return CELLULOID_MPRIS_MODULE(g_object_new( type,
941 "controller", controller,
942 "conn", conn,
943 "iface", iface,
944 NULL ));
945 }
946