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