]> git.sesse.net Git - vlc/blob - modules/control/dbus.c
Fix compilation
[vlc] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright © 2006-2008 Rafaël Carré
5  * Copyright © 2007-2008 Mirsal Ennaime
6  * Copyright © 2009 The VideoLAN team
7  * $Id$
8  *
9  * Authors:    Rafaël Carré <funman at videolanorg>
10  *             Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*
28  * D-Bus Specification:
29  *      http://dbus.freedesktop.org/doc/dbus-specification.html
30  * D-Bus low-level C API (libdbus)
31  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
32  *  extract:
33  *   "If you use this low-level API directly, you're signing up for some pain."
34  *
35  * MPRIS Specification version 1.0
36  *      http://wiki.xmms2.xmms.se/index.php/MPRIS
37  */
38
39 /*****************************************************************************
40  * Preamble
41  *****************************************************************************/
42
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
46
47 #include <dbus/dbus.h>
48 #include "dbus.h"
49
50 #include <vlc_common.h>
51 #include <vlc_plugin.h>
52 #include <vlc_aout.h>
53 #include <vlc_interface.h>
54 #include <vlc_playlist.h>
55 #include <vlc_meta.h>
56
57 #include <math.h>
58
59 #include <assert.h>
60
61 /*****************************************************************************
62  * Local prototypes.
63  *****************************************************************************/
64
65 static int  Open    ( vlc_object_t * );
66 static void Close   ( vlc_object_t * );
67 static void Run     ( intf_thread_t * );
68
69 static int StateChange( intf_thread_t *, int );
70 static int TrackChange( intf_thread_t * );
71 static int StatusChangeEmit( intf_thread_t *);
72 static int TrackListChangeEmit( intf_thread_t *, int, int );
73
74 static int AllCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
75
76 static int GetInputMeta ( input_item_t *, DBusMessageIter * );
77 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
78 static int UpdateCaps( intf_thread_t* );
79
80 /* GetCaps() capabilities */
81 enum
82 {
83      CAPS_NONE                  = 0,
84      CAPS_CAN_GO_NEXT           = 1 << 0,
85      CAPS_CAN_GO_PREV           = 1 << 1,
86      CAPS_CAN_PAUSE             = 1 << 2,
87      CAPS_CAN_PLAY              = 1 << 3,
88      CAPS_CAN_SEEK              = 1 << 4,
89      CAPS_CAN_PROVIDE_METADATA  = 1 << 5,
90      CAPS_CAN_HAS_TRACKLIST     = 1 << 6
91 };
92
93 // The signal that can be get from the callbacks
94 enum
95 {
96     SIGNAL_ITEM_CURRENT,
97     SIGNAL_INTF_CHANGE,
98     SIGNAL_PLAYLIST_ITEM_APPEND,
99     SIGNAL_PLAYLIST_ITEM_DELETED,
100     SIGNAL_RANDOM,
101     SIGNAL_REPEAT,
102     SIGNAL_LOOP,
103     SIGNAL_STATE
104 };
105
106 struct intf_sys_t
107 {
108     DBusConnection *p_conn;
109     playlist_t     *p_playlist;
110     bool            b_meta_read;
111     dbus_int32_t    i_caps;
112     bool            b_dead;
113     vlc_array_t    *p_events;
114     vlc_mutex_t     lock;
115 };
116
117 typedef struct
118 {
119     int signal;
120     int i_node;
121     int i_input_state;
122 } callback_info_t;
123
124 #define INTF ((intf_thread_t *)p_this)
125 #define PL   (INTF->p_sys->p_playlist)
126
127
128 /*****************************************************************************
129  * Module descriptor
130  *****************************************************************************/
131
132 vlc_module_begin ()
133     set_shortname( N_("dbus"))
134     set_category( CAT_INTERFACE )
135     set_subcategory( SUBCAT_INTERFACE_CONTROL )
136     set_description( N_("D-Bus control interface") )
137     set_capability( "interface", 0 )
138     set_callbacks( Open, Close )
139 vlc_module_end ()
140
141 /*****************************************************************************
142  * Methods
143  *****************************************************************************/
144
145 /* Player */
146
147 DBUS_METHOD( Quit )
148 { /* exits vlc */
149     REPLY_INIT;
150     libvlc_Quit(INTF->p_libvlc);
151     REPLY_SEND;
152 }
153
154 DBUS_METHOD( MprisVersion )
155 { /*implemented version of the mpris spec */
156     REPLY_INIT;
157     OUT_ARGUMENTS;
158     VLC_UNUSED( p_this );
159     dbus_uint16_t i_major = VLC_MPRIS_VERSION_MAJOR;
160     dbus_uint16_t i_minor = VLC_MPRIS_VERSION_MINOR;
161     DBusMessageIter version;
162
163     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_STRUCT, NULL,
164             &version ) )
165         return DBUS_HANDLER_RESULT_NEED_MEMORY;
166
167     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
168             &i_major ) )
169         return DBUS_HANDLER_RESULT_NEED_MEMORY;
170
171     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
172             &i_minor ) )
173         return DBUS_HANDLER_RESULT_NEED_MEMORY;
174
175     if( !dbus_message_iter_close_container( &args, &version ) )
176         return DBUS_HANDLER_RESULT_NEED_MEMORY;
177     REPLY_SEND;
178 }
179
180 DBUS_METHOD( PositionGet )
181 { /* returns position in milliseconds */
182     REPLY_INIT;
183     OUT_ARGUMENTS;
184     dbus_int32_t i_pos;
185
186     input_thread_t *p_input = playlist_CurrentInput( PL );
187
188     if( !p_input )
189         i_pos = 0;
190     else
191     {
192         i_pos = var_GetTime( p_input, "time" ) / 1000;
193         vlc_object_release( p_input );
194     }
195     ADD_INT32( &i_pos );
196     REPLY_SEND;
197 }
198
199 DBUS_METHOD( PositionSet )
200 { /* set position in milliseconds */
201
202     REPLY_INIT;
203     vlc_value_t position;
204     dbus_int32_t i_pos;
205
206     DBusError error;
207     dbus_error_init( &error );
208
209     dbus_message_get_args( p_from, &error,
210             DBUS_TYPE_INT32, &i_pos,
211             DBUS_TYPE_INVALID );
212
213     if( dbus_error_is_set( &error ) )
214     {
215         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
216                 error.message );
217         dbus_error_free( &error );
218         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
219     }
220     input_thread_t *p_input = playlist_CurrentInput( PL );
221
222     if( p_input )
223     {
224         position.i_time = ((mtime_t)i_pos) * 1000;
225         var_Set( p_input, "time", position );
226         vlc_object_release( p_input );
227     }
228     REPLY_SEND;
229 }
230
231 DBUS_METHOD( VolumeGet )
232 { /* returns volume in percentage */
233     REPLY_INIT;
234     OUT_ARGUMENTS;
235     dbus_int32_t i_dbus_vol;
236     audio_volume_t i_vol;
237
238     /* 2nd argument of aout_VolumeGet is int32 */
239     aout_VolumeGet( PL, &i_vol );
240
241     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
242     i_dbus_vol = round( f_vol );
243     ADD_INT32( &i_dbus_vol );
244     REPLY_SEND;
245 }
246
247 DBUS_METHOD( VolumeSet )
248 { /* set volume in percentage */
249     REPLY_INIT;
250
251     DBusError error;
252     dbus_error_init( &error );
253
254     dbus_int32_t i_dbus_vol;
255     audio_volume_t i_vol;
256
257     dbus_message_get_args( p_from, &error,
258             DBUS_TYPE_INT32, &i_dbus_vol,
259             DBUS_TYPE_INVALID );
260
261     if( dbus_error_is_set( &error ) )
262     {
263         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
264                 error.message );
265         dbus_error_free( &error );
266         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
267     }
268
269     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
270     i_vol = round( f_vol );
271     aout_VolumeSet( PL, i_vol );
272     REPLY_SEND;
273 }
274
275 DBUS_METHOD( Next )
276 { /* next playlist item */
277     REPLY_INIT;
278     playlist_Next( PL );
279     REPLY_SEND;
280 }
281
282 DBUS_METHOD( Prev )
283 { /* previous playlist item */
284     REPLY_INIT;
285     playlist_Prev( PL );
286     REPLY_SEND;
287 }
288
289 DBUS_METHOD( Stop )
290 { /* stop playing */
291     REPLY_INIT;
292     playlist_Stop( PL );
293     REPLY_SEND;
294 }
295
296 DBUS_METHOD( GetStatus )
297 { /* returns the current status as a struct of 4 ints */
298 /*
299     First   0 = Playing, 1 = Paused, 2 = Stopped.
300     Second  0 = Playing linearly , 1 = Playing randomly.
301     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
302     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
303  */
304     REPLY_INIT;
305     OUT_ARGUMENTS;
306
307     MarshalStatus( p_this, &args );
308
309     REPLY_SEND;
310 }
311
312 DBUS_METHOD( Pause )
313 {
314     REPLY_INIT;
315     playlist_Pause( PL );
316     REPLY_SEND;
317 }
318
319 DBUS_METHOD( Play )
320 {
321     REPLY_INIT;
322
323     input_thread_t *p_input =  playlist_CurrentInput( PL );
324
325     if( p_input )
326     {
327         double i_pos = 0;
328         input_Control( p_input, INPUT_SET_POSITION, i_pos );
329         vlc_object_release( p_input );
330     }
331     else
332         playlist_Play( PL );
333
334     REPLY_SEND;
335 }
336
337 DBUS_METHOD( GetCurrentMetadata )
338 {
339     REPLY_INIT;
340     OUT_ARGUMENTS;
341     playlist_t *p_playlist = PL;
342
343     PL_LOCK;
344     playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
345     if( p_item )
346         GetInputMeta( p_item->p_input, &args );
347     PL_UNLOCK;
348     REPLY_SEND;
349 }
350
351 DBUS_METHOD( GetCaps )
352 {
353     REPLY_INIT;
354     OUT_ARGUMENTS;
355
356     ADD_INT32( &INTF->p_sys->i_caps );
357
358     REPLY_SEND;
359 }
360
361 /* Media Player information */
362
363 DBUS_METHOD( Identity )
364 {
365     VLC_UNUSED(p_this);
366     REPLY_INIT;
367     OUT_ARGUMENTS;
368     char *psz_identity;
369
370     if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 )
371     {
372         ADD_STRING( &psz_identity );
373         free( psz_identity );
374     }
375     else
376         return DBUS_HANDLER_RESULT_NEED_MEMORY;
377
378     REPLY_SEND;
379 }
380
381 /* TrackList */
382
383 DBUS_METHOD( AddTrack )
384 { /* add the string to the playlist, and play it if the boolean is true */
385     REPLY_INIT;
386     OUT_ARGUMENTS;
387
388     DBusError error;
389     dbus_error_init( &error );
390
391     char *psz_mrl;
392     dbus_bool_t b_play;
393
394     dbus_message_get_args( p_from, &error,
395             DBUS_TYPE_STRING, &psz_mrl,
396             DBUS_TYPE_BOOLEAN, &b_play,
397             DBUS_TYPE_INVALID );
398
399     if( dbus_error_is_set( &error ) )
400     {
401         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
402                 error.message );
403         dbus_error_free( &error );
404         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
405     }
406
407     playlist_Add( PL, psz_mrl, NULL, PLAYLIST_APPEND |
408             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
409             PLAYLIST_END, true, false );
410
411     dbus_int32_t i_success = 0;
412     ADD_INT32( &i_success );
413
414     REPLY_SEND;
415 }
416
417 DBUS_METHOD( GetCurrentTrack )
418 {
419     REPLY_INIT;
420     OUT_ARGUMENTS;
421
422     /* XXX: how about locking?! */
423     dbus_int32_t i_position = PL->i_current_index;
424
425     ADD_INT32( &i_position );
426     REPLY_SEND;
427 }
428
429 DBUS_METHOD( GetMetadata )
430 {
431     REPLY_INIT;
432     OUT_ARGUMENTS;
433     DBusError error;
434     dbus_error_init( &error );
435
436     dbus_int32_t i_position;
437     playlist_t *p_playlist = PL;
438
439     dbus_message_get_args( p_from, &error,
440            DBUS_TYPE_INT32, &i_position,
441            DBUS_TYPE_INVALID );
442
443     if( dbus_error_is_set( &error ) )
444     {
445         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
446                 error.message );
447         dbus_error_free( &error );
448         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
449     }
450
451     PL_LOCK;
452     if( i_position < p_playlist->current.i_size )
453     {
454         GetInputMeta( p_playlist->current.p_elems[i_position]->p_input, &args );
455     }
456
457     PL_UNLOCK;
458     REPLY_SEND;
459 }
460
461 DBUS_METHOD( GetLength )
462 {
463     REPLY_INIT;
464     OUT_ARGUMENTS;
465
466     /* XXX: how about locking */
467     dbus_int32_t i_elements = PL->current.i_size;
468
469     ADD_INT32( &i_elements );
470     REPLY_SEND;
471 }
472
473 DBUS_METHOD( DelTrack )
474 {
475     REPLY_INIT;
476
477     DBusError error;
478     dbus_error_init( &error );
479
480     dbus_int32_t i_position;
481     playlist_t *p_playlist = PL;
482
483     dbus_message_get_args( p_from, &error,
484             DBUS_TYPE_INT32, &i_position,
485             DBUS_TYPE_INVALID );
486
487     if( dbus_error_is_set( &error ) )
488     {
489         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
490                 error.message );
491         dbus_error_free( &error );
492         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
493     }
494
495     PL_LOCK;
496     if( i_position < p_playlist->current.i_size )
497     {
498         playlist_DeleteFromInput( p_playlist,
499             p_playlist->current.p_elems[i_position]->p_input,
500             pl_Locked );
501     }
502     PL_UNLOCK;
503
504     REPLY_SEND;
505 }
506
507 DBUS_METHOD( SetLoop )
508 {
509     REPLY_INIT;
510     OUT_ARGUMENTS;
511
512     DBusError error;
513     dbus_bool_t b_loop;
514
515     dbus_error_init( &error );
516     dbus_message_get_args( p_from, &error,
517             DBUS_TYPE_BOOLEAN, &b_loop,
518             DBUS_TYPE_INVALID );
519
520     if( dbus_error_is_set( &error ) )
521     {
522         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
523                 error.message );
524         dbus_error_free( &error );
525         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
526     }
527
528     var_SetBool( PL, "loop", ( b_loop == TRUE ) );
529
530     REPLY_SEND;
531 }
532
533 DBUS_METHOD( Repeat )
534 {
535     REPLY_INIT;
536     OUT_ARGUMENTS;
537
538     DBusError error;
539     dbus_bool_t b_repeat;
540
541     dbus_error_init( &error );
542     dbus_message_get_args( p_from, &error,
543             DBUS_TYPE_BOOLEAN, &b_repeat,
544             DBUS_TYPE_INVALID );
545
546     if( dbus_error_is_set( &error ) )
547     {
548         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
549                 error.message );
550         dbus_error_free( &error );
551         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
552     }
553
554     var_SetBool( PL, "repeat", ( b_repeat == TRUE ) );
555
556     REPLY_SEND;
557 }
558
559 DBUS_METHOD( SetRandom )
560 {
561     REPLY_INIT;
562     OUT_ARGUMENTS;
563
564     DBusError error;
565     dbus_bool_t b_random;
566
567     dbus_error_init( &error );
568     dbus_message_get_args( p_from, &error,
569             DBUS_TYPE_BOOLEAN, &b_random,
570             DBUS_TYPE_INVALID );
571
572     if( dbus_error_is_set( &error ) )
573     {
574         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
575                 error.message );
576         dbus_error_free( &error );
577         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
578     }
579
580     var_SetBool( PL, "random", ( b_random == TRUE ) );
581
582     REPLY_SEND;
583 }
584 /*****************************************************************************
585  * Introspection method
586  *****************************************************************************/
587
588 DBUS_METHOD( handle_introspect_root )
589 { /* handles introspection of root object */
590     VLC_UNUSED(p_this);
591     REPLY_INIT;
592     OUT_ARGUMENTS;
593     ADD_STRING( &psz_introspection_xml_data_root );
594     REPLY_SEND;
595 }
596
597 DBUS_METHOD( handle_introspect_player )
598 {
599     VLC_UNUSED(p_this);
600     REPLY_INIT;
601     OUT_ARGUMENTS;
602     ADD_STRING( &psz_introspection_xml_data_player );
603     REPLY_SEND;
604 }
605
606 DBUS_METHOD( handle_introspect_tracklist )
607 {
608     VLC_UNUSED(p_this);
609     REPLY_INIT;
610     OUT_ARGUMENTS;
611     ADD_STRING( &psz_introspection_xml_data_tracklist );
612     REPLY_SEND;
613 }
614
615 /*****************************************************************************
616  * handle_*: answer to incoming messages
617  *****************************************************************************/
618
619 #define METHOD_FUNC( method, function ) \
620     else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
621         return function( p_conn, p_from, p_this )
622
623 DBUS_METHOD( handle_root )
624 {
625
626     if( dbus_message_is_method_call( p_from,
627                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
628         return handle_introspect_root( p_conn, p_from, p_this );
629
630     /* here D-Bus method's names are associated to an handler */
631
632     METHOD_FUNC( "Identity",                Identity );
633     METHOD_FUNC( "MprisVersion",            MprisVersion );
634     METHOD_FUNC( "Quit",                    Quit );
635
636     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
637 }
638
639
640 DBUS_METHOD( handle_player )
641 {
642     if( dbus_message_is_method_call( p_from,
643                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
644         return handle_introspect_player( p_conn, p_from, p_this );
645
646     /* here D-Bus method's names are associated to an handler */
647
648     METHOD_FUNC( "Prev",                    Prev );
649     METHOD_FUNC( "Next",                    Next );
650     METHOD_FUNC( "Stop",                    Stop );
651     METHOD_FUNC( "Play",                    Play );
652     METHOD_FUNC( "Pause",                   Pause );
653     METHOD_FUNC( "Repeat",                  Repeat );
654     METHOD_FUNC( "VolumeSet",               VolumeSet );
655     METHOD_FUNC( "VolumeGet",               VolumeGet );
656     METHOD_FUNC( "PositionSet",             PositionSet );
657     METHOD_FUNC( "PositionGet",             PositionGet );
658     METHOD_FUNC( "GetStatus",               GetStatus );
659     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
660     METHOD_FUNC( "GetCaps",                 GetCaps );
661
662     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
663 }
664
665 DBUS_METHOD( handle_tracklist )
666 {
667     if( dbus_message_is_method_call( p_from,
668                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
669     return handle_introspect_tracklist( p_conn, p_from, p_this );
670
671     /* here D-Bus method's names are associated to an handler */
672
673     METHOD_FUNC( "GetMetadata",             GetMetadata );
674     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
675     METHOD_FUNC( "GetLength",               GetLength );
676     METHOD_FUNC( "AddTrack",                AddTrack );
677     METHOD_FUNC( "DelTrack",                DelTrack );
678     METHOD_FUNC( "SetLoop",                 SetLoop );
679     METHOD_FUNC( "SetRandom",               SetRandom );
680
681     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
682 }
683
684 /*****************************************************************************
685  * Open: initialize interface
686  *****************************************************************************/
687
688 static int Open( vlc_object_t *p_this )
689 { /* initialisation of the connection */
690     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
691     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
692     playlist_t      *p_playlist;
693     DBusConnection  *p_conn;
694     DBusError       error;
695
696     if( !p_sys )
697         return VLC_ENOMEM;
698
699     p_sys->b_meta_read = false;
700     p_sys->i_caps = CAPS_NONE;
701     p_sys->b_dead = false;
702
703     dbus_error_init( &error );
704
705     /* connect to the session bus */
706     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
707     if( !p_conn )
708     {
709         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
710                 error.message );
711         dbus_error_free( &error );
712         free( p_sys );
713         return VLC_EGENERIC;
714     }
715
716     /* register a well-known name on the bus */
717     dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
718     if( dbus_error_is_set( &error ) )
719     {
720         msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
721                  ": %s", error.message );
722         dbus_error_free( &error );
723         free( p_sys );
724         return VLC_EGENERIC;
725     }
726
727     /* we register the objects */
728     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
729             &vlc_dbus_root_vtable, p_this );
730     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
731             &vlc_dbus_player_vtable, p_this );
732     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
733             &vlc_dbus_tracklist_vtable, p_this );
734
735     dbus_connection_flush( p_conn );
736
737     p_intf->pf_run = Run;
738     p_intf->p_sys = p_sys;
739     p_sys->p_conn = p_conn;
740     p_sys->p_events = vlc_array_new();
741     vlc_mutex_init( &p_sys->lock );
742
743     p_playlist = pl_Hold( p_intf );
744     p_sys->p_playlist = p_playlist;
745
746     PL_LOCK;
747     var_AddCallback( p_playlist, "item-current", AllCallback, p_intf );
748     var_AddCallback( p_playlist, "intf-change", AllCallback, p_intf );
749     var_AddCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
750     var_AddCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
751     var_AddCallback( p_playlist, "random", AllCallback, p_intf );
752     var_AddCallback( p_playlist, "repeat", AllCallback, p_intf );
753     var_AddCallback( p_playlist, "loop", AllCallback, p_intf );
754     PL_UNLOCK;
755
756     UpdateCaps( p_intf );
757
758     return VLC_SUCCESS;
759 }
760
761 /*****************************************************************************
762  * Close: destroy interface
763  *****************************************************************************/
764
765 static void Close   ( vlc_object_t *p_this )
766 {
767     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
768     intf_sys_t      *p_sys      = p_intf->p_sys;
769     playlist_t      *p_playlist = p_sys->p_playlist;
770     input_thread_t  *p_input;
771
772     var_DelCallback( p_playlist, "item-current", AllCallback, p_intf );
773     var_DelCallback( p_playlist, "intf-change", AllCallback, p_intf );
774     var_DelCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
775     var_DelCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
776     var_DelCallback( p_playlist, "random", AllCallback, p_intf );
777     var_DelCallback( p_playlist, "repeat", AllCallback, p_intf );
778     var_DelCallback( p_playlist, "loop", AllCallback, p_intf );
779
780     p_input = playlist_CurrentInput( p_playlist );
781     if ( p_input )
782     {
783         var_DelCallback( p_input, "state", AllCallback, p_intf );
784         vlc_object_release( p_input );
785     }
786     pl_Release( p_intf );
787
788     dbus_connection_unref( p_sys->p_conn );
789
790     // Free the events array
791     for( int i = 0; i < vlc_array_count( p_sys->p_events ); i++ )
792     {
793         callback_info_t* info = vlc_array_item_at_index( p_sys->p_events, i );
794         free( info );
795     }
796     vlc_mutex_destroy( &p_sys->lock );
797     vlc_array_destroy( p_sys->p_events );
798     free( p_sys );
799 }
800
801 /*****************************************************************************
802  * Run: main loop
803  *****************************************************************************/
804
805 static void Run          ( intf_thread_t *p_intf )
806 {
807     for( ;; )
808     {
809         if( dbus_connection_get_dispatch_status(p_intf->p_sys->p_conn)
810                                              == DBUS_DISPATCH_COMPLETE )
811             msleep( INTF_IDLE_SLEEP );
812         int canc = vlc_savecancel();
813         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
814
815         /* Get the list of events to process
816          *
817          * We can't keep the lock on p_intf->p_sys->p_events, else we risk a
818          * deadlock:
819          * The signal functions could lock mutex X while p_events is locked;
820          * While some other function in vlc (playlist) might lock mutex X
821          * and then set a variable which would call AllCallback(), which itself
822          * needs to lock p_events to add a new event.
823          */
824         vlc_mutex_lock( &p_intf->p_sys->lock );
825         int i_events = vlc_array_count( p_intf->p_sys->p_events );
826         callback_info_t* info[i_events];
827         for( int i = i_events - 1; i >= 0; i-- )
828         {
829             info[i] = vlc_array_item_at_index( p_intf->p_sys->p_events, i );
830             vlc_array_remove( p_intf->p_sys->p_events, i );
831         }
832         vlc_mutex_unlock( &p_intf->p_sys->lock );
833
834         for( int i = 0; i < i_events; i++ )
835         {
836             switch( info[i]->signal )
837             {
838             case SIGNAL_ITEM_CURRENT:
839                 TrackChange( p_intf );
840                 break;
841             case SIGNAL_INTF_CHANGE:
842             case SIGNAL_PLAYLIST_ITEM_APPEND:
843             case SIGNAL_PLAYLIST_ITEM_DELETED:
844                 TrackListChangeEmit( p_intf, info[i]->signal, info[i]->i_node );
845                 break;
846             case SIGNAL_RANDOM:
847             case SIGNAL_REPEAT:
848             case SIGNAL_LOOP:
849                 StatusChangeEmit( p_intf );
850                 break;
851             case SIGNAL_STATE:
852                 StateChange( p_intf, info[i]->i_input_state );
853                 break;
854             default:
855                 assert(0);
856             }
857             free( info[i] );
858         }
859         vlc_restorecancel( canc );
860     }
861 }
862
863
864 // Get all the callbacks
865 static int AllCallback( vlc_object_t *p_this, const char *psz_var,
866                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
867 {
868     (void)p_this;
869     (void)oldval;
870     intf_thread_t *p_intf = (intf_thread_t*)p_data;
871
872     callback_info_t *info = malloc( sizeof( callback_info_t ) );
873     if( !info )
874         return VLC_ENOMEM;
875
876     // Wich event is it ?
877     if( !strcmp( "item-current", psz_var ) )
878         info->signal = SIGNAL_ITEM_CURRENT;
879     else if( !strcmp( "intf-change", psz_var ) )
880         info->signal = SIGNAL_INTF_CHANGE;
881     else if( !strcmp( "playlist-item-append", psz_var ) )
882     {
883         info->signal = SIGNAL_PLAYLIST_ITEM_APPEND;
884         info->i_node = ((playlist_add_t*)newval.p_address)->i_node;
885     }
886     else if( !strcmp( "playlist-item-deleted", psz_var ) )
887         info->signal = SIGNAL_PLAYLIST_ITEM_DELETED;
888     else if( !strcmp( "random", psz_var ) )
889         info->signal = SIGNAL_RANDOM;
890     else if( !strcmp( "repeat", psz_var ) )
891         info->signal = SIGNAL_REPEAT;
892     else if( !strcmp( "loop", psz_var ) )
893         info->signal = SIGNAL_LOOP;
894     else if( !strcmp( "state", psz_var ) )
895     {
896         info->signal = SIGNAL_STATE;
897         info->i_input_state = newval.i_int;
898     }
899     else
900         assert(0);
901
902     // Append the event
903     vlc_mutex_lock( &p_intf->p_sys->lock );
904     vlc_array_append( p_intf->p_sys->p_events, info );
905     vlc_mutex_unlock( &p_intf->p_sys->lock );
906     return VLC_SUCCESS;
907 }
908
909 /******************************************************************************
910  * CapsChange: player capabilities change signal
911  *****************************************************************************/
912 DBUS_SIGNAL( CapsChangeSignal )
913 {
914     SIGNAL_INIT( "CapsChange" );
915     OUT_ARGUMENTS;
916
917     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
918     SIGNAL_SEND;
919 }
920
921 /******************************************************************************
922  * TrackListChange: tracklist order / length change signal
923  *****************************************************************************/
924 DBUS_SIGNAL( TrackListChangeSignal )
925 { /* emit the new tracklist lengh */
926     SIGNAL_INIT("TrackListChange");
927     OUT_ARGUMENTS;
928
929     /* XXX: locking */
930     dbus_int32_t i_elements = ((intf_thread_t*)p_data)->p_sys->p_playlist->current.i_size;
931
932     ADD_INT32( &i_elements );
933     SIGNAL_SEND;
934 }
935
936 /*****************************************************************************
937  * TrackListChangeEmit: Emits the TrackListChange signal
938  *****************************************************************************/
939 /* FIXME: It is not called on tracklist reordering */
940 static int TrackListChangeEmit( intf_thread_t *p_intf, int signal, int i_node )
941 {
942     // "playlist-item-append"
943     if( signal == SIGNAL_PLAYLIST_ITEM_APPEND )
944     {
945         /* don't signal when items are added/removed in p_category */
946         playlist_t *p_playlist = p_intf->p_sys->p_playlist;
947         PL_LOCK;
948         playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_node );
949         assert( p_item );
950         while( p_item->p_parent )
951             p_item = p_item->p_parent;
952         if( p_item == p_playlist->p_root_category )
953         {
954             PL_UNLOCK;
955             return VLC_SUCCESS;
956         }
957         PL_UNLOCK;
958     }
959
960     if( p_intf->p_sys->b_dead )
961         return VLC_SUCCESS;
962
963     UpdateCaps( p_intf );
964     TrackListChangeSignal( p_intf->p_sys->p_conn, p_intf );
965     return VLC_SUCCESS;
966 }
967 /*****************************************************************************
968  * TrackChange: Playlist item change callback
969  *****************************************************************************/
970
971 DBUS_SIGNAL( TrackChangeSignal )
972 { /* emit the metadata of the new item */
973     SIGNAL_INIT( "TrackChange" );
974     OUT_ARGUMENTS;
975
976     input_item_t *p_item = (input_item_t*) p_data;
977     GetInputMeta ( p_item, &args );
978
979     SIGNAL_SEND;
980 }
981
982 /*****************************************************************************
983  * StatusChange: Player status change signal
984  *****************************************************************************/
985
986 DBUS_SIGNAL( StatusChangeSignal )
987 { /* send the updated status info on the bus */
988     SIGNAL_INIT( "StatusChange" );
989     OUT_ARGUMENTS;
990
991     /* we're called from a callback of input_thread_t, so it can not be
992      * destroyed before we return */
993     MarshalStatus( (intf_thread_t*) p_data, &args );
994
995     SIGNAL_SEND;
996 }
997
998 /*****************************************************************************
999  * StateChange: callback on input "state"
1000  *****************************************************************************/
1001 //static int StateChange( vlc_object_t *p_this, const char* psz_var,
1002 //            vlc_value_t oldval, vlc_value_t newval, void *p_data )
1003 static int StateChange( intf_thread_t *p_intf, int i_input_state )
1004 {
1005     intf_sys_t          *p_sys      = p_intf->p_sys;
1006     playlist_t          *p_playlist = p_sys->p_playlist;
1007     input_thread_t      *p_input;
1008     input_item_t        *p_item;
1009
1010     if( p_intf->p_sys->b_dead )
1011         return VLC_SUCCESS;
1012
1013     UpdateCaps( p_intf );
1014
1015     if( !p_sys->b_meta_read && i_input_state == PLAYING_S )
1016     {
1017         p_input = playlist_CurrentInput( p_playlist );
1018         if( p_input )
1019         {
1020             p_item = input_GetItem( p_input );
1021             if( p_item )
1022             {
1023                 p_sys->b_meta_read = true;
1024                 TrackChangeSignal( p_sys->p_conn, p_item );
1025             }
1026             vlc_object_release( p_input );
1027         }
1028     }
1029
1030     if( i_input_state == PLAYING_S || i_input_state == PAUSE_S ||
1031         i_input_state == END_S )
1032     {
1033         StatusChangeSignal( p_sys->p_conn, p_intf );
1034     }
1035
1036     return VLC_SUCCESS;
1037 }
1038
1039 /*****************************************************************************
1040  * StatusChangeEmit: Emits the StatusChange signal
1041  *****************************************************************************/
1042 static int StatusChangeEmit( intf_thread_t * p_intf )
1043 {
1044     if( p_intf->p_sys->b_dead )
1045         return VLC_SUCCESS;
1046
1047     UpdateCaps( p_intf );
1048     StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
1049     return VLC_SUCCESS;
1050 }
1051
1052 /*****************************************************************************
1053  * TrackChange: callback on playlist "item-current"
1054  *****************************************************************************/
1055 static int TrackChange( intf_thread_t *p_intf )
1056 {
1057     intf_sys_t          *p_sys      = p_intf->p_sys;
1058     playlist_t          *p_playlist = p_sys->p_playlist;
1059     input_thread_t      *p_input    = NULL;
1060     input_item_t        *p_item     = NULL;
1061
1062     if( p_intf->p_sys->b_dead )
1063         return VLC_SUCCESS;
1064
1065     p_sys->b_meta_read = false;
1066
1067     p_input = playlist_CurrentInput( p_playlist );
1068     if( !p_input )
1069     {
1070         return VLC_SUCCESS;
1071     }
1072
1073     p_item = input_GetItem( p_input );
1074     if( !p_item )
1075     {
1076         vlc_object_release( p_input );
1077         return VLC_EGENERIC;
1078     }
1079
1080     if( input_item_IsPreparsed( p_item ) )
1081     {
1082         p_sys->b_meta_read = true;
1083         TrackChangeSignal( p_sys->p_conn, p_item );
1084     }
1085
1086     var_AddCallback( p_input, "state", AllCallback, p_intf );
1087
1088     vlc_object_release( p_input );
1089     return VLC_SUCCESS;
1090 }
1091
1092 /*****************************************************************************
1093  * UpdateCaps: update p_sys->i_caps
1094  * This function have to be called with the playlist unlocked
1095  ****************************************************************************/
1096 static int UpdateCaps( intf_thread_t* p_intf )
1097 {
1098     intf_sys_t* p_sys = p_intf->p_sys;
1099     dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
1100     playlist_t* p_playlist = p_sys->p_playlist;
1101
1102     PL_LOCK;
1103     if( p_playlist->current.i_size > 0 )
1104         i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
1105     PL_UNLOCK;
1106
1107     input_thread_t* p_input = playlist_CurrentInput( p_playlist );
1108     if( p_input )
1109     {
1110         /* XXX: if UpdateCaps() is called too early, these are
1111          * unconditionnaly true */
1112         if( var_GetBool( p_input, "can-pause" ) )
1113             i_caps |= CAPS_CAN_PAUSE;
1114         if( var_GetBool( p_input, "can-seek" ) )
1115             i_caps |= CAPS_CAN_SEEK;
1116         vlc_object_release( p_input );
1117     }
1118
1119     if( p_sys->b_meta_read )
1120         i_caps |= CAPS_CAN_PROVIDE_METADATA;
1121
1122     if( i_caps != p_intf->p_sys->i_caps )
1123     {
1124         p_sys->i_caps = i_caps;
1125         CapsChangeSignal( p_intf->p_sys->p_conn, (vlc_object_t*)p_intf );
1126     }
1127
1128     return VLC_SUCCESS;
1129 }
1130
1131 /*****************************************************************************
1132  * GetInputMeta: Fill a DBusMessage with the given input item metadata
1133  *****************************************************************************/
1134
1135 #define ADD_META( entry, type, data ) \
1136     if( data ) { \
1137         dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
1138                 NULL, &dict_entry ); \
1139         dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
1140                 &ppsz_meta_items[entry] ); \
1141         dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
1142                 type##_AS_STRING, &variant ); \
1143         dbus_message_iter_append_basic( &variant, \
1144                 type, \
1145                 & data ); \
1146         dbus_message_iter_close_container( &dict_entry, &variant ); \
1147         dbus_message_iter_close_container( &dict, &dict_entry ); }
1148
1149 #define ADD_VLC_META_STRING( entry, item ) \
1150     { \
1151         char * psz = input_item_Get##item( p_input );\
1152         ADD_META( entry, DBUS_TYPE_STRING, \
1153                   psz ); \
1154         free( psz ); \
1155     }
1156
1157 static int GetInputMeta( input_item_t* p_input,
1158                         DBusMessageIter *args )
1159 {
1160     DBusMessageIter dict, dict_entry, variant;
1161     /* We need the track length to be expressed in milli-seconds
1162      * instead of µ-seconds */
1163     dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
1164
1165     const char* ppsz_meta_items[] =
1166     {
1167     "title", "artist", "genre", "copyright", "album", "tracknum",
1168     "description", "rating", "date", "setting", "url", "language",
1169     "nowplaying", "publisher", "encodedby", "arturl", "trackid",
1170     "status", "location", "length", "video-codec", "audio-codec",
1171     "video-bitrate", "audio-bitrate", "audio-samplerate"
1172     };
1173
1174     dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
1175
1176     ADD_VLC_META_STRING( 0,  Title );
1177     ADD_VLC_META_STRING( 1,  Artist );
1178     ADD_VLC_META_STRING( 2,  Genre );
1179     ADD_VLC_META_STRING( 3,  Copyright );
1180     ADD_VLC_META_STRING( 4,  Album );
1181     ADD_VLC_META_STRING( 5,  TrackNum );
1182     ADD_VLC_META_STRING( 6,  Description );
1183     ADD_VLC_META_STRING( 7,  Rating );
1184     ADD_VLC_META_STRING( 8,  Date );
1185     ADD_VLC_META_STRING( 9,  Setting );
1186     ADD_VLC_META_STRING( 10, URL );
1187     ADD_VLC_META_STRING( 11, Language );
1188     ADD_VLC_META_STRING( 12, NowPlaying );
1189     ADD_VLC_META_STRING( 13, Publisher );
1190     ADD_VLC_META_STRING( 14, EncodedBy );
1191     ADD_VLC_META_STRING( 15, ArtURL );
1192     ADD_VLC_META_STRING( 16, TrackID );
1193
1194     vlc_mutex_lock( &p_input->lock );
1195     if( p_input->p_meta )
1196     {
1197         int i_status = vlc_meta_GetStatus( p_input->p_meta );
1198         ADD_META( 17, DBUS_TYPE_INT32, i_status );
1199     }
1200     vlc_mutex_unlock( &p_input->lock );
1201
1202     ADD_VLC_META_STRING( 18, URI );
1203     ADD_META( 19, DBUS_TYPE_INT64, i_length );
1204
1205     dbus_message_iter_close_container( args, &dict );
1206     return VLC_SUCCESS;
1207 }
1208
1209 #undef ADD_META
1210 #undef ADD_VLC_META_STRING
1211
1212 /*****************************************************************************
1213  * MarshalStatus: Fill a DBusMessage with the current player status
1214  *****************************************************************************/
1215
1216 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
1217 { /* This is NOT the right way to do that, it would be better to sore
1218      the status information in p_sys and update it on change, thus
1219      avoiding a long lock */
1220
1221     DBusMessageIter status;
1222     dbus_int32_t i_state, i_random, i_repeat, i_loop;
1223     int i_val;
1224     playlist_t* p_playlist = p_intf->p_sys->p_playlist;
1225     input_thread_t* p_input = NULL;
1226
1227     i_state = 2;
1228
1229     p_input = playlist_CurrentInput( p_playlist );
1230     if( p_input )
1231     {
1232         i_val = var_GetInteger( p_input, "state" );
1233         if( i_val >= END_S )
1234             i_state = 2;
1235         else if( i_val == PAUSE_S )
1236             i_state = 1;
1237         else if( i_val <= PLAYING_S )
1238             i_state = 0;
1239         vlc_object_release( p_input );
1240     }
1241
1242     i_random = var_CreateGetBool( p_playlist, "random" );
1243
1244     i_repeat = var_CreateGetBool( p_playlist, "repeat" );
1245
1246     i_loop = var_CreateGetBool( p_playlist, "loop" );
1247
1248     dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
1249     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
1250     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
1251     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
1252     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
1253     dbus_message_iter_close_container( args, &status );
1254
1255     return VLC_SUCCESS;
1256 }