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