7d0a2c2170f720d92a50cd571be755c70ae94bb9
[vlc.git] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright (C) 2006 Rafaël Carré
5  * $Id$
6  *
7  * Authors:    Rafaël Carré <funman at videolanorg>
8  *             Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*
26  * D-Bus Specification:
27  *      http://dbus.freedesktop.org/doc/dbus-specification.html
28  * D-Bus low-level C API (libdbus)
29  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
30  */
31
32 /*
33  * TODO:
34  *  properties ?
35  *
36  *  macros to read incoming arguments
37  *
38  *  explore different possible types (arrays..)
39  *
40  *  what must we do if org.videolan.vlc already exist on the bus ?
41  *  ( there is more than one vlc instance )
42  */
43
44 /*****************************************************************************
45  * Preamble
46  *****************************************************************************/
47
48 #include <dbus/dbus.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "dbus.h"
54
55 #include <vlc/vlc.h>
56 #include <vlc_aout.h>
57 #include <vlc_interface.h>
58 #include <vlc_meta.h>
59 #include <vlc_input.h>
60 #include <vlc_playlist.h>
61
62
63 /*****************************************************************************
64  * Local prototypes.
65  *****************************************************************************/
66
67 static int  Open    ( vlc_object_t * );
68 static void Close   ( vlc_object_t * );
69 static void Run     ( intf_thread_t * );
70
71
72 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
73                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
74
75 static int GetInputMeta ( input_item_t *p_input, 
76                     DBusMessageIter *args);
77
78 struct intf_sys_t
79 {
80     DBusConnection *p_conn;
81 };
82
83 /*****************************************************************************
84  * Module descriptor
85  *****************************************************************************/
86
87 vlc_module_begin();
88     set_shortname( _("dbus"));
89     set_category( CAT_INTERFACE );
90     set_subcategory( SUBCAT_INTERFACE_CONTROL );
91     set_description( _("D-Bus control interface") );
92     set_capability( "interface", 0 );
93     set_callbacks( Open, Close );
94 vlc_module_end();
95
96 /*****************************************************************************
97  * Methods
98  *****************************************************************************/
99 #if 0
100 DBUS_METHOD( PlaylistExport_XSPF )
101 { /*export playlist to an xspf file */
102
103   /* reads the filename to export to */
104   /* returns the status as int32:
105    *    0 : success
106    *    1 : error
107    *    2 : playlist empty
108    */
109     REPLY_INIT;
110     OUT_ARGUMENTS;
111
112     DBusError error; 
113     dbus_error_init( &error );
114
115     char *psz_file;
116     dbus_int32_t i_ret;
117
118     dbus_message_get_args( p_from, &error,
119             DBUS_TYPE_STRING, &psz_file,
120             DBUS_TYPE_INVALID );
121
122     if( dbus_error_is_set( &error ) )
123     {
124         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
125                 error.message );
126         dbus_error_free( &error );
127         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
128     }
129
130     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
131
132     if( ( !playlist_IsEmpty( p_playlist ) ) &&
133             ( p_playlist->p_root_category->i_children > 0 ) )
134     {
135         if( playlist_Export( p_playlist, psz_file,
136                          p_playlist->p_root_category->pp_children[0],
137                          "export-xspf" ) == VLC_SUCCESS )
138             i_ret = 0;
139         else
140             i_ret = 1;
141     }
142     else
143         i_ret = 2;
144
145     pl_Release( ((vlc_object_t*) p_this ) );
146
147     ADD_INT32( &i_ret );
148     REPLY_SEND;
149 }
150 #endif
151
152 /* Player */
153
154 DBUS_METHOD( Quit )
155 { /* exits vlc */
156     REPLY_INIT;
157     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
158     playlist_Stop( p_playlist );
159     pl_Release( ((vlc_object_t*) p_this) );
160     vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
161     REPLY_SEND;
162 }
163
164 DBUS_METHOD( PositionGet )
165 { /* returns position as an int in the range [0;1000] */
166     REPLY_INIT;
167     OUT_ARGUMENTS;
168     vlc_value_t position;
169     dbus_int32_t i_pos;
170
171     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
172     input_thread_t *p_input = p_playlist->p_input;
173
174     if( !p_input )
175         i_pos = 0;
176     else
177     {
178         var_Get( p_input, "position", &position );
179         i_pos = position.f_float * 1000 ;
180     }
181     pl_Release( ((vlc_object_t*) p_this) );
182     ADD_INT32( &i_pos );
183     REPLY_SEND;
184 }
185
186 DBUS_METHOD( PositionSet )
187 { /* set position from an int in the range [0;1000] */
188
189     REPLY_INIT;
190     vlc_value_t position;
191     playlist_t* p_playlist = NULL;
192     dbus_int32_t i_pos;
193
194     DBusError error;
195     dbus_error_init( &error );
196
197     dbus_message_get_args( p_from, &error,
198             DBUS_TYPE_INT32, &i_pos,
199             DBUS_TYPE_INVALID );
200
201     if( dbus_error_is_set( &error ) )
202     {
203         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
204                 error.message );
205         dbus_error_free( &error );
206         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
207     }
208     p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
209     input_thread_t *p_input = p_playlist->p_input;
210
211     if( p_input )
212     {
213         position.f_float = ((float)i_pos) / 1000;
214         var_Set( p_input, "position", position );
215     }
216     pl_Release( ((vlc_object_t*) p_this) );
217     REPLY_SEND;
218 }
219
220 DBUS_METHOD( VolumeGet )
221 { /* returns volume in percentage */
222     REPLY_INIT;
223     OUT_ARGUMENTS;
224     dbus_int32_t i_dbus_vol;
225     audio_volume_t i_vol;
226     /* 2nd argument of aout_VolumeGet is int32 */
227     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
228     i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
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     i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
256     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
257
258     REPLY_SEND;
259 }
260
261 DBUS_METHOD( Next )
262 { /* next playlist item */
263     REPLY_INIT;
264     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
265     playlist_Next( p_playlist );
266     pl_Release( ((vlc_object_t*) p_this) );
267     REPLY_SEND;
268 }
269
270 DBUS_METHOD( Prev )
271 { /* previous playlist item */
272     REPLY_INIT;
273     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
274     playlist_Prev( p_playlist );
275     pl_Release( ((vlc_object_t*) p_this) );
276     REPLY_SEND;
277 }
278
279 DBUS_METHOD( Stop )
280 { /* stop playing */
281     REPLY_INIT;
282     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
283     playlist_Stop( p_playlist );
284     pl_Release( ((vlc_object_t*) p_this) );
285     REPLY_SEND;
286 }
287
288 DBUS_METHOD( GetStatus )
289 { /* returns an int: 0=playing 1=paused 2=stopped */
290     REPLY_INIT;
291     OUT_ARGUMENTS;
292
293     dbus_int32_t i_status;
294     vlc_value_t val;
295
296     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
297     input_thread_t *p_input = p_playlist->p_input;
298
299     i_status = 2;
300     if( p_input )
301     {
302         var_Get( p_input, "state", &val );
303         if( val.i_int == PAUSE_S )
304             i_status = 1;
305         else if( val.i_int == PLAYING_S )
306             i_status = 0;
307     }
308
309     pl_Release( p_playlist );
310
311     ADD_INT32( &i_status );
312     REPLY_SEND;
313 }
314
315 DBUS_METHOD( Pause )
316 {
317     REPLY_INIT;
318     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
319     playlist_Pause( p_playlist );
320     pl_Release( p_playlist );
321     REPLY_SEND;
322 }
323
324 DBUS_METHOD( Play )
325 {
326     REPLY_INIT;
327     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
328     playlist_Play( p_playlist );
329     pl_Release( p_playlist );
330     REPLY_SEND;
331 }
332
333 DBUS_METHOD( GetCurrentMetadata )
334 {
335     REPLY_INIT;
336     OUT_ARGUMENTS;
337     playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );
338
339     GetInputMeta( p_playlist->status.p_item->p_input, &args );
340
341     pl_Release( p_playlist );
342     REPLY_SEND;
343 }
344
345 /* Media Player information */
346
347 DBUS_METHOD( Identity )
348 {
349     REPLY_INIT;
350     OUT_ARGUMENTS;
351     char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
352     sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
353     ADD_STRING( &psz_identity );
354     free( psz_identity );
355     REPLY_SEND;
356 }
357
358 /* TrackList */
359
360 DBUS_METHOD( AddTrack )
361 { /* add the string to the playlist, and play it if the boolean is true */
362     REPLY_INIT;
363
364     DBusError error;
365     dbus_error_init( &error );
366     playlist_t* p_playlist = NULL;
367
368     char *psz_mrl;
369     dbus_bool_t b_play;
370
371     dbus_message_get_args( p_from, &error,
372             DBUS_TYPE_STRING, &psz_mrl,
373             DBUS_TYPE_BOOLEAN, &b_play,
374             DBUS_TYPE_INVALID );
375
376     if( dbus_error_is_set( &error ) )
377     {
378         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
379                 error.message );
380         dbus_error_free( &error );
381         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
382     }
383
384     p_playlist = pl_Yield( (vlc_object_t*) p_this );
385     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
386             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
387             PLAYLIST_END, VLC_TRUE, VLC_FALSE );
388     pl_Release( p_playlist );
389
390     REPLY_SEND;
391 }
392
393 DBUS_METHOD( GetCurrentTrack )
394 {
395     REPLY_INIT;
396     OUT_ARGUMENTS;
397     dbus_int32_t i_position = 0;
398     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
399     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
400
401     while ( p_tested_item->p_input->i_id !=
402                     p_playlist->status.p_item->p_input->i_id )
403     {
404         i_position++;
405         TEST_NEXT;
406     }
407
408     pl_Release( p_playlist );
409
410     ADD_INT32( &i_position );
411     REPLY_SEND;
412 }
413
414 DBUS_METHOD( GetMetadata )
415 {
416     REPLY_INIT;
417     OUT_ARGUMENTS;
418     DBusError error;
419     dbus_error_init( &error );
420
421     dbus_int32_t i_position, i_count = 0;
422
423     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
424     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
425
426     dbus_message_get_args( p_from, &error,
427             DBUS_TYPE_INT32, &i_position,
428             DBUS_TYPE_INVALID );
429
430     if( dbus_error_is_set( &error ) )
431     {
432         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
433                 error.message );
434         dbus_error_free( &error );
435         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
436     }
437     
438     while ( i_count < i_position ) 
439     {
440         i_count++;
441             TEST_NEXT;
442     }
443
444     GetInputMeta ( p_tested_item->p_input, &args );
445
446     pl_Release( p_playlist );
447     REPLY_SEND;
448 }
449
450 DBUS_METHOD( GetLength )
451
452     REPLY_INIT;
453     OUT_ARGUMENTS;
454
455     dbus_int32_t i_elements = 0;
456     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
457     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
458     playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist, 
459                     p_playlist->p_root_onelevel ); 
460
461     while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id )
462     {
463         i_elements++;
464         TEST_NEXT;
465     }
466
467     pl_Release( p_playlist );
468     
469     ADD_INT32( &i_elements );
470     REPLY_SEND;
471 }
472
473 DBUS_METHOD( DelTrack )
474 {
475     REPLY_INIT;
476
477     DBusError error;
478     dbus_error_init( &error );
479
480     dbus_int32_t i_position, i_count = 0;
481     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
482     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
483
484     dbus_message_get_args( p_from, &error,
485             DBUS_TYPE_INT32, &i_position,
486             DBUS_TYPE_INVALID );
487
488     if( dbus_error_is_set( &error ) )
489     {
490         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
491                 error.message );
492         dbus_error_free( &error );
493         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
494     }
495     
496     while ( i_count < i_position ) 
497     {
498         i_count++;
499         TEST_NEXT;
500     }
501
502     PL_LOCK; 
503     playlist_DeleteFromInput( p_playlist, 
504                     p_tested_item->p_input->i_id, 
505                     VLC_TRUE );
506     PL_UNLOCK;
507
508     pl_Release( p_playlist );
509     
510     REPLY_SEND;
511 }
512
513 DBUS_METHOD( Loop )
514 {
515     REPLY_INIT;
516     OUT_ARGUMENTS;
517
518     DBusError error;
519     dbus_bool_t b_loop;
520     vlc_value_t val;
521     playlist_t* p_playlist = NULL;
522     
523     dbus_error_init( &error );
524     dbus_message_get_args( p_from, &error,
525             DBUS_TYPE_BOOLEAN, &b_loop,
526             DBUS_TYPE_INVALID );
527     
528     if( dbus_error_is_set( &error ) )
529     {
530         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
531                 error.message );
532         dbus_error_free( &error );
533         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
534     }
535
536     val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ;
537     p_playlist = pl_Yield( (vlc_object_t*) p_this );
538     var_Set ( p_playlist, "loop", val );
539     pl_Release( ((vlc_object_t*) p_this) );
540
541     REPLY_SEND;
542 }
543
544 DBUS_METHOD( Repeat )
545 {
546     REPLY_INIT;
547     OUT_ARGUMENTS;
548
549     DBusError error;
550     dbus_bool_t b_repeat;
551     vlc_value_t val;
552     playlist_t* p_playlist = NULL;
553     
554     dbus_error_init( &error );
555     dbus_message_get_args( p_from, &error,
556             DBUS_TYPE_BOOLEAN, &b_repeat,
557             DBUS_TYPE_INVALID );
558     
559     if( dbus_error_is_set( &error ) )
560     {
561         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
562                 error.message );
563         dbus_error_free( &error );
564         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
565     }
566
567     val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ;
568     
569     p_playlist = pl_Yield( (vlc_object_t*) p_this );
570     var_Set ( p_playlist, "repeat", val );
571     pl_Release( ((vlc_object_t*) p_this) );
572
573     REPLY_SEND;
574 }
575
576 /*****************************************************************************
577  * Introspection method
578  *****************************************************************************/
579
580 DBUS_METHOD( handle_introspect_root )
581 { /* handles introspection of /org/videolan/vlc */
582     REPLY_INIT;
583     OUT_ARGUMENTS;
584     ADD_STRING( &psz_introspection_xml_data_root );
585     REPLY_SEND;
586 }
587
588 DBUS_METHOD( handle_introspect_player )
589 {
590     REPLY_INIT;
591     OUT_ARGUMENTS;
592     ADD_STRING( &psz_introspection_xml_data_player );
593     REPLY_SEND;
594 }
595
596 DBUS_METHOD( handle_introspect_tracklist )
597 {
598     REPLY_INIT;
599     OUT_ARGUMENTS;
600     ADD_STRING( &psz_introspection_xml_data_tracklist );
601     REPLY_SEND;
602 }
603
604 /*****************************************************************************
605  * handle_*: answer to incoming messages
606  *****************************************************************************/
607
608 #define METHOD_FUNC( method, function ) \
609     else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
610         return function( p_conn, p_from, p_this )
611
612 DBUS_METHOD( handle_root )
613 {
614
615     if( dbus_message_is_method_call( p_from,
616                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
617         return handle_introspect_root( p_conn, p_from, p_this );
618
619     /* here D-Bus method's names are associated to an handler */
620
621     METHOD_FUNC( "Identity",                Identity );
622
623     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
624 }
625
626
627 DBUS_METHOD( handle_player )
628 {
629     if( dbus_message_is_method_call( p_from,
630                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
631     return handle_introspect_player( p_conn, p_from, p_this );
632
633     /* here D-Bus method's names are associated to an handler */
634
635     METHOD_FUNC( "Prev",                    Prev );
636     METHOD_FUNC( "Next",                    Next );
637     METHOD_FUNC( "Quit",                    Quit );
638     METHOD_FUNC( "Stop",                    Stop );
639     METHOD_FUNC( "Play",                    Play );
640     METHOD_FUNC( "Pause",                   Pause );
641     METHOD_FUNC( "VolumeSet",               VolumeSet );
642     METHOD_FUNC( "VolumeGet",               VolumeGet );
643     METHOD_FUNC( "PositionSet",             PositionSet );
644     METHOD_FUNC( "PositionGet",             PositionGet );
645     METHOD_FUNC( "GetStatus",               GetStatus );
646     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
647
648     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
649 }
650
651 DBUS_METHOD( handle_tracklist )
652 {
653     if( dbus_message_is_method_call( p_from,
654                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
655     return handle_introspect_tracklist( p_conn, p_from, p_this );
656
657     /* here D-Bus method's names are associated to an handler */
658
659     METHOD_FUNC( "GetMetadata",             GetMetadata );
660     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
661     METHOD_FUNC( "GetLength",               GetLength );
662     METHOD_FUNC( "AddTrack",                AddTrack );
663     METHOD_FUNC( "DelTrack",                DelTrack );
664     METHOD_FUNC( "Loop",                    Loop );
665     METHOD_FUNC( "Repeat",                  Repeat );
666
667     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
668 }
669
670 /*****************************************************************************
671  * Open: initialize interface
672  *****************************************************************************/
673
674 static int Open( vlc_object_t *p_this )
675 { /* initialisation of the connection */
676     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
677     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
678     playlist_t      *p_playlist;
679     DBusConnection  *p_conn;
680     DBusError       error;
681
682     if( !p_sys )
683         return VLC_ENOMEM;
684
685     dbus_threads_init_default();
686
687     dbus_error_init( &error );
688
689     /* connect to the session bus */
690     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
691     if( !p_conn )
692     {
693         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
694                 error.message );
695         dbus_error_free( &error );
696         free( p_sys );
697         return VLC_EGENERIC;
698     }
699
700     /* register a well-known name on the bus */
701     dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
702     if( dbus_error_is_set( &error ) )
703     {
704         msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:"                " %s\n", error.message );
705         dbus_error_free( &error );
706         free( p_sys );
707         return VLC_EGENERIC;
708     }
709
710     /* we register the objects */
711     dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
712             &vlc_dbus_root_vtable, p_this );
713     dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
714             &vlc_dbus_player_vtable, p_this );
715     dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
716             &vlc_dbus_tracklist_vtable, p_this );
717
718     dbus_connection_flush( p_conn );
719
720     p_playlist = pl_Yield( p_intf );
721     PL_LOCK;
722     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
723     PL_UNLOCK;
724     pl_Release( p_playlist );
725
726     p_intf->pf_run = Run;
727     p_intf->p_sys = p_sys;
728     p_sys->p_conn = p_conn;
729
730     return VLC_SUCCESS;
731 }
732
733 /*****************************************************************************
734  * Close: destroy interface
735  *****************************************************************************/
736
737 static void Close   ( vlc_object_t *p_this )
738 {
739     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
740     playlist_t      *p_playlist = pl_Yield( p_intf );;
741
742     PL_LOCK;
743     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
744     PL_UNLOCK;
745     pl_Release( p_playlist );
746
747     dbus_connection_unref( p_intf->p_sys->p_conn );
748
749     free( p_intf->p_sys );
750 }
751
752 /*****************************************************************************
753  * Run: main loop
754  *****************************************************************************/
755
756 static void Run          ( intf_thread_t *p_intf )
757 {
758     while( !p_intf->b_die )
759     {
760         msleep( INTF_IDLE_SLEEP );
761         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
762     }
763 }
764
765 /*****************************************************************************
766  * TrackChange: Playlist item change callback
767  *****************************************************************************/
768
769 DBUS_SIGNAL( TrackChangeSignal )
770 { /* emit the metadata of the new item */
771     SIGNAL_INIT( "TrackChange" );
772     OUT_ARGUMENTS;
773
774     input_thread_t *p_input = (input_thread_t*) p_data;
775     GetInputMeta ( input_GetItem(p_input), &args );
776
777     SIGNAL_SEND;
778 }
779
780 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
781             vlc_value_t oldval, vlc_value_t newval, void *p_data )
782 {
783     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
784     intf_sys_t          *p_sys      = p_intf->p_sys;
785     playlist_t          *p_playlist;
786     input_thread_t      *p_input    = NULL;
787     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
788
789     p_playlist = pl_Yield( p_intf );
790     PL_LOCK;
791     p_input = p_playlist->p_input;
792
793     if( !p_input )
794     {
795         PL_UNLOCK;
796         pl_Release( p_playlist );
797         return VLC_SUCCESS;
798     }
799
800     vlc_object_yield( p_input );
801     PL_UNLOCK;
802     pl_Release( p_playlist );
803
804     TrackChangeSignal( p_sys->p_conn, p_input );
805
806     vlc_object_release( p_input );
807     return VLC_SUCCESS;
808 }
809
810 /*****************************************************************************
811  * GetInputMeta: Fill a DBusMessage with the given input item metadata
812  *****************************************************************************/
813
814 #define ADD_META( entry, type, data ) \
815     if( data ) { \
816         dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
817                 NULL, &dict_entry ); \
818         dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
819                 &ppsz_meta_items[entry] ); \
820         dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
821                 type##_AS_STRING, &variant ); \
822         dbus_message_iter_append_basic( &variant, \
823                 type, \
824                 & data ); \
825         dbus_message_iter_close_container( &dict_entry, &variant ); \
826         dbus_message_iter_close_container( &dict, &dict_entry ); }\
827
828 #define ADD_VLC_META_STRING( entry, item ) \
829         ADD_META( entry, DBUS_TYPE_STRING, \
830                 p_input->p_meta->psz_##item );
831
832 static int GetInputMeta( input_item_t* p_input, 
833                         DBusMessageIter *args )
834 { /*FIXME: Works only for already read metadata. */ 
835   
836     DBusMessageIter dict, dict_entry, variant;
837     /* We need the track length to be expressed in seconds
838      * instead of milliseconds */
839     dbus_int64_t i_length = (p_input->i_duration / 1000);
840
841
842     const char* ppsz_meta_items[] = 
843     {
844     "title", "artist", "genre", "copyright", "album", "tracknum",
845     "description", "rating", "date", "setting", "url", "language",
846     "nowplaying", "publisher", "encodedby", "arturl", "trackid",
847     "status", "URI", "length", "video-codec", "audio-codec",
848     "video-bitrate", "audio-bitrate", "audio-samplerate"
849     };
850     
851     dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
852
853     ADD_VLC_META_STRING( 0, title );
854     ADD_VLC_META_STRING( 1, artist );
855     ADD_VLC_META_STRING( 2, genre );
856     ADD_VLC_META_STRING( 3, copyright );
857     ADD_VLC_META_STRING( 4, album );
858     ADD_VLC_META_STRING( 5, tracknum );
859     ADD_VLC_META_STRING( 6, description );
860     ADD_VLC_META_STRING( 7, rating );
861     ADD_VLC_META_STRING( 8, date );
862     ADD_VLC_META_STRING( 9, setting );
863     ADD_VLC_META_STRING( 10, url );
864     ADD_VLC_META_STRING( 11, language );
865     ADD_VLC_META_STRING( 12, nowplaying );
866     ADD_VLC_META_STRING( 13, publisher );
867     ADD_VLC_META_STRING( 14, encodedby );
868     ADD_VLC_META_STRING( 15, arturl );
869     ADD_VLC_META_STRING( 16, trackid ); 
870
871     ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
872     ADD_META( 18, DBUS_TYPE_STRING, p_input->psz_uri ); 
873     ADD_META( 19, DBUS_TYPE_INT64, i_length ); 
874
875     dbus_message_iter_close_container( args, &dict );
876     return VLC_SUCCESS;
877 }
878
879 #undef ADD_META
880 #undef ADD_VLC_META_STRING