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