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