root/modules/control/dbus.c

Revision 47fd5024be5edf4ef7f85d56482d17bc598766e3, 35.2 kB (checked in by Pierre d'Herbemont <pdherbemont@videolan.org>, 2 months ago)

Fix playlist_CurrentInput usage.

  • Property mode set to 100644
Line 
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_Hold( (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_Hold( ((vlc_object_t*) p_this) );
170     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
171
172     if( !p_input )
173         i_pos = 0;
174     else
175     {
176         var_Get( p_input, "time", &position );
177         i_pos = position.i_time / 1000;
178         vlc_object_release( p_input );
179     }
180     pl_Release( ((vlc_object_t*) p_this) );
181     ADD_INT32( &i_pos );
182     REPLY_SEND;
183 }
184
185 DBUS_METHOD( PositionSet )
186 { /* set position in milliseconds */
187
188     REPLY_INIT;
189     vlc_value_t position;
190     playlist_t* p_playlist = NULL;
191     dbus_int32_t i_pos;
192
193     DBusError error;
194     dbus_error_init( &error );
195
196     dbus_message_get_args( p_from, &error,
197             DBUS_TYPE_INT32, &i_pos,
198             DBUS_TYPE_INVALID );
199
200     if( dbus_error_is_set( &error ) )
201     {
202         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
203                 error.message );
204         dbus_error_free( &error );
205         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206     }
207     p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
208     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
209
210     if( p_input )
211     {
212         position.i_time = i_pos * 1000;
213         var_Set( p_input, "time", position );
214         vlc_object_release( p_input );
215     }
216     pl_Release( ((vlc_object_t*) p_this) );
217     REPLY_SEND;
218 }
219
220 DBUS_METHOD( VolumeGet )
221 { /* returns volume in percentage */
222     REPLY_INIT;
223     OUT_ARGUMENTS;
224     dbus_int32_t i_dbus_vol;
225     audio_volume_t i_vol;
226     /* 2nd argument of aout_VolumeGet is int32 */
227     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
228     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
229     i_dbus_vol = round( f_vol );
230     ADD_INT32( &i_dbus_vol );
231     REPLY_SEND;
232 }
233
234 DBUS_METHOD( VolumeSet )
235 { /* set volume in percentage */
236     REPLY_INIT;
237
238     DBusError error;
239     dbus_error_init( &error );
240
241     dbus_int32_t i_dbus_vol;
242     audio_volume_t i_vol;
243
244     dbus_message_get_args( p_from, &error,
245             DBUS_TYPE_INT32, &i_dbus_vol,
246             DBUS_TYPE_INVALID );
247
248     if( dbus_error_is_set( &error ) )
249     {
250         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
251                 error.message );
252         dbus_error_free( &error );
253         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
254     }
255
256     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
257     i_vol = round( f_vol );
258     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
259
260     REPLY_SEND;
261 }
262
263 DBUS_METHOD( Next )
264 { /* next playlist item */
265     REPLY_INIT;
266     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
267     playlist_Next( p_playlist );
268     pl_Release( ((vlc_object_t*) p_this) );
269     REPLY_SEND;
270 }
271
272 DBUS_METHOD( Prev )
273 { /* previous playlist item */
274     REPLY_INIT;
275     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
276     playlist_Prev( p_playlist );
277     pl_Release( ((vlc_object_t*) p_this) );
278     REPLY_SEND;
279 }
280
281 DBUS_METHOD( Stop )
282 { /* stop playing */
283     REPLY_INIT;
284     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
285     playlist_Stop( p_playlist );
286     pl_Release( ((vlc_object_t*) p_this) );
287     REPLY_SEND;
288 }
289
290 DBUS_METHOD( GetStatus )
291 { /* returns the current status as a struct of 4 ints */
292 /*
293     First   0 = Playing, 1 = Paused, 2 = Stopped.
294     Second  0 = Playing linearly , 1 = Playing randomly.
295     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
296     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
297  */
298     REPLY_INIT;
299     OUT_ARGUMENTS;
300
301     MarshalStatus( p_this, &args, true );
302
303     REPLY_SEND;
304 }
305
306 DBUS_METHOD( Pause )
307 {
308     REPLY_INIT;
309     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
310     playlist_Pause( p_playlist );
311     pl_Release( (vlc_object_t*) p_this );
312     REPLY_SEND;
313 }
314
315 DBUS_METHOD( Play )
316 {
317     REPLY_INIT;
318     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
319
320     input_thread_t *p_input =  playlist_CurrentInput( p_playlist );
321
322     if( p_input )
323     {
324         double i_pos = 0;
325         input_Control( p_input, INPUT_SET_POSITION, i_pos );
326         vlc_object_release( p_input );
327     }
328     else
329         playlist_Play( p_playlist );
330
331     pl_Release( (vlc_object_t*) p_this );
332     REPLY_SEND;
333 }
334
335 DBUS_METHOD( GetCurrentMetadata )
336 {
337     REPLY_INIT;
338     OUT_ARGUMENTS;
339     playlist_t* p_playlist = pl_Hold( (vlc_object_t*) p_this );
340     PL_LOCK;
341     playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
342     if(  p_item )
343         GetInputMeta( p_item->p_input, &args );
344     PL_UNLOCK;
345     pl_Release( (vlc_object_t*) p_this );
346     REPLY_SEND;
347 }
348
349 DBUS_METHOD( GetCaps )
350 {
351     REPLY_INIT;
352     OUT_ARGUMENTS;
353
354     ADD_INT32( &((intf_thread_t*)p_this)->p_sys->i_caps );
355
356     REPLY_SEND;
357 }
358
359 /* Media Player information */
360
361 DBUS_METHOD( Identity )
362 {
363     VLC_UNUSED(p_this);
364     REPLY_INIT;
365     OUT_ARGUMENTS;
366     char *psz_identity;
367     if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 )
368     {
369         ADD_STRING( &psz_identity );
370         free( psz_identity );
371     }
372     else
373         return DBUS_HANDLER_RESULT_NEED_MEMORY;
374
375     REPLY_SEND;
376 }
377
378 /* TrackList */
379
380 DBUS_METHOD( AddTrack )
381 { /* add the string to the playlist, and play it if the boolean is true */
382     REPLY_INIT;
383     OUT_ARGUMENTS;
384
385     DBusError error;
386     dbus_error_init( &error );
387     playlist_t* p_playlist = NULL;
388
389     char *psz_mrl;
390     dbus_bool_t b_play;
391
392     dbus_message_get_args( p_from, &error,
393             DBUS_TYPE_STRING, &psz_mrl,
394             DBUS_TYPE_BOOLEAN, &b_play,
395             DBUS_TYPE_INVALID );
396
397     if( dbus_error_is_set( &error ) )
398     {
399         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
400                 error.message );
401         dbus_error_free( &error );
402         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
403     }
404
405     p_playlist = pl_Hold( (vlc_object_t*) p_this );
406     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
407             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
408             PLAYLIST_END, true, false );
409     pl_Release( (vlc_object_t*) p_this );
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     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
423     dbus_int32_t i_position = p_playlist->i_current_index;
424     pl_Release( (vlc_object_t*) p_this );
425
426     ADD_INT32( &i_position );
427     REPLY_SEND;
428 }
429
430 DBUS_METHOD( GetMetadata )
431 {
432     REPLY_INIT;
433     OUT_ARGUMENTS;
434     DBusError error;
435     dbus_error_init( &error );
436
437     dbus_int32_t i_position;
438
439     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
440     PL_LOCK;
441
442     dbus_message_get_args( p_from, &error,
443            DBUS_TYPE_INT32, &i_position,
444            DBUS_TYPE_INVALID );
445
446     if( dbus_error_is_set( &error ) )
447     {
448         PL_UNLOCK;
449         pl_Release( (vlc_object_t*) p_this );
450         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
451                 error.message );
452         dbus_error_free( &error );
453         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
454     }
455
456     if( i_position < p_playlist->current.i_size )
457     {
458         GetInputMeta( p_playlist->current.p_elems[i_position]->p_input, &args );
459     }
460
461     PL_UNLOCK;
462     pl_Release( (vlc_object_t*) p_this );
463     REPLY_SEND;
464 }
465
466 DBUS_METHOD( GetLength )
467 {
468     REPLY_INIT;
469     OUT_ARGUMENTS;
470
471     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
472     dbus_int32_t i_elements = p_playlist->current.i_size;
473     pl_Release( (vlc_object_t*) p_this );
474
475     ADD_INT32( &i_elements );
476     REPLY_SEND;
477 }
478
479 DBUS_METHOD( DelTrack )
480 {
481     REPLY_INIT;
482
483     DBusError error;
484     dbus_error_init( &error );
485
486     dbus_int32_t i_position;
487     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
488
489     dbus_message_get_args( p_from, &error,
490             DBUS_TYPE_INT32, &i_position,
491             DBUS_TYPE_INVALID );
492
493     if( dbus_error_is_set( &error ) )
494     {
495         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
496                 error.message );
497         dbus_error_free( &error );
498         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
499     }
500
501     PL_LOCK;
502     if( i_position < p_playlist->current.i_size )
503     {
504         playlist_DeleteFromInput( p_playlist,
505             p_playlist->current.p_elems[i_position]->p_input->i_id,
506             pl_Locked );
507     }
508     PL_UNLOCK;
509
510     pl_Release( (vlc_object_t*) p_this );
511
512     REPLY_SEND;
513 }
514
515 DBUS_METHOD( SetLoop )
516 {
517     REPLY_INIT;
518     OUT_ARGUMENTS;
519
520     DBusError error;
521     dbus_bool_t b_loop;
522     vlc_value_t val;
523     playlist_t* p_playlist = NULL;
524
525     dbus_error_init( &error );
526     dbus_message_get_args( p_from, &error,
527             DBUS_TYPE_BOOLEAN, &b_loop,
528             DBUS_TYPE_INVALID );
529
530     if( dbus_error_is_set( &error ) )
531     {
532         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
533                 error.message );
534         dbus_error_free( &error );
535         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
536     }
537
538     val.b_bool = ( b_loop == TRUE ) ? true : false ;
539     p_playlist = pl_Hold( (vlc_object_t*) p_this );
540     var_Set ( p_playlist, "loop", val );
541     pl_Release( ((vlc_object_t*) p_this) );
542
543     REPLY_SEND;
544 }
545
546 DBUS_METHOD( Repeat )
547 {
548     REPLY_INIT;
549     OUT_ARGUMENTS;
550
551     DBusError error;
552     dbus_bool_t b_repeat;
553     vlc_value_t val;
554     playlist_t* p_playlist = NULL;
555
556     dbus_error_init( &error );
557     dbus_message_get_args( p_from, &error,
558             DBUS_TYPE_BOOLEAN, &b_repeat,
559             DBUS_TYPE_INVALID );
560
561     if( dbus_error_is_set( &error ) )
562     {
563         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
564                 error.message );
565         dbus_error_free( &error );
566         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
567     }
568
569     val.b_bool = ( b_repeat == TRUE ) ? true : false ;
570
571     p_playlist = pl_Hold( (vlc_object_t*) p_this );
572     var_Set ( p_playlist, "repeat", val );
573     pl_Release( ((vlc_object_t*) p_this) );
574
575     REPLY_SEND;
576 }
577
578 DBUS_METHOD( SetRandom )
579 {
580     REPLY_INIT;
581     OUT_ARGUMENTS;
582
583     DBusError error;
584     dbus_bool_t b_random;
585     vlc_value_t val;
586     playlist_t* p_playlist = NULL;
587
588     dbus_error_init( &error );
589     dbus_message_get_args( p_from, &error,
590             DBUS_TYPE_BOOLEAN, &b_random,
591             DBUS_TYPE_INVALID );
592
593     if( dbus_error_is_set( &error ) )
594     {
595         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
596                 error.message );
597         dbus_error_free( &error );
598         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
599     }
600
601     val.b_bool = ( b_random == TRUE ) ? true : false ;
602
603     p_playlist = pl_Hold( (vlc_object_t*) p_this );
604     var_Set ( p_playlist, "random", val );
605     pl_Release( ((vlc_object_t*) p_this) );
606
607     REPLY_SEND;
608 }
609 /*****************************************************************************
610  * Introspection method
611  *****************************************************************************/
612
613 DBUS_METHOD( handle_introspect_root )
614 { /* handles introspection of root object */
615     VLC_UNUSED(p_this);
616     REPLY_INIT;
617     OUT_ARGUMENTS;
618     ADD_STRING( &psz_introspection_xml_data_root );
619     REPLY_SEND;
620 }
621
622 DBUS_METHOD( handle_introspect_player )
623 {
624     VLC_UNUSED(p_this);
625     REPLY_INIT;
626     OUT_ARGUMENTS;
627     ADD_STRING( &psz_introspection_xml_data_player );
628     REPLY_SEND;
629 }
630
631 DBUS_METHOD( handle_introspect_tracklist )
632 {
633     VLC_UNUSED(p_this);
634     REPLY_INIT;
635     OUT_ARGUMENTS;
636     ADD_STRING( &psz_introspection_xml_data_tracklist );
637     REPLY_SEND;
638 }
639
640 /*****************************************************************************
641  * handle_*: answer to incoming messages
642  *****************************************************************************/
643
644 #define METHOD_FUNC( method, function ) \
645     else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
646         return function( p_conn, p_from, p_this )
647
648 DBUS_METHOD( handle_root )
649 {
650
651     if( dbus_message_is_method_call( p_from,
652                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
653         return handle_introspect_root( p_conn, p_from, p_this );
654
655     /* here D-Bus method's names are associated to an handler */
656
657     METHOD_FUNC( "Identity",                Identity );
658     METHOD_FUNC( "MprisVersion",            MprisVersion );
659     METHOD_FUNC( "Quit",                    Quit );
660
661     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
662 }
663
664
665 DBUS_METHOD( handle_player )
666 {
667     if( dbus_message_is_method_call( p_from,
668                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
669         return handle_introspect_player( p_conn, p_from, p_this );
670
671     /* here D-Bus method's names are associated to an handler */
672
673     METHOD_FUNC( "Prev",                    Prev );
674     METHOD_FUNC( "Next",                    Next );
675     METHOD_FUNC( "Stop",                    Stop );
676     METHOD_FUNC( "Play",                    Play );
677     METHOD_FUNC( "Pause",                   Pause );
678     METHOD_FUNC( "Repeat",                  Repeat );
679     METHOD_FUNC( "VolumeSet",               VolumeSet );
680     METHOD_FUNC( "VolumeGet",               VolumeGet );
681     METHOD_FUNC( "PositionSet",             PositionSet );
682     METHOD_FUNC( "PositionGet",             PositionGet );
683     METHOD_FUNC( "GetStatus",               GetStatus );
684     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
685     METHOD_FUNC( "GetCaps",                 GetCaps );
686
687     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
688 }
689
690 DBUS_METHOD( handle_tracklist )
691 {
692     if( dbus_message_is_method_call( p_from,
693                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
694     return handle_introspect_tracklist( p_conn, p_from, p_this );
695
696     /* here D-Bus method's names are associated to an handler */
697
698     METHOD_FUNC( "GetMetadata",             GetMetadata );
699     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
700     METHOD_FUNC( "GetLength",               GetLength );
701     METHOD_FUNC( "AddTrack",                AddTrack );
702     METHOD_FUNC( "DelTrack",                DelTrack );
703     METHOD_FUNC( "SetLoop",                 SetLoop );
704     METHOD_FUNC( "SetRandom",               SetRandom );
705
706     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
707 }
708
709 /*****************************************************************************
710  * Open: initialize interface
711  *****************************************************************************/
712
713 static int Open( vlc_object_t *p_this )
714 { /* initialisation of the connection */
715     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
716     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
717     playlist_t      *p_playlist;
718     DBusConnection  *p_conn;
719     DBusError       error;
720
721     if( !p_sys )
722         return VLC_ENOMEM;
723
724     p_sys->b_meta_read = false;
725     p_sys->i_caps = CAPS_NONE;
726     p_sys->b_dead = false;
727
728     dbus_error_init( &error );
729
730     /* connect to the session bus */
731     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
732     if( !p_conn )
733     {
734         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
735                 error.message );
736         dbus_error_free( &error );
737         free( p_sys );
738         return VLC_EGENERIC;
739     }
740
741     /* register a well-known name on the bus */
742     dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
743     if( dbus_error_is_set( &error ) )
744     {
745         msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
746                  ": %s", error.message );
747         dbus_error_free( &error );
748         free( p_sys );
749         return VLC_EGENERIC;
750     }
751
752     /* we register the objects */
753     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
754             &vlc_dbus_root_vtable, p_this );
755     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
756             &vlc_dbus_player_vtable, p_this );
757     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
758             &vlc_dbus_tracklist_vtable, p_this );
759
760     dbus_connection_flush( p_conn );
761
762     p_playlist = pl_Hold( p_intf );
763     PL_LOCK;
764     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
765     var_AddCallback( p_playlist, "intf-change", TrackListChangeEmit, p_intf );
766     var_AddCallback( p_playlist, "item-append", TrackListChangeEmit, p_intf );
767     var_AddCallback( p_playlist, "item-deleted", TrackListChangeEmit, p_intf );
768     var_AddCallback( p_playlist, "random", StatusChangeEmit, p_intf );
769     var_AddCallback( p_playlist, "repeat", StatusChangeEmit, p_intf );
770     var_AddCallback( p_playlist, "loop", StatusChangeEmit, p_intf );
771     PL_UNLOCK;
772     pl_Release( p_intf );
773
774     p_intf->pf_run = Run;
775     p_intf->p_sys = p_sys;
776     p_sys->p_conn = p_conn;
777
778     UpdateCaps( p_intf, false );
779
780     return VLC_SUCCESS;
781 }
782
783 /*****************************************************************************
784  * Close: destroy interface
785  *****************************************************************************/
786
787 static void Close   ( vlc_object_t *p_this )
788 {
789     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
790     playlist_t      *p_playlist = pl_Hold( p_intf );;
791     input_thread_t  *p_input;
792
793     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
794     var_DelCallback( p_playlist, "intf-change", TrackListChangeEmit, p_intf );
795     var_DelCallback( p_playlist, "item-append", TrackListChangeEmit, p_intf );
796     var_DelCallback( p_playlist, "item-deleted", TrackListChangeEmit, p_intf );
797     var_DelCallback( p_playlist, "random", StatusChangeEmit, p_intf );
798     var_DelCallback( p_playlist, "repeat", StatusChangeEmit, p_intf );
799     var_DelCallback( p_playlist, "loop", StatusChangeEmit, p_intf );
800
801     p_input = playlist_CurrentInput( p_playlist );
802     if ( p_input )
803     {
804         var_DelCallback( p_input, "state", StateChange, p_intf );
805         vlc_object_release( p_input );
806     }
807
808     pl_Release( p_intf );
809
810     dbus_connection_unref( p_intf->p_sys->p_conn );
811
812     free( p_intf->p_sys );
813 }
814
815 /*****************************************************************************
816  * Run: main loop
817  *****************************************************************************/
818
819 static void Run          ( intf_thread_t *p_intf )
820 {
821     for( ;; )
822     {
823         msleep( INTF_IDLE_SLEEP );
824         int canc = vlc_savecancel();
825         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
826         vlc_restorecancel( canc );
827     }
828 }
829
830 /******************************************************************************
831  * CapsChange: player capabilities change signal
832  *****************************************************************************/
833 DBUS_SIGNAL( CapsChangeSignal )
834 {
835     SIGNAL_INIT( "CapsChange" );
836     OUT_ARGUMENTS;
837
838     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
839     SIGNAL_SEND;
840 }
841
842 /******************************************************************************
843  * TrackListChange: tracklist order / length change signal
844  *****************************************************************************/
845 DBUS_SIGNAL( TrackListChangeSignal )
846 { /* emit the new tracklist lengh */
847     SIGNAL_INIT("TrackListChange");
848     OUT_ARGUMENTS;
849
850     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_data );
851     dbus_int32_t i_elements = p_playlist->current.i_size;
852     pl_Release( (vlc_object_t*) p_data );
853
854     ADD_INT32( &i_elements );
855     SIGNAL_SEND;
856 }
857
858 /*****************************************************************************
859  * TrackListChangeEmit: Emits the TrackListChange signal
860  *****************************************************************************/
861 /* FIXME: It is not called on tracklist reordering */
862 static int TrackListChangeEmit( vlc_object_t *p_this, const char *psz_var,
863             vlc_value_t oldval, vlc_value_t newval, void *p_data )
864 {
865     VLC_UNUSED(oldval);
866     intf_thread_t *p_intf = p_data;
867
868     if( !strcmp( psz_var, "item-append" ) || !strcmp( psz_var, "item-remove" ) )
869     {
870         /* don't signal when items are added/removed in p_category */
871         playlist_t *p_playlist = (playlist_t*)p_this;
872         playlist_add_t *p_add = newval.p_address;
873         playlist_item_t *p_item;
874         p_item = playlist_ItemGetById( p_playlist, p_add->i_node, pl_Locked );
875         assert( p_item );
876         while( p_item->p_parent )
877             p_item = p_item->p_parent;
878         if( p_item == p_playlist->p_root_category )
879             return VLC_SUCCESS;
880     }
881
882     if( p_intf->p_sys->b_dead )
883         return VLC_SUCCESS;
884
885     UpdateCaps( p_intf, true );
886     TrackListChangeSignal( p_intf->p_sys->p_conn, p_data );
887     return VLC_SUCCESS;
888 }
889 /*****************************************************************************
890  * TrackChange: Playlist item change callback
891  *****************************************************************************/
892
893 DBUS_SIGNAL( TrackChangeSignal )
894 { /* emit the metadata of the new item */
895     SIGNAL_INIT( "TrackChange" );
896     OUT_ARGUMENTS;
897
898     input_item_t *p_item = (input_item_t*) p_data;
899     GetInputMeta ( p_item, &args );
900
901     SIGNAL_SEND;
902 }
903
904 /*****************************************************************************
905  * StatusChange: Player status change signal
906  *****************************************************************************/
907
908 DBUS_SIGNAL( StatusChangeSignal )
909 { /* send the updated status info on the bus */
910     SIGNAL_INIT( "StatusChange" );
911     OUT_ARGUMENTS;
912
913     /* we're called from a callback of input_thread_t, so it can not be
914      * destroyed before we return */
915     MarshalStatus( (intf_thread_t*) p_data, &args, false );
916
917     SIGNAL_SEND;
918 }
919
920 /*****************************************************************************
921  * StateChange: callback on input "state"
922  *****************************************************************************/
923 static int StateChange( vlc_object_t *p_this, const char* psz_var,
924             vlc_value_t oldval, vlc_value_t newval, void *p_data )
925 {
926     VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
927     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
928     intf_sys_t          *p_sys      = p_intf->p_sys;
929
930     if( p_intf->p_sys->b_dead )
931         return VLC_SUCCESS;
932
933     UpdateCaps( p_intf, true );
934
935     if( !p_sys->b_meta_read && newval.i_int == PLAYING_S )
936     {
937         input_item_t *p_item = input_GetItem( (input_thread_t*)p_this );
938         if( p_item )
939         {
940             p_sys->b_meta_read = true;
941             TrackChangeSignal( p_sys->p_conn, p_item );
942         }
943     }
944
945     if( newval.i_int == PLAYING_S || newval.i_int == PAUSE_S ||
946         newval.i_int == STOP_S ||  newval.i_int == END_S )
947     {
948         StatusChangeSignal( p_sys->p_conn, (void*) p_intf );
949     }
950
951     return VLC_SUCCESS;
952 }
953
954 /*****************************************************************************
955  * StatusChangeEmit: Emits the StatusChange signal
956  *****************************************************************************/
957 static int StatusChangeEmit( vlc_object_t *p_this, const char *psz_var,
958             vlc_value_t oldval, vlc_value_t newval, void *p_data )
959 {
960     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
961     VLC_UNUSED(oldval); VLC_UNUSED(newval);
962     intf_thread_t *p_intf = p_data;
963
964     if( p_intf->p_sys->b_dead )
965         return VLC_SUCCESS;
966
967     UpdateCaps( p_intf, false );
968     StatusChangeSignal( p_intf->p_sys->p_conn, p_data );
969     return VLC_SUCCESS;
970 }
971
972 /*****************************************************************************
973  * TrackChange: callback on playlist "playlist-current"
974  *****************************************************************************/
975 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
976             vlc_value_t oldval, vlc_value_t newval, void *p_data )
977 {
978     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
979     intf_sys_t          *p_sys      = p_intf->p_sys;
980     playlist_t          *p_playlist;
981     input_thread_t      *p_input    = NULL;
982     input_item_t        *p_item     = NULL;
983     VLC_UNUSED( p_this ); VLC_UNUSED( psz_var );
984     VLC_UNUSED( oldval ); VLC_UNUSED( newval );
985
986     if( p_intf->p_sys->b_dead )
987         return VLC_SUCCESS;
988
989     p_sys->b_meta_read = false;
990
991     p_playlist = pl_Hold( p_intf );
992     p_input = playlist_CurrentInput( p_playlist );
993     if( !p_input )
994     {
995         pl_Release( p_intf );
996         return VLC_SUCCESS;
997     }
998
999     pl_Release( p_intf );
1000
1001     p_item = input_GetItem( p_input );
1002     if( !p_item )
1003     {
1004         vlc_object_release( p_input );
1005         return VLC_EGENERIC;
1006     }
1007
1008     if( input_item_IsPreparsed( p_item ) )
1009     {
1010         p_sys->b_meta_read = true;
1011         TrackChangeSignal( p_sys->p_conn, p_item );
1012     }
1013
1014     var_AddCallback( p_input, "state", StateChange, p_intf );
1015
1016     vlc_object_release( p_input );
1017     return VLC_SUCCESS;
1018 }
1019
1020 /*****************************************************************************
1021  * UpdateCaps: update p_sys->i_caps
1022  ****************************************************************************/
1023 static int UpdateCaps( intf_thread_t* p_intf, bool b_playlist_locked )
1024 {
1025     intf_sys_t* p_sys = p_intf->p_sys;
1026     dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
1027     playlist_t* p_playlist = pl_Hold( p_intf );
1028     if( !b_playlist_locked ) PL_LOCK;
1029    
1030     if( p_playlist->current.i_size > 0 )
1031         i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
1032     if( !b_playlist_locked ) PL_UNLOCK;
1033
1034     input_thread_t* p_input = playlist_CurrentInput( p_playlist );
1035     if( p_input )
1036     {
1037         /* XXX: if UpdateCaps() is called too early, these are
1038          * unconditionnaly true */
1039         if( var_GetBool( p_input, "can-pause" ) )
1040             i_caps |= CAPS_CAN_PAUSE;
1041         if( var_GetBool( p_input, "seekable" ) )
1042             i_caps |= CAPS_CAN_SEEK;
1043         vlc_object_release( p_input );
1044     }
1045
1046     pl_Release( p_intf );
1047
1048     if( p_sys->b_meta_read )
1049         i_caps |= CAPS_CAN_PROVIDE_METADATA;
1050