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