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