]> git.sesse.net Git - vlc/blob - modules/control/dbus.c
dbus: monitors input "state" variable to emit the TrackChange signal only when the...
[vlc] / 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  *  extract:
31     "If you use this low-level API directly, you're signing up for some pain."
32  * MPRIS Specification (still drafting on June, 25 of 2007):
33  *      http://wiki.xmms2.xmms.se/index.php/Media_Player_Interfaces
34  */
35
36 /*****************************************************************************
37  * Preamble
38  *****************************************************************************/
39
40 #include <dbus/dbus.h>
41
42 #include "dbus.h"
43
44 #include <vlc/vlc.h>
45 #include <vlc_aout.h>
46 #include <vlc_interface.h>
47 #include <vlc_meta.h>
48 #include <vlc_input.h>
49 #include <vlc_playlist.h>
50
51 /*****************************************************************************
52  * Local prototypes.
53  *****************************************************************************/
54
55 static int  Open    ( vlc_object_t * );
56 static void Close   ( vlc_object_t * );
57 static void Run     ( intf_thread_t * );
58
59 static int StateChange( vlc_object_t *, const char *, vlc_value_t,
60                         vlc_value_t, void * );
61
62 static int TrackChange( vlc_object_t *, const char *, vlc_value_t,
63                         vlc_value_t, void * );
64
65 static int GetInputMeta ( input_item_t *, DBusMessageIter * );
66
67 struct intf_sys_t
68 {
69     DBusConnection *p_conn;
70     vlc_bool_t      b_meta_read;
71     input_thread_t *p_input;
72 };
73
74 /*****************************************************************************
75  * Module descriptor
76  *****************************************************************************/
77
78 vlc_module_begin();
79     set_shortname( _("dbus"));
80     set_category( CAT_INTERFACE );
81     set_subcategory( SUBCAT_INTERFACE_CONTROL );
82     set_description( _("D-Bus control interface") );
83     set_capability( "interface", 0 );
84     set_callbacks( Open, Close );
85 vlc_module_end();
86
87 /*****************************************************************************
88  * Methods
89  *****************************************************************************/
90
91 /* Player */
92
93 DBUS_METHOD( Quit )
94 { /* exits vlc */
95     REPLY_INIT;
96     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
97     playlist_Stop( p_playlist );
98     pl_Release( ((vlc_object_t*) p_this) );
99     vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
100     REPLY_SEND;
101 }
102
103 DBUS_METHOD( PositionGet )
104 { /* returns position in milliseconds */
105     REPLY_INIT;
106     OUT_ARGUMENTS;
107     vlc_value_t position;
108     dbus_int32_t i_pos;
109
110     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
111     PL_LOCK;
112     input_thread_t *p_input = p_playlist->p_input;
113
114     if( !p_input )
115         i_pos = 0;
116     else
117     {
118         var_Get( p_input, "time", &position );
119         i_pos = position.i_time / 1000;
120     }
121     PL_UNLOCK;
122     pl_Release( ((vlc_object_t*) p_this) );
123     ADD_INT32( &i_pos );
124     REPLY_SEND;
125 }
126
127 DBUS_METHOD( PositionSet )
128 { /* set position in milliseconds */
129
130     REPLY_INIT;
131     vlc_value_t position;
132     playlist_t* p_playlist = NULL;
133     dbus_int32_t i_pos;
134
135     DBusError error;
136     dbus_error_init( &error );
137
138     dbus_message_get_args( p_from, &error,
139             DBUS_TYPE_INT32, &i_pos,
140             DBUS_TYPE_INVALID );
141
142     if( dbus_error_is_set( &error ) )
143     {
144         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
145                 error.message );
146         dbus_error_free( &error );
147         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
148     }
149     p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
150     PL_LOCK;
151     input_thread_t *p_input = p_playlist->p_input;
152
153     if( p_input )
154     {
155         position.i_time = i_pos * 1000;
156         var_Set( p_input, "time", position );
157     }
158     PL_UNLOCK;
159     pl_Release( ((vlc_object_t*) p_this) );
160     REPLY_SEND;
161 }
162
163 DBUS_METHOD( VolumeGet )
164 { /* returns volume in percentage */
165     REPLY_INIT;
166     OUT_ARGUMENTS;
167     dbus_int32_t i_dbus_vol;
168     audio_volume_t i_vol;
169     /* 2nd argument of aout_VolumeGet is int32 */
170     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
171     i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
172     ADD_INT32( &i_dbus_vol );
173     REPLY_SEND;
174 }
175
176 DBUS_METHOD( VolumeSet )
177 { /* set volume in percentage */
178     REPLY_INIT;
179
180     DBusError error;
181     dbus_error_init( &error );
182
183     dbus_int32_t i_dbus_vol;
184     audio_volume_t i_vol;
185
186     dbus_message_get_args( p_from, &error,
187             DBUS_TYPE_INT32, &i_dbus_vol,
188             DBUS_TYPE_INVALID );
189
190     if( dbus_error_is_set( &error ) )
191     {
192         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
193                 error.message );
194         dbus_error_free( &error );
195         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
196     }
197
198     i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
199     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
200
201     REPLY_SEND;
202 }
203
204 DBUS_METHOD( Next )
205 { /* next playlist item */
206     REPLY_INIT;
207     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
208     playlist_Next( p_playlist );
209     pl_Release( ((vlc_object_t*) p_this) );
210     REPLY_SEND;
211 }
212
213 DBUS_METHOD( Prev )
214 { /* previous playlist item */
215     REPLY_INIT;
216     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
217     playlist_Prev( p_playlist );
218     pl_Release( ((vlc_object_t*) p_this) );
219     REPLY_SEND;
220 }
221
222 DBUS_METHOD( Stop )
223 { /* stop playing */
224     REPLY_INIT;
225     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
226     playlist_Stop( p_playlist );
227     pl_Release( ((vlc_object_t*) p_this) );
228     REPLY_SEND;
229 }
230
231 DBUS_METHOD( GetStatus )
232 { /* returns an int: 0=playing 1=paused 2=stopped */
233     REPLY_INIT;
234     OUT_ARGUMENTS;
235
236     dbus_int32_t i_status;
237     vlc_value_t val;
238
239     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
240     PL_LOCK;
241     input_thread_t *p_input = p_playlist->p_input;
242
243     i_status = 2;
244     if( p_input )
245     {
246         var_Get( p_input, "state", &val );
247         if( val.i_int == PAUSE_S )
248             i_status = 1;
249         else if( val.i_int == PLAYING_S )
250             i_status = 0;
251     }
252
253     PL_UNLOCK;
254     pl_Release( p_playlist );
255
256     ADD_INT32( &i_status );
257     REPLY_SEND;
258 }
259
260 DBUS_METHOD( Pause )
261 {
262     REPLY_INIT;
263     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
264     playlist_Pause( p_playlist );
265     pl_Release( p_playlist );
266     REPLY_SEND;
267 }
268
269 DBUS_METHOD( Play )
270 {
271     REPLY_INIT;
272     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
273     playlist_Play( p_playlist );
274     pl_Release( p_playlist );
275     REPLY_SEND;
276 }
277
278 DBUS_METHOD( GetCurrentMetadata )
279 {
280     REPLY_INIT;
281     OUT_ARGUMENTS;
282     playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );
283     PL_LOCK;
284     if( p_playlist->status.p_item )
285         GetInputMeta( p_playlist->status.p_item->p_input, &args );
286     PL_UNLOCK;
287     pl_Release( p_playlist );
288     REPLY_SEND;
289 }
290
291 /* Media Player information */
292
293 DBUS_METHOD( Identity )
294 {
295     REPLY_INIT;
296     OUT_ARGUMENTS;
297     char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 2 );
298     sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
299     ADD_STRING( &psz_identity );
300     free( psz_identity );
301     REPLY_SEND;
302 }
303
304 /* TrackList */
305
306 DBUS_METHOD( AddTrack )
307 { /* add the string to the playlist, and play it if the boolean is true */
308     REPLY_INIT;
309
310     DBusError error;
311     dbus_error_init( &error );
312     playlist_t* p_playlist = NULL;
313
314     char *psz_mrl;
315     dbus_bool_t b_play;
316
317     dbus_message_get_args( p_from, &error,
318             DBUS_TYPE_STRING, &psz_mrl,
319             DBUS_TYPE_BOOLEAN, &b_play,
320             DBUS_TYPE_INVALID );
321
322     if( dbus_error_is_set( &error ) )
323     {
324         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
325                 error.message );
326         dbus_error_free( &error );
327         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
328     }
329
330     p_playlist = pl_Yield( (vlc_object_t*) p_this );
331     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
332             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
333             PLAYLIST_END, VLC_TRUE, VLC_FALSE );
334     pl_Release( p_playlist );
335
336     REPLY_SEND;
337 }
338
339 DBUS_METHOD( GetCurrentTrack )
340 {
341     REPLY_INIT;
342     OUT_ARGUMENTS;
343
344     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
345     dbus_int32_t i_position = p_playlist->i_current_index;
346     pl_Release( p_playlist );
347
348     ADD_INT32( &i_position );
349     REPLY_SEND;
350 }
351
352 DBUS_METHOD( GetMetadata )
353 {
354     REPLY_INIT;
355     OUT_ARGUMENTS;
356     DBusError error;
357     dbus_error_init( &error );
358
359     dbus_int32_t i_position;
360
361     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
362
363     dbus_message_get_args( p_from, &error,
364            DBUS_TYPE_INT32, &i_position,
365            DBUS_TYPE_INVALID );
366
367     if( dbus_error_is_set( &error ) )
368     {
369         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
370                 error.message );
371         dbus_error_free( &error );
372         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
373     }
374
375     if( i_position <= p_playlist->items.i_size / 2 )
376     {
377         GetInputMeta( p_playlist->items.p_elems[i_position*2-1]->p_input, &args );
378     }
379
380     pl_Release( p_playlist );
381     REPLY_SEND;
382 }
383
384 DBUS_METHOD( GetLength )
385 {
386     REPLY_INIT;
387     OUT_ARGUMENTS;
388
389     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
390     dbus_int32_t i_elements = p_playlist->items.i_size / 2;
391     pl_Release( p_playlist );
392
393     ADD_INT32( &i_elements );
394     REPLY_SEND;
395 }
396
397 DBUS_METHOD( DelTrack )
398 {
399     REPLY_INIT;
400
401     DBusError error;
402     dbus_error_init( &error );
403
404     dbus_int32_t i_position;
405     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
406
407     dbus_message_get_args( p_from, &error,
408             DBUS_TYPE_INT32, &i_position,
409             DBUS_TYPE_INVALID );
410
411     if( dbus_error_is_set( &error ) )
412     {
413         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
414                 error.message );
415         dbus_error_free( &error );
416         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
417     }
418
419     if( i_position >= p_playlist->items.i_size / 2 )
420     {
421         playlist_DeleteFromInput( p_playlist,
422             p_playlist->items.p_elems[i_position*2-1]->i_id,
423             VLC_FALSE );
424     }
425
426     pl_Release( p_playlist );
427
428     REPLY_SEND;
429 }
430
431 DBUS_METHOD( Loop )
432 {
433     REPLY_INIT;
434     OUT_ARGUMENTS;
435
436     DBusError error;
437     dbus_bool_t b_loop;
438     vlc_value_t val;
439     playlist_t* p_playlist = NULL;
440
441     dbus_error_init( &error );
442     dbus_message_get_args( p_from, &error,
443             DBUS_TYPE_BOOLEAN, &b_loop,
444             DBUS_TYPE_INVALID );
445
446     if( dbus_error_is_set( &error ) )
447     {
448         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
449                 error.message );
450         dbus_error_free( &error );
451         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
452     }
453
454     val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ;
455     p_playlist = pl_Yield( (vlc_object_t*) p_this );
456     var_Set ( p_playlist, "loop", val );
457     pl_Release( ((vlc_object_t*) p_this) );
458
459     REPLY_SEND;
460 }
461
462 DBUS_METHOD( Repeat )
463 {
464     REPLY_INIT;
465     OUT_ARGUMENTS;
466
467     DBusError error;
468     dbus_bool_t b_repeat;
469     vlc_value_t val;
470     playlist_t* p_playlist = NULL;
471
472     dbus_error_init( &error );
473     dbus_message_get_args( p_from, &error,
474             DBUS_TYPE_BOOLEAN, &b_repeat,
475             DBUS_TYPE_INVALID );
476
477     if( dbus_error_is_set( &error ) )
478     {
479         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
480                 error.message );
481         dbus_error_free( &error );
482         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483     }
484
485     val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ;
486
487     p_playlist = pl_Yield( (vlc_object_t*) p_this );
488     var_Set ( p_playlist, "repeat", val );
489     pl_Release( ((vlc_object_t*) p_this) );
490
491     REPLY_SEND;
492 }
493
494 DBUS_METHOD( Random )
495 {
496     REPLY_INIT;
497     OUT_ARGUMENTS;
498
499     DBusError error;
500     dbus_bool_t b_random;
501     vlc_value_t val;
502     playlist_t* p_playlist = NULL;
503
504     dbus_error_init( &error );
505     dbus_message_get_args( p_from, &error,
506             DBUS_TYPE_BOOLEAN, &b_random,
507             DBUS_TYPE_INVALID );
508
509     if( dbus_error_is_set( &error ) )
510     {
511         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
512                 error.message );
513         dbus_error_free( &error );
514         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
515     }
516
517     val.b_bool = ( b_random == TRUE ) ? VLC_TRUE : VLC_FALSE ;
518
519     p_playlist = pl_Yield( (vlc_object_t*) p_this );
520     var_Set ( p_playlist, "random", val );
521     pl_Release( ((vlc_object_t*) p_this) );
522
523     REPLY_SEND;
524 }
525 /*****************************************************************************
526  * Introspection method
527  *****************************************************************************/
528
529 DBUS_METHOD( handle_introspect_root )
530 { /* handles introspection of root object */
531     REPLY_INIT;
532     OUT_ARGUMENTS;
533     ADD_STRING( &psz_introspection_xml_data_root );
534     REPLY_SEND;
535 }
536
537 DBUS_METHOD( handle_introspect_player )
538 {
539     REPLY_INIT;
540     OUT_ARGUMENTS;
541     ADD_STRING( &psz_introspection_xml_data_player );
542     REPLY_SEND;
543 }
544
545 DBUS_METHOD( handle_introspect_tracklist )
546 {
547     REPLY_INIT;
548     OUT_ARGUMENTS;
549     ADD_STRING( &psz_introspection_xml_data_tracklist );
550     REPLY_SEND;
551 }
552
553 /*****************************************************************************
554  * handle_*: answer to incoming messages
555  *****************************************************************************/
556
557 #define METHOD_FUNC( method, function ) \
558     else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
559         return function( p_conn, p_from, p_this )
560
561 DBUS_METHOD( handle_root )
562 {
563
564     if( dbus_message_is_method_call( p_from,
565                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
566         return handle_introspect_root( p_conn, p_from, p_this );
567
568     /* here D-Bus method's names are associated to an handler */
569
570     METHOD_FUNC( "Identity",                Identity );
571
572     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
573 }
574
575
576 DBUS_METHOD( handle_player )
577 {
578     if( dbus_message_is_method_call( p_from,
579                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
580     return handle_introspect_player( p_conn, p_from, p_this );
581
582     /* here D-Bus method's names are associated to an handler */
583
584     METHOD_FUNC( "Prev",                    Prev );
585     METHOD_FUNC( "Next",                    Next );
586     METHOD_FUNC( "Quit",                    Quit );
587     METHOD_FUNC( "Stop",                    Stop );
588     METHOD_FUNC( "Play",                    Play );
589     METHOD_FUNC( "Pause",                   Pause );
590     METHOD_FUNC( "Repeat",                  Repeat );
591     METHOD_FUNC( "VolumeSet",               VolumeSet );
592     METHOD_FUNC( "VolumeGet",               VolumeGet );
593     METHOD_FUNC( "PositionSet",             PositionSet );
594     METHOD_FUNC( "PositionGet",             PositionGet );
595     METHOD_FUNC( "GetStatus",               GetStatus );
596     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
597
598     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
599 }
600
601 DBUS_METHOD( handle_tracklist )
602 {
603     if( dbus_message_is_method_call( p_from,
604                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
605     return handle_introspect_tracklist( p_conn, p_from, p_this );
606
607     /* here D-Bus method's names are associated to an handler */
608
609     METHOD_FUNC( "GetMetadata",             GetMetadata );
610     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
611     METHOD_FUNC( "GetLength",               GetLength );
612     METHOD_FUNC( "AddTrack",                AddTrack );
613     METHOD_FUNC( "DelTrack",                DelTrack );
614     METHOD_FUNC( "Loop",                    Loop );
615     METHOD_FUNC( "Random",                  Random );
616
617     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
618 }
619
620 /*****************************************************************************
621  * Open: initialize interface
622  *****************************************************************************/
623
624 static int Open( vlc_object_t *p_this )
625 { /* initialisation of the connection */
626     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
627     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
628     playlist_t      *p_playlist;
629     DBusConnection  *p_conn;
630     DBusError       error;
631
632     if( !p_sys )
633         return VLC_ENOMEM;
634
635     p_sys->b_meta_read = VLC_FALSE;
636     p_sys->p_input = NULL;
637
638     dbus_error_init( &error );
639
640     /* connect to the session bus */
641     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
642     if( !p_conn )
643     {
644         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
645                 error.message );
646         dbus_error_free( &error );
647         free( p_sys );
648         return VLC_EGENERIC;
649     }
650
651     /* register a well-known name on the bus */
652     dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
653     if( dbus_error_is_set( &error ) )
654     {
655         msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
656                  ": %s", error.message );
657         dbus_error_free( &error );
658         free( p_sys );
659         return VLC_EGENERIC;
660     }
661
662     /* we register the objects */
663     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
664             &vlc_dbus_root_vtable, p_this );
665     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
666             &vlc_dbus_player_vtable, p_this );
667     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
668             &vlc_dbus_tracklist_vtable, p_this );
669
670     dbus_connection_flush( p_conn );
671
672     p_playlist = pl_Yield( p_intf );
673     PL_LOCK;
674     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
675     PL_UNLOCK;
676     pl_Release( p_playlist );
677
678     p_intf->pf_run = Run;
679     p_intf->p_sys = p_sys;
680     p_sys->p_conn = p_conn;
681
682     return VLC_SUCCESS;
683 }
684
685 /*****************************************************************************
686  * Close: destroy interface
687  *****************************************************************************/
688
689 static void Close   ( vlc_object_t *p_this )
690 {
691     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
692     playlist_t      *p_playlist = pl_Yield( p_intf );;
693
694     PL_LOCK;
695     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
696     PL_UNLOCK;
697     if( p_intf->p_sys->p_input )
698         var_DelCallback( p_intf->p_sys->p_input, "state", StateChange, p_intf );
699     pl_Release( p_playlist );
700
701     dbus_connection_unref( p_intf->p_sys->p_conn );
702
703     free( p_intf->p_sys );
704 }
705
706 /*****************************************************************************
707  * Run: main loop
708  *****************************************************************************/
709
710 static void Run          ( intf_thread_t *p_intf )
711 {
712     while( !intf_ShouldDie( p_intf ) )
713     {
714         msleep( INTF_IDLE_SLEEP );
715         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
716     }
717 }
718
719 /*****************************************************************************
720  * TrackChange: Playlist item change callback
721  *****************************************************************************/
722
723 DBUS_SIGNAL( TrackChangeSignal )
724 { /* emit the metadata of the new item */
725     SIGNAL_INIT( "TrackChange" );
726     OUT_ARGUMENTS;
727
728     input_item_t *p_item = (input_item_t*) p_data;
729     GetInputMeta ( p_item, &args );
730
731     SIGNAL_SEND;
732 }
733
734 /*****************************************************************************
735  * StateChange: callback on input "state"
736  *****************************************************************************/
737 static int StateChange( vlc_object_t *p_this, const char* psz_var,
738             vlc_value_t oldval, vlc_value_t newval, void *p_data )
739 {
740     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
741     intf_sys_t          *p_sys      = p_intf->p_sys;
742
743     if( !p_sys->b_meta_read && newval.i_int == PLAYING_S )
744     {
745         input_item_t *p_item = input_GetItem( (input_thread_t*)p_this );
746         if( p_item )
747         {
748             p_sys->b_meta_read = VLC_TRUE;
749             TrackChangeSignal( p_sys->p_conn, p_item );
750         }
751     }
752
753     return VLC_SUCCESS;
754 }
755
756 /*****************************************************************************
757  * TrackChange: callback on playlist "playlist-current"
758  *****************************************************************************/
759 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
760             vlc_value_t oldval, vlc_value_t newval, void *p_data )
761 {
762     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
763     intf_sys_t          *p_sys      = p_intf->p_sys;
764     playlist_t          *p_playlist;
765     input_thread_t      *p_input    = NULL;
766     input_item_t        *p_item     = NULL;
767     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var );
768     VLC_UNUSED( oldval ); VLC_UNUSED( newval );
769
770     p_sys->b_meta_read = VLC_FALSE;
771
772     p_playlist = pl_Yield( p_intf );
773     PL_LOCK;
774     p_input = p_playlist->p_input;
775
776     if( !p_input )
777     {
778         PL_UNLOCK;
779         pl_Release( p_playlist );
780         return VLC_SUCCESS;
781     }
782
783     vlc_object_yield( p_input );
784     PL_UNLOCK;
785     pl_Release( p_playlist );
786
787     p_item = input_GetItem( p_input );
788     if( !p_item )
789     {
790         vlc_object_release( p_input );
791         return VLC_EGENERIC;
792     }
793
794     p_sys->p_input = NULL;
795     if( input_item_IsPreparsed( p_item ) )
796     {
797         p_sys->b_meta_read = VLC_TRUE;
798         TrackChangeSignal( p_sys->p_conn, p_item );
799     }
800     else
801     {
802         var_AddCallback( p_input, "state", StateChange, p_intf );
803         p_sys->p_input = p_input;
804     }
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     { \
830         char * psz = input_item_Get##item( p_input );\
831         ADD_META( entry, DBUS_TYPE_STRING, \
832                   psz ); \
833         free( psz ); \
834     }
835
836 static int GetInputMeta( input_item_t* p_input,
837                         DBusMessageIter *args )
838 { /*FIXME: Works only for already read metadata. */
839
840     DBusMessageIter dict, dict_entry, variant;
841     /* We need the track length to be expressed in seconds
842      * instead of milliseconds */
843     dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
844
845     const char* ppsz_meta_items[] =
846     {
847     "title", "artist", "genre", "copyright", "album", "tracknum",
848     "description", "rating", "date", "setting", "url", "language",
849     "nowplaying", "publisher", "encodedby", "arturl", "trackid",
850     "status", "URI", "length", "video-codec", "audio-codec",
851     "video-bitrate", "audio-bitrate", "audio-samplerate"
852     };
853
854     dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
855
856     ADD_VLC_META_STRING( 0,  Title );
857     ADD_VLC_META_STRING( 1,  Artist );
858     ADD_VLC_META_STRING( 2,  Genre );
859     ADD_VLC_META_STRING( 3,  Copyright );
860     ADD_VLC_META_STRING( 4,  Album );
861     ADD_VLC_META_STRING( 5,  TrackNum );
862     ADD_VLC_META_STRING( 6,  Description );
863     ADD_VLC_META_STRING( 7,  Rating );
864     ADD_VLC_META_STRING( 8,  Date );
865     ADD_VLC_META_STRING( 9,  Setting );
866     ADD_VLC_META_STRING( 10, URL );
867     ADD_VLC_META_STRING( 11, Language );
868     ADD_VLC_META_STRING( 12, NowPlaying );
869     ADD_VLC_META_STRING( 13, Publisher );
870     ADD_VLC_META_STRING( 14, EncodedBy );
871     ADD_VLC_META_STRING( 15, ArtURL );
872     ADD_VLC_META_STRING( 16, TrackID );
873
874     vlc_mutex_lock( &p_input->lock );
875     if( p_input->p_meta )
876         ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
877     vlc_mutex_unlock( &p_input->lock );
878
879     ADD_VLC_META_STRING( 18, URI );
880     ADD_META( 19, DBUS_TYPE_INT64, i_length );
881
882     dbus_message_iter_close_container( args, &dict );
883     return VLC_SUCCESS;
884 }
885
886 #undef ADD_META
887 #undef ADD_VLC_META_STRING