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