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