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