]> git.sesse.net Git - vlc/blob - modules/control/dbus.c
Include vlc_plugin.h as needed
[vlc] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright © 2006-2008 Rafaël Carré
5  * Copyright © 2007-2008 Mirsal Ennaime
6  * $Id$
7  *
8  * Authors:    Rafaël Carré <funman at videolanorg>
9  *             Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*
27  * D-Bus Specification:
28  *      http://dbus.freedesktop.org/doc/dbus-specification.html
29  * D-Bus low-level C API (libdbus)
30  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
31  *  extract:
32  *   "If you use this low-level API directly, you're signing up for some pain."
33  *
34  * MPRIS Specification (still drafting on Jan, 23 of 2008):
35  *      http://wiki.xmms2.xmms.se/index.php/MPRIS
36  */
37
38 /*****************************************************************************
39  * Preamble
40  *****************************************************************************/
41
42 #include <dbus/dbus.h>
43
44 #include "dbus.h"
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <vlc/vlc.h>
51 #include <vlc_plugin.h>
52 #include <vlc_aout.h>
53 #include <vlc_interface.h>
54 #include <vlc_meta.h>
55 #include <vlc_input.h>
56 #include <vlc_playlist.h>
57
58 #include <math.h>
59
60 /*****************************************************************************
61  * Local prototypes.
62  *****************************************************************************/
63
64 static int  Open    ( vlc_object_t * );
65 static void Close   ( vlc_object_t * );
66 static void Run     ( intf_thread_t * );
67
68 static int StateChange( vlc_object_t *, const char *, vlc_value_t,
69                         vlc_value_t, void * );
70
71 static int TrackChange( vlc_object_t *, const char *, vlc_value_t,
72                         vlc_value_t, void * );
73
74 static int StatusChangeEmit( vlc_object_t *, const char *, vlc_value_t,
75                         vlc_value_t, void * );
76
77 static int TrackListChangeEmit( vlc_object_t *, const char *, vlc_value_t,
78                         vlc_value_t, void * );
79
80 static int GetInputMeta ( input_item_t *, DBusMessageIter * );
81 static int MarshalStatus ( intf_thread_t *, DBusMessageIter *, bool );
82 static int UpdateCaps( intf_thread_t*, bool );
83
84 /* GetCaps() capabilities */
85 enum
86 {
87      CAPS_NONE                  = 0,
88      CAPS_CAN_GO_NEXT           = 1 << 0,
89      CAPS_CAN_GO_PREV           = 1 << 1,
90      CAPS_CAN_PAUSE             = 1 << 2,
91      CAPS_CAN_PLAY              = 1 << 3,
92      CAPS_CAN_SEEK              = 1 << 4,
93      CAPS_CAN_PROVIDE_METADATA  = 1 << 5,
94      CAPS_CAN_HAS_TRACKLIST     = 1 << 6
95 };
96
97 struct intf_sys_t
98 {
99     DBusConnection *p_conn;
100     bool      b_meta_read;
101     dbus_int32_t    i_caps;
102 };
103
104 /*****************************************************************************
105  * Module descriptor
106  *****************************************************************************/
107
108 vlc_module_begin();
109     set_shortname( _("dbus"));
110     set_category( CAT_INTERFACE );
111     set_subcategory( SUBCAT_INTERFACE_CONTROL );
112     set_description( _("D-Bus control interface") );
113     set_capability( "interface", 0 );
114     set_callbacks( Open, Close );
115 vlc_module_end();
116
117 /*****************************************************************************
118  * Methods
119  *****************************************************************************/
120
121 /* Player */
122
123 DBUS_METHOD( Quit )
124 { /* exits vlc */
125     REPLY_INIT;
126     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
127     playlist_Stop( p_playlist );
128     pl_Release( ((vlc_object_t*) p_this) );
129     vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
130     REPLY_SEND;
131 }
132
133 DBUS_METHOD( MprisVersion )
134 { /*implemented version of the mpris spec */
135     REPLY_INIT;
136     OUT_ARGUMENTS;
137     VLC_UNUSED( p_this );
138     dbus_uint16_t i_major = VLC_MPRIS_VERSION_MAJOR;
139     dbus_uint16_t i_minor = VLC_MPRIS_VERSION_MINOR;
140     DBusMessageIter version;
141
142     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_STRUCT, NULL,
143             &version ) )
144         return DBUS_HANDLER_RESULT_NEED_MEMORY;
145
146     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
147             &i_major ) )
148         return DBUS_HANDLER_RESULT_NEED_MEMORY;
149
150     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
151             &i_minor ) )
152         return DBUS_HANDLER_RESULT_NEED_MEMORY;
153
154     if( !dbus_message_iter_close_container( &args, &version ) )
155         return DBUS_HANDLER_RESULT_NEED_MEMORY;
156     REPLY_SEND;
157 }
158
159 DBUS_METHOD( PositionGet )
160 { /* returns position in milliseconds */
161     REPLY_INIT;
162     OUT_ARGUMENTS;
163     vlc_value_t position;
164     dbus_int32_t i_pos;
165
166     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
167     PL_LOCK;
168     input_thread_t *p_input = p_playlist->p_input;
169
170     if( !p_input )
171         i_pos = 0;
172     else
173     {
174         var_Get( p_input, "time", &position );
175         i_pos = position.i_time / 1000;
176     }
177     PL_UNLOCK;
178     pl_Release( ((vlc_object_t*) p_this) );
179     ADD_INT32( &i_pos );
180     REPLY_SEND;
181 }
182
183 DBUS_METHOD( PositionSet )
184 { /* set position in milliseconds */
185
186     REPLY_INIT;
187     vlc_value_t position;
188     playlist_t* p_playlist = NULL;
189     dbus_int32_t i_pos;
190
191     DBusError error;
192     dbus_error_init( &error );
193
194     dbus_message_get_args( p_from, &error,
195             DBUS_TYPE_INT32, &i_pos,
196             DBUS_TYPE_INVALID );
197
198     if( dbus_error_is_set( &error ) )
199     {
200         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
201                 error.message );
202         dbus_error_free( &error );
203         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
204     }
205     p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
206     PL_LOCK;
207     input_thread_t *p_input = p_playlist->p_input;
208
209     if( p_input )
210     {
211         position.i_time = i_pos * 1000;
212         var_Set( p_input, "time", position );
213     }
214     PL_UNLOCK;
215     pl_Release( ((vlc_object_t*) p_this) );
216     REPLY_SEND;
217 }
218
219 DBUS_METHOD( VolumeGet )
220 { /* returns volume in percentage */
221     REPLY_INIT;
222     OUT_ARGUMENTS;
223     dbus_int32_t i_dbus_vol;
224     audio_volume_t i_vol;
225     /* 2nd argument of aout_VolumeGet is int32 */
226     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
227     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
228     i_dbus_vol = round( f_vol );
229     ADD_INT32( &i_dbus_vol );
230     REPLY_SEND;
231 }
232
233 DBUS_METHOD( VolumeSet )
234 { /* set volume in percentage */
235     REPLY_INIT;
236
237     DBusError error;
238     dbus_error_init( &error );
239
240     dbus_int32_t i_dbus_vol;
241     audio_volume_t i_vol;
242
243     dbus_message_get_args( p_from, &error,
244             DBUS_TYPE_INT32, &i_dbus_vol,
245             DBUS_TYPE_INVALID );
246
247     if( dbus_error_is_set( &error ) )
248     {
249         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
250                 error.message );
251         dbus_error_free( &error );
252         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
253     }
254
255     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
256     i_vol = round( f_vol );
257     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
258
259     REPLY_SEND;
260 }
261
262 DBUS_METHOD( Next )
263 { /* next playlist item */
264     REPLY_INIT;
265     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
266     playlist_Next( p_playlist );
267     pl_Release( ((vlc_object_t*) p_this) );
268     REPLY_SEND;
269 }
270
271 DBUS_METHOD( Prev )
272 { /* previous playlist item */
273     REPLY_INIT;
274     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
275     playlist_Prev( p_playlist );
276     pl_Release( ((vlc_object_t*) p_this) );
277     REPLY_SEND;
278 }
279
280 DBUS_METHOD( Stop )
281 { /* stop playing */
282     REPLY_INIT;
283     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
284     playlist_Stop( p_playlist );
285     pl_Release( ((vlc_object_t*) p_this) );
286     REPLY_SEND;
287 }
288
289 DBUS_METHOD( GetStatus )
290 { /* returns the current status as a struct of 4 ints */
291 /*
292     First   0 = Playing, 1 = Paused, 2 = Stopped.
293     Second  0 = Playing linearly , 1 = Playing randomly.
294     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
295     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
296  */
297     REPLY_INIT;
298     OUT_ARGUMENTS;
299
300     MarshalStatus( p_this, &args, true );
301
302     REPLY_SEND;
303 }
304
305 DBUS_METHOD( Pause )
306 {
307     REPLY_INIT;
308     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
309     playlist_Pause( p_playlist );
310     pl_Release( p_playlist );
311     REPLY_SEND;
312 }
313
314 DBUS_METHOD( Play )
315 {
316     REPLY_INIT;
317     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
318     playlist_Play( p_playlist );
319     pl_Release( p_playlist );
320     REPLY_SEND;
321 }
322
323 DBUS_METHOD( GetCurrentMetadata )
324 {
325     REPLY_INIT;
326     OUT_ARGUMENTS;
327     playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );
328     PL_LOCK;
329     if( p_playlist->status.p_item )
330         GetInputMeta( p_playlist->status.p_item->p_input, &args );
331     PL_UNLOCK;
332     pl_Release( p_playlist );
333     REPLY_SEND;
334 }
335
336 DBUS_METHOD( GetCaps )
337 {
338     REPLY_INIT;
339     OUT_ARGUMENTS;
340
341     ADD_INT32( &((intf_thread_t*)p_this)->p_sys->i_caps );
342
343     REPLY_SEND;
344 }
345
346 /* Media Player information */
347
348 DBUS_METHOD( Identity )
349 {
350     VLC_UNUSED(p_this);
351     REPLY_INIT;
352     OUT_ARGUMENTS;
353     char *psz_identity;
354     if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 )
355     {
356         ADD_STRING( &psz_identity );
357         free( psz_identity );
358     }
359     else
360         return DBUS_HANDLER_RESULT_NEED_MEMORY;
361
362     REPLY_SEND;
363 }
364
365 /* TrackList */
366
367 DBUS_METHOD( AddTrack )
368 { /* add the string to the playlist, and play it if the boolean is true */
369     REPLY_INIT;
370
371     DBusError error;
372     dbus_error_init( &error );
373     playlist_t* p_playlist = NULL;
374
375     char *psz_mrl;
376     dbus_bool_t b_play;
377
378     dbus_message_get_args( p_from, &error,
379             DBUS_TYPE_STRING, &psz_mrl,
380             DBUS_TYPE_BOOLEAN, &b_play,
381             DBUS_TYPE_INVALID );
382
383     if( dbus_error_is_set( &error ) )
384     {
385         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
386                 error.message );
387         dbus_error_free( &error );
388         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
389     }
390
391     p_playlist = pl_Yield( (vlc_object_t*) p_this );
392     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
393             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
394             PLAYLIST_END, true, false );
395     pl_Release( p_playlist );
396
397     REPLY_SEND;
398 }
399
400 DBUS_METHOD( GetCurrentTrack )
401 {
402     REPLY_INIT;
403     OUT_ARGUMENTS;
404
405     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
406     dbus_int32_t i_position = p_playlist->i_current_index;
407     pl_Release( p_playlist );
408
409     ADD_INT32( &i_position );
410     REPLY_SEND;
411 }
412
413 DBUS_METHOD( GetMetadata )
414 {
415     REPLY_INIT;
416     OUT_ARGUMENTS;
417     DBusError error;
418     dbus_error_init( &error );
419
420     dbus_int32_t i_position;
421
422     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
423     PL_LOCK;
424
425     dbus_message_get_args( p_from, &error,
426            DBUS_TYPE_INT32, &i_position,
427            DBUS_TYPE_INVALID );
428
429     if( dbus_error_is_set( &error ) )
430     {
431         PL_UNLOCK;
432         pl_Release( p_playlist );
433         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
434                 error.message );
435         dbus_error_free( &error );
436         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
437     }
438
439     if( i_position < p_playlist->current.i_size )
440     {
441         GetInputMeta( p_playlist->current.p_elems[i_position]->p_input, &args );
442     }
443
444     PL_UNLOCK;
445     pl_Release( p_playlist );
446     REPLY_SEND;
447 }
448
449 DBUS_METHOD( GetLength )
450 {
451     REPLY_INIT;
452     OUT_ARGUMENTS;
453
454     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
455     dbus_int32_t i_elements = p_playlist->current.i_size;
456     pl_Release( p_playlist );
457
458     ADD_INT32( &i_elements );
459     REPLY_SEND;
460 }
461
462 DBUS_METHOD( DelTrack )
463 {
464     REPLY_INIT;
465
466     DBusError error;
467     dbus_error_init( &error );
468
469     dbus_int32_t i_position;
470     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
471
472     dbus_message_get_args( p_from, &error,
473             DBUS_TYPE_INT32, &i_position,
474             DBUS_TYPE_INVALID );
475
476     if( dbus_error_is_set( &error ) )
477     {
478         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
479                 error.message );
480         dbus_error_free( &error );
481         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
482     }
483
484     PL_LOCK;
485     if( i_position < p_playlist->current.i_size )
486     {
487         playlist_DeleteFromInput( p_playlist,
488             p_playlist->current.p_elems[i_position]->p_input->i_id,
489             true );
490     }
491     PL_UNLOCK;
492
493     pl_Release( p_playlist );
494
495     REPLY_SEND;
496 }
497
498 DBUS_METHOD( SetLoop )
499 {
500     REPLY_INIT;
501     OUT_ARGUMENTS;
502
503     DBusError error;
504     dbus_bool_t b_loop;
505     vlc_value_t val;
506     playlist_t* p_playlist = NULL;
507
508     dbus_error_init( &error );
509     dbus_message_get_args( p_from, &error,
510             DBUS_TYPE_BOOLEAN, &b_loop,
511             DBUS_TYPE_INVALID );
512
513     if( dbus_error_is_set( &error ) )
514     {
515         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
516                 error.message );
517         dbus_error_free( &error );
518         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
519     }
520
521     val.b_bool = ( b_loop == TRUE ) ? true : false ;
522     p_playlist = pl_Yield( (vlc_object_t*) p_this );
523     var_Set ( p_playlist, "loop", val );
524     pl_Release( ((vlc_object_t*) p_this) );
525
526     REPLY_SEND;
527 }
528
529 DBUS_METHOD( Repeat )
530 {
531     REPLY_INIT;
532     OUT_ARGUMENTS;
533
534     DBusError error;
535     dbus_bool_t b_repeat;
536     vlc_value_t val;
537     playlist_t* p_playlist = NULL;
538
539     dbus_error_init( &error );
540     dbus_message_get_args( p_from, &error,
541             DBUS_TYPE_BOOLEAN, &b_repeat,
542             DBUS_TYPE_INVALID );
543
544     if( dbus_error_is_set( &error ) )
545     {
546         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
547                 error.message );
548         dbus_error_free( &error );
549         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
550     }
551
552     val.b_bool = ( b_repeat == TRUE ) ? true : false ;
553
554     p_playlist = pl_Yield( (vlc_object_t*) p_this );
555     var_Set ( p_playlist, "repeat", val );
556     pl_Release( ((vlc_object_t*) p_this) );
557
558     REPLY_SEND;
559 }
560
561 DBUS_METHOD( SetRandom )
562 {
563     REPLY_INIT;
564     OUT_ARGUMENTS;
565
566     DBusError error;
567     dbus_bool_t b_random;
568     vlc_value_t val;
569     playlist_t* p_playlist = NULL;
570
571     dbus_error_init( &error );
572     dbus_message_get_args( p_from, &error,
573             DBUS_TYPE_BOOLEAN, &b_random,
574             DBUS_TYPE_INVALID );
575
576     if( dbus_error_is_set( &error ) )
577     {
578         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
579                 error.message );
580         dbus_error_free( &error );
581         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
582     }
583
584     val.b_bool = ( b_random == TRUE ) ? true : false ;
585
586     p_playlist = pl_Yield( (vlc_object_t*) p_this );
587     var_Set ( p_playlist, "random", val );
588     pl_Release( ((vlc_object_t*) p_this) );
589
590     REPLY_SEND;
591 }
592 /*****************************************************************************
593  * Introspection method
594  *****************************************************************************/
595
596 DBUS_METHOD( handle_introspect_root )
597 { /* handles introspection of root object */
598     VLC_UNUSED(p_this);
599     REPLY_INIT;
600     OUT_ARGUMENTS;
601     ADD_STRING( &psz_introspection_xml_data_root );
602     REPLY_SEND;
603 }
604
605 DBUS_METHOD( handle_introspect_player )
606 {
607     VLC_UNUSED(p_this);
608     REPLY_INIT;
609     OUT_ARGUMENTS;
610     ADD_STRING( &psz_introspection_xml_data_player );
611     REPLY_SEND;
612 }
613
614 DBUS_METHOD( handle_introspect_tracklist )
615 {
616     VLC_UNUSED(p_this);
617     REPLY_INIT;
618     OUT_ARGUMENTS;
619     ADD_STRING( &psz_introspection_xml_data_tracklist );
620     REPLY_SEND;
621 }
622
623 /*****************************************************************************
624  * handle_*: answer to incoming messages
625  *****************************************************************************/
626
627 #define METHOD_FUNC( method, function ) \
628     else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
629         return function( p_conn, p_from, p_this )
630
631 DBUS_METHOD( handle_root )
632 {
633
634     if( dbus_message_is_method_call( p_from,
635                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
636         return handle_introspect_root( p_conn, p_from, p_this );
637
638     /* here D-Bus method's names are associated to an handler */
639
640     METHOD_FUNC( "Identity",                Identity );
641     METHOD_FUNC( "MprisVersion",            MprisVersion );
642     METHOD_FUNC( "Quit",                    Quit );
643
644     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
645 }
646
647
648 DBUS_METHOD( handle_player )
649 {
650     if( dbus_message_is_method_call( p_from,
651                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
652         return handle_introspect_player( p_conn, p_from, p_this );
653
654     /* here D-Bus method's names are associated to an handler */
655
656     METHOD_FUNC( "Prev",                    Prev );
657     METHOD_FUNC( "Next",                    Next );
658     METHOD_FUNC( "Stop",                    Stop );
659     METHOD_FUNC( "Play",                    Play );
660     METHOD_FUNC( "Pause",                   Pause );
661     METHOD_FUNC( "Repeat",                  Repeat );
662     METHOD_FUNC( "VolumeSet",               VolumeSet );
663     METHOD_FUNC( "VolumeGet",               VolumeGet );
664     METHOD_FUNC( "PositionSet",             PositionSet );
665     METHOD_FUNC( "PositionGet",             PositionGet );
666     METHOD_FUNC( "GetStatus",               GetStatus );
667     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
668     METHOD_FUNC( "GetCaps",                 GetCaps );
669
670     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
671 }
672
673 DBUS_METHOD( handle_tracklist )
674 {
675     if( dbus_message_is_method_call( p_from,
676                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
677     return handle_introspect_tracklist( p_conn, p_from, p_this );
678
679     /* here D-Bus method's names are associated to an handler */
680
681     METHOD_FUNC( "GetMetadata",             GetMetadata );
682     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
683     METHOD_FUNC( "GetLength",               GetLength );
684     METHOD_FUNC( "AddTrack",                AddTrack );
685     METHOD_FUNC( "DelTrack",                DelTrack );
686     METHOD_FUNC( "SetLoop",                 SetLoop );
687     METHOD_FUNC( "SetRandom",               SetRandom );
688
689     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
690 }
691
692 /*****************************************************************************
693  * Open: initialize interface
694  *****************************************************************************/
695
696 static int Open( vlc_object_t *p_this )
697 { /* initialisation of the connection */
698     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
699     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
700     playlist_t      *p_playlist;
701     DBusConnection  *p_conn;
702     DBusError       error;
703
704     if( !p_sys )
705         return VLC_ENOMEM;
706
707     p_sys->b_meta_read = false;
708     p_sys->i_caps = CAPS_NONE;
709
710     dbus_error_init( &error );
711
712     /* connect to the session bus */
713     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
714     if( !p_conn )
715     {
716         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
717                 error.message );
718         dbus_error_free( &error );
719         free( p_sys );
720         return VLC_EGENERIC;
721     }
722
723     /* register a well-known name on the bus */
724     dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
725     if( dbus_error_is_set( &error ) )
726     {
727         msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
728                  ": %s", error.message );
729         dbus_error_free( &error );
730         free( p_sys );
731         return VLC_EGENERIC;
732     }
733
734     /* we register the objects */
735     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
736             &vlc_dbus_root_vtable, p_this );
737     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
738             &vlc_dbus_player_vtable, p_this );
739     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
740             &vlc_dbus_tracklist_vtable, p_this );
741
742     dbus_connection_flush( p_conn );
743
744     p_playlist = pl_Yield( p_intf );
745     PL_LOCK;
746     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
747     var_AddCallback( p_playlist, "intf-change", TrackListChangeEmit, p_intf );
748     var_AddCallback( p_playlist, "item-append", TrackListChangeEmit, p_intf );
749     var_AddCallback( p_playlist, "item-deleted", TrackListChangeEmit, p_intf );
750     var_AddCallback( p_playlist, "random", StatusChangeEmit, p_intf );
751     var_AddCallback( p_playlist, "repeat", StatusChangeEmit, p_intf );
752     var_AddCallback( p_playlist, "loop", StatusChangeEmit, p_intf );
753     PL_UNLOCK;
754     pl_Release( p_playlist );
755
756     p_intf->pf_run = Run;
757     p_intf->p_sys = p_sys;
758     p_sys->p_conn = p_conn;
759
760     UpdateCaps( p_intf, false );
761
762     return VLC_SUCCESS;
763 }
764
765 /*****************************************************************************
766  * Close: destroy interface
767  *****************************************************************************/
768
769 static void Close   ( vlc_object_t *p_this )
770 {
771     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
772     playlist_t      *p_playlist = pl_Yield( p_intf );;
773     input_thread_t  *p_input;
774
775     p_this->b_dead = true;
776
777     PL_LOCK;
778     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
779     var_DelCallback( p_playlist, "intf-change", TrackListChangeEmit, p_intf );
780     var_DelCallback( p_playlist, "item-append", TrackListChangeEmit, p_intf );
781     var_DelCallback( p_playlist, "item-deleted", TrackListChangeEmit, p_intf );
782     var_DelCallback( p_playlist, "random", StatusChangeEmit, p_intf );
783     var_DelCallback( p_playlist, "repeat", StatusChangeEmit, p_intf );
784     var_DelCallback( p_playlist, "loop", StatusChangeEmit, p_intf );
785
786     p_input = p_playlist->p_input;
787     if ( p_input )
788     {
789         vlc_object_yield( p_input );
790         var_DelCallback( p_input, "state", StateChange, p_intf );
791         vlc_object_release( p_input );
792     }
793
794     PL_UNLOCK;
795     pl_Release( p_playlist );
796
797     dbus_connection_unref( p_intf->p_sys->p_conn );
798
799     free( p_intf->p_sys );
800 }
801
802 /*****************************************************************************
803  * Run: main loop
804  *****************************************************************************/
805
806 static void Run          ( intf_thread_t *p_intf )
807 {
808     while( !intf_ShouldDie( p_intf ) )
809     {
810         msleep( INTF_IDLE_SLEEP );
811         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
812     }
813 }
814
815 /******************************************************************************
816  * CapsChange: player capabilities change signal
817  *****************************************************************************/
818 DBUS_SIGNAL( CapsChangeSignal )
819 {
820     SIGNAL_INIT( "CapsChange" );
821     OUT_ARGUMENTS;
822
823     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
824     SIGNAL_SEND;
825 }
826
827 /******************************************************************************
828  * TrackListChange: tracklist order / length change signal 
829  *****************************************************************************/
830 DBUS_SIGNAL( TrackListChangeSignal )
831 { /* emit the new tracklist lengh */
832     SIGNAL_INIT("TrackListChange");
833     OUT_ARGUMENTS;
834
835     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_data );
836     dbus_int32_t i_elements = p_playlist->current.i_size;
837     pl_Release( p_playlist );
838
839     ADD_INT32( &i_elements );
840     SIGNAL_SEND;
841 }
842
843 /*****************************************************************************
844  * TrackListChangeEmit: Emits the TrackListChange signal
845  *****************************************************************************/
846 /* FIXME: It is not called on tracklist reordering */
847 static int TrackListChangeEmit( vlc_object_t *p_this, const char *psz_var,
848             vlc_value_t oldval, vlc_value_t newval, void *p_data )
849 {
850     VLC_UNUSED(oldval);
851     intf_thread_t *p_intf = p_data;
852
853     if( !strcmp( psz_var, "item-append" ) || !strcmp( psz_var, "item-remove" ) )
854     {
855         /* don't signal when items are added/removed in p_category */
856         playlist_t *p_playlist = (playlist_t*)p_this;
857         playlist_add_t *p_add = newval.p_address;
858         playlist_item_t *p_item;
859         p_item = playlist_ItemGetById( p_playlist, p_add->i_node, true );
860         assert( p_item );
861         while( p_item->p_parent )
862             p_item = p_item->p_parent;
863         if( p_item == p_playlist->p_root_category )
864             return VLC_SUCCESS;
865     }
866
867     if( p_intf->b_dead )
868         return VLC_SUCCESS;
869
870     UpdateCaps( p_intf, true );
871     TrackListChangeSignal( p_intf->p_sys->p_conn, p_data );
872     return VLC_SUCCESS;
873 }
874 /*****************************************************************************
875  * TrackChange: Playlist item change callback
876  *****************************************************************************/
877
878 DBUS_SIGNAL( TrackChangeSignal )
879 { /* emit the metadata of the new item */
880     SIGNAL_INIT( "TrackChange" );
881     OUT_ARGUMENTS;
882
883     input_item_t *p_item = (input_item_t*) p_data;
884     GetInputMeta ( p_item, &args );
885
886     SIGNAL_SEND;
887 }
888
889 /*****************************************************************************
890  * StatusChange: Player status change signal
891  *****************************************************************************/
892
893 DBUS_SIGNAL( StatusChangeSignal )
894 { /* send the updated status info on the bus */
895     SIGNAL_INIT( "StatusChange" );
896     OUT_ARGUMENTS;
897
898     /* we're called from a callback of input_thread_t, so it can not be
899      * destroyed before we return */
900     MarshalStatus( (intf_thread_t*) p_data, &args, false );
901
902     SIGNAL_SEND;
903 }
904
905 /*****************************************************************************
906  * StateChange: callback on input "state"
907  *****************************************************************************/
908 static int StateChange( vlc_object_t *p_this, const char* psz_var,
909             vlc_value_t oldval, vlc_value_t newval, void *p_data )
910 {
911     VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
912     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
913     intf_sys_t          *p_sys      = p_intf->p_sys;
914
915     if( p_intf->b_dead )
916         return VLC_SUCCESS;
917
918     UpdateCaps( p_intf, true );
919
920     if( !p_sys->b_meta_read && newval.i_int == PLAYING_S )
921     {
922         input_item_t *p_item = input_GetItem( (input_thread_t*)p_this );
923         if( p_item )
924         {
925             p_sys->b_meta_read = true;
926             TrackChangeSignal( p_sys->p_conn, p_item );
927         }
928     }
929
930     if( newval.i_int == PLAYING_S || newval.i_int == PAUSE_S ||
931         newval.i_int == END_S )
932     {
933         StatusChangeSignal( p_sys->p_conn, (void*) p_intf );
934     }
935
936     return VLC_SUCCESS;
937 }
938
939 /*****************************************************************************
940  * StatusChangeEmit: Emits the StatusChange signal
941  *****************************************************************************/
942 static int StatusChangeEmit( vlc_object_t *p_this, const char *psz_var,
943             vlc_value_t oldval, vlc_value_t newval, void *p_data )
944 {
945     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
946     VLC_UNUSED(oldval); VLC_UNUSED(newval);
947     intf_thread_t *p_intf = p_data;
948
949     if( p_intf->b_dead )
950         return VLC_SUCCESS;
951
952     UpdateCaps( p_intf, false );
953     StatusChangeSignal( p_intf->p_sys->p_conn, p_data );
954     return VLC_SUCCESS;
955 }
956
957 /*****************************************************************************
958  * TrackChange: callback on playlist "playlist-current"
959  *****************************************************************************/
960 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
961             vlc_value_t oldval, vlc_value_t newval, void *p_data )
962 {
963     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
964     intf_sys_t          *p_sys      = p_intf->p_sys;
965     playlist_t          *p_playlist;
966     input_thread_t      *p_input    = NULL;
967     input_item_t        *p_item     = NULL;
968     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var );
969     VLC_UNUSED( oldval ); VLC_UNUSED( newval );
970
971     if( p_intf->b_dead )
972         return VLC_SUCCESS;
973
974     p_sys->b_meta_read = false;
975
976     p_playlist = pl_Yield( p_intf );
977     p_input = p_playlist->p_input;
978
979     if( !p_input )
980     {
981         PL_UNLOCK;
982         pl_Release( p_playlist );
983         return VLC_SUCCESS;
984     }
985
986     vlc_object_yield( p_input );
987     pl_Release( p_playlist );
988
989     p_item = input_GetItem( p_input );
990     if( !p_item )
991     {
992         vlc_object_release( p_input );
993         return VLC_EGENERIC;
994     }
995
996     if( input_item_IsPreparsed( p_item ) )
997     {
998         p_sys->b_meta_read = true;
999         TrackChangeSignal( p_sys->p_conn, p_item );
1000     }
1001
1002     var_AddCallback( p_input, "state", StateChange, p_intf );
1003
1004     vlc_object_release( p_input );
1005     return VLC_SUCCESS;
1006 }
1007
1008 /*****************************************************************************
1009  * UpdateCaps: update p_sys->i_caps
1010  ****************************************************************************/
1011 static int UpdateCaps( intf_thread_t* p_intf, bool b_playlist_locked )
1012 {
1013     intf_sys_t* p_sys = p_intf->p_sys;
1014     dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
1015     playlist_t* p_playlist = pl_Yield( (vlc_object_t*)p_intf );
1016     if( !b_playlist_locked ) PL_LOCK;
1017     
1018     if( p_playlist->current.i_size > 0 )
1019         i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
1020
1021     if( p_playlist->p_input )
1022     {
1023         /* XXX: if UpdateCaps() is called too early, these are
1024          * unconditionnaly true */
1025         if( var_GetBool( p_playlist->p_input, "can-pause" ) )
1026             i_caps |= CAPS_CAN_PAUSE;
1027         if( var_GetBool( p_playlist->p_input, "seekable" ) )
1028             i_caps |= CAPS_CAN_SEEK;
1029     }
1030
1031     if( !b_playlist_locked ) PL_UNLOCK;
1032     pl_Release( p_playlist );
1033
1034     if( p_sys->b_meta_read )
1035         i_caps |= CAPS_CAN_PROVIDE_METADATA;
1036
1037     if( i_caps != p_intf->p_sys->i_caps )
1038     {
1039         p_sys->i_caps = i_caps;
1040         CapsChangeSignal( p_intf->p_sys->p_conn, (vlc_object_t*)p_intf );
1041     }
1042
1043     return VLC_SUCCESS;
1044 }
1045
1046 /*****************************************************************************
1047  * GetInputMeta: Fill a DBusMessage with the given input item metadata
1048  *****************************************************************************/
1049
1050 #define ADD_META( entry, type, data ) \
1051     if( data ) { \
1052         dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
1053                 NULL, &dict_entry ); \
1054         dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
1055                 &ppsz_meta_items[entry] ); \
1056         dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
1057                 type##_AS_STRING, &variant ); \
1058         dbus_message_iter_append_basic( &variant, \
1059                 type, \
1060                 & data ); \
1061         dbus_message_iter_close_container( &dict_entry, &variant ); \
1062         dbus_message_iter_close_container( &dict, &dict_entry ); }
1063
1064 #define ADD_VLC_META_STRING( entry, item ) \
1065     { \
1066         char * psz = input_item_Get##item( p_input );\
1067         ADD_META( entry, DBUS_TYPE_STRING, \
1068                   psz ); \
1069         free( psz ); \
1070     }
1071
1072 static int GetInputMeta( input_item_t* p_input,
1073                         DBusMessageIter *args )
1074 {
1075     DBusMessageIter dict, dict_entry, variant;
1076     /* We need the track length to be expressed in milli-seconds
1077      * instead of µ-seconds */
1078     dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
1079
1080     const char* ppsz_meta_items[] =
1081     {
1082     "title", "artist", "genre", "copyright", "album", "tracknum",
1083     "description", "rating", "date", "setting", "url", "language",
1084     "nowplaying", "publisher", "encodedby", "arturl", "trackid",
1085     "status", "location", "length", "video-codec", "audio-codec",
1086     "video-bitrate", "audio-bitrate", "audio-samplerate"
1087     };
1088
1089     dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
1090
1091     ADD_VLC_META_STRING( 0,  Title );
1092     ADD_VLC_META_STRING( 1,  Artist );
1093     ADD_VLC_META_STRING( 2,  Genre );
1094     ADD_VLC_META_STRING( 3,  Copyright );
1095     ADD_VLC_META_STRING( 4,  Album );
1096     ADD_VLC_META_STRING( 5,  TrackNum );
1097     ADD_VLC_META_STRING( 6,  Description );
1098     ADD_VLC_META_STRING( 7,  Rating );
1099     ADD_VLC_META_STRING( 8,  Date );
1100     ADD_VLC_META_STRING( 9,  Setting );
1101     ADD_VLC_META_STRING( 10, URL );
1102     ADD_VLC_META_STRING( 11, Language );
1103     ADD_VLC_META_STRING( 12, NowPlaying );
1104     ADD_VLC_META_STRING( 13, Publisher );
1105     ADD_VLC_META_STRING( 14, EncodedBy );
1106     ADD_VLC_META_STRING( 15, ArtURL );
1107     ADD_VLC_META_STRING( 16, TrackID );
1108
1109     vlc_mutex_lock( &p_input->lock );
1110     if( p_input->p_meta )
1111         ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
1112     vlc_mutex_unlock( &p_input->lock );
1113
1114     ADD_VLC_META_STRING( 18, URI );
1115     ADD_META( 19, DBUS_TYPE_INT64, i_length );
1116
1117     dbus_message_iter_close_container( args, &dict );
1118     return VLC_SUCCESS;
1119 }
1120
1121 #undef ADD_META
1122 #undef ADD_VLC_META_STRING
1123
1124 /*****************************************************************************
1125  * MarshalStatus: Fill a DBusMessage with the current player status
1126  *****************************************************************************/
1127
1128 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args,
1129                           bool lock )
1130 { /* This is NOT the right way to do that, it would be better to sore
1131      the status information in p_sys and update it on change, thus
1132      avoiding a long lock */
1133
1134     DBusMessageIter status;
1135     dbus_int32_t i_state, i_random, i_repeat, i_loop;
1136     vlc_value_t val;
1137     playlist_t* p_playlist = NULL;
1138     input_thread_t* p_input = NULL;
1139
1140     p_playlist = pl_Yield( (vlc_object_t*) p_intf );
1141     if( lock )
1142         PL_LOCK;
1143
1144     i_state = 2;
1145
1146     p_input = p_playlist->p_input;
1147     if( p_input )
1148     {
1149         var_Get( p_input, "state", &val );
1150         if( val.i_int >= END_S )
1151             i_state = 2;
1152         else if( val.i_int == PAUSE_S )
1153             i_state = 1;
1154         else if( val.i_int <= PLAYING_S )
1155             i_state = 0;
1156     }
1157
1158     i_random = var_CreateGetBool( p_playlist, "random" );
1159
1160     i_repeat = var_CreateGetBool( p_playlist, "repeat" );
1161
1162     i_loop = var_CreateGetBool( p_playlist, "loop" );
1163
1164     if( lock )
1165         PL_UNLOCK;
1166     pl_Release( p_playlist );
1167
1168     dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
1169     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
1170     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
1171     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
1172     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
1173     dbus_message_iter_close_container( args, &status );
1174
1175     return VLC_SUCCESS;
1176 }