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