]> git.sesse.net Git - vlc/blob - modules/control/dbus.c
D-Bus module now uses new specification
[vlc] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright (C) 2006 Rafaël Carré
5  * $Id$
6  *
7  * Author:    Rafaël Carré <funman at videolanorg>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*
25  * D-Bus Specification:
26  *      http://dbus.freedesktop.org/doc/dbus-specification.html
27  * D-Bus low-level C API (libdbus)
28  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
29  */
30
31 /*
32  * TODO:
33  *  properties ?
34  *
35  *  macros to read incoming arguments
36  *
37  *  explore different possible types (arrays..)
38  *
39  *  what must we do if org.videolan.vlc already exist on the bus ?
40  *  ( there is more than one vlc instance )
41  */
42
43 /*****************************************************************************
44  * Preamble
45  *****************************************************************************/
46
47 #include <dbus/dbus.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include "dbus.h"
53
54 #include <vlc/vlc.h>
55 #include <vlc_aout.h>
56 #include <vlc_interface.h>
57 #include <vlc_meta.h>
58 #include <vlc_input.h>
59 #include <vlc_playlist.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
70 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
71                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
72
73 struct intf_sys_t
74 {
75     DBusConnection *p_conn;
76 };
77
78 /*****************************************************************************
79  * Module descriptor
80  *****************************************************************************/
81
82 vlc_module_begin();
83     set_shortname( _("dbus"));
84     set_category( CAT_INTERFACE );
85     set_subcategory( SUBCAT_INTERFACE_CONTROL );
86     set_description( _("D-Bus control interface") );
87     set_capability( "interface", 0 );
88     set_callbacks( Open, Close );
89 vlc_module_end();
90
91 /*****************************************************************************
92  * Methods
93  *****************************************************************************/
94 #if 0
95 DBUS_METHOD( PlaylistExport_XSPF )
96 { /*export playlist to an xspf file */
97
98   /* reads the filename to export to */
99   /* returns the status as int32:
100    *    0 : success
101    *    1 : error
102    *    2 : playlist empty
103    */
104     REPLY_INIT;
105     OUT_ARGUMENTS;
106
107     DBusError error; 
108     dbus_error_init( &error );
109
110     char *psz_file;
111     dbus_int32_t i_ret;
112
113     dbus_message_get_args( p_from, &error,
114             DBUS_TYPE_STRING, &psz_file,
115             DBUS_TYPE_INVALID );
116
117     if( dbus_error_is_set( &error ) )
118     {
119         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
120                 error.message );
121         dbus_error_free( &error );
122         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
123     }
124
125     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
126
127     if( ( !playlist_IsEmpty( p_playlist ) ) &&
128             ( p_playlist->p_root_category->i_children > 0 ) )
129     {
130         if( playlist_Export( p_playlist, psz_file,
131                          p_playlist->p_root_category->pp_children[0],
132                          "export-xspf" ) == VLC_SUCCESS )
133             i_ret = 0;
134         else
135             i_ret = 1;
136     }
137     else
138         i_ret = 2;
139
140     pl_Release( ((vlc_object_t*) p_this ) );
141
142     ADD_INT32( &i_ret );
143     REPLY_SEND;
144 }
145 #endif
146
147 /* Player */
148
149 DBUS_METHOD( Quit )
150 { /* exits vlc */
151     REPLY_INIT;
152     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
153     playlist_Stop( p_playlist );
154     pl_Release( ((vlc_object_t*) p_this) );
155     ((vlc_object_t*)p_this)->p_libvlc->b_die = VLC_TRUE;
156     REPLY_SEND;
157 }
158
159 DBUS_METHOD( PositionGet )
160 { /* returns position as an int in the range [0;1000] */
161     REPLY_INIT;
162     OUT_ARGUMENTS;
163     vlc_value_t position;
164     dbus_int32_t i_pos;
165
166     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
167     input_thread_t *p_input = p_playlist->p_input;
168
169     if( !p_input )
170         i_pos = 0;
171     else
172     {
173         var_Get( p_input, "position", &position );
174         i_pos = position.f_float * 1000 ;
175     }
176     ADD_INT32( &i_pos );
177     pl_Release( ((vlc_object_t*) p_this) );
178     REPLY_SEND;
179 }
180
181 DBUS_METHOD( PositionSet )
182 { /* set position from an int in the range [0;1000] */
183
184     REPLY_INIT;
185     vlc_value_t position;
186     dbus_int32_t i_pos;
187
188     DBusError error;
189     dbus_error_init( &error );
190
191     dbus_message_get_args( p_from, &error,
192             DBUS_TYPE_INT32, &i_pos,
193             DBUS_TYPE_INVALID );
194
195     if( dbus_error_is_set( &error ) )
196     {
197         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
198                 error.message );
199         dbus_error_free( &error );
200         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
201     }
202     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
203     input_thread_t *p_input = p_playlist->p_input;
204
205     if( p_input )
206     {
207         position.f_float = ((float)i_pos) / 1000;
208         var_Set( p_input, "position", position );
209     }
210     pl_Release( ((vlc_object_t*) p_this) );
211     REPLY_SEND;
212 }
213
214 DBUS_METHOD( VolumeGet )
215 { /* returns volume in percentage */
216     REPLY_INIT;
217     OUT_ARGUMENTS;
218     dbus_int32_t i_dbus_vol;
219     audio_volume_t i_vol;
220     /* 2nd argument of aout_VolumeGet is int32 */
221     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
222     i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
223     ADD_INT32( &i_dbus_vol );
224     REPLY_SEND;
225 }
226
227 DBUS_METHOD( VolumeSet )
228 { /* set volume in percentage */
229     REPLY_INIT;
230
231     DBusError error;
232     dbus_error_init( &error );
233
234     dbus_int32_t i_dbus_vol;
235     audio_volume_t i_vol;
236
237     dbus_message_get_args( p_from, &error,
238             DBUS_TYPE_INT32, &i_dbus_vol,
239             DBUS_TYPE_INVALID );
240
241     if( dbus_error_is_set( &error ) )
242     {
243         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
244                 error.message );
245         dbus_error_free( &error );
246         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
247     }
248
249     i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
250     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
251
252     REPLY_SEND;
253 }
254
255 DBUS_METHOD( Next )
256 { /* next playlist item */
257     REPLY_INIT;
258     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
259     playlist_Next( p_playlist );
260     pl_Release( ((vlc_object_t*) p_this) );
261     REPLY_SEND;
262 }
263
264 DBUS_METHOD( Prev )
265 { /* previous playlist item */
266     REPLY_INIT;
267     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
268     playlist_Prev( p_playlist );
269     pl_Release( ((vlc_object_t*) p_this) );
270     REPLY_SEND;
271 }
272
273 DBUS_METHOD( Stop )
274 { /* stop playing */
275     REPLY_INIT;
276     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
277     playlist_Stop( p_playlist );
278     pl_Release( ((vlc_object_t*) p_this) );
279     REPLY_SEND;
280 }
281
282 DBUS_METHOD( GetStatus )
283 { /* returns an int: 0=playing 1=paused 2=stopped */
284     REPLY_INIT;
285     OUT_ARGUMENTS;
286
287     dbus_int32_t i_status;
288     vlc_value_t val;
289
290     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
291     input_thread_t *p_input = p_playlist->p_input;
292
293     i_status = 2;
294     if( p_input )
295     {
296         var_Get( p_input, "state", &val );
297         if( val.i_int == PAUSE_S )
298             i_status = 1;
299         else if( val.i_int == PLAYING_S )
300             i_status = 0;
301     }
302
303     pl_Release( p_playlist );
304
305     ADD_INT32( &i_status );
306     REPLY_SEND;
307 }
308
309 DBUS_METHOD( Pause )
310 {
311     REPLY_INIT;
312     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
313     playlist_Pause( p_playlist );
314     pl_Release( p_playlist );
315     REPLY_SEND;
316 }
317
318 DBUS_METHOD( Play )
319 {
320     REPLY_INIT;
321     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
322     playlist_Play( p_playlist );
323     pl_Release( p_playlist );
324     REPLY_SEND;
325 }
326
327 /* Media Player information */
328
329 DBUS_METHOD( Identity )
330 {
331     REPLY_INIT;
332     OUT_ARGUMENTS;
333     char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
334     sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
335     ADD_STRING( &psz_identity );
336     free( psz_identity );
337     REPLY_SEND;
338 }
339
340 /* TrackList */
341
342 DBUS_METHOD( AddTrack )
343 { /* add the string to the playlist, and play it if the boolean is true */
344     REPLY_INIT;
345
346     DBusError error;
347     dbus_error_init( &error );
348
349     char *psz_mrl;
350     dbus_bool_t b_play;
351
352     dbus_message_get_args( p_from, &error,
353             DBUS_TYPE_STRING, &psz_mrl,
354             DBUS_TYPE_BOOLEAN, &b_play,
355             DBUS_TYPE_INVALID );
356
357     if( dbus_error_is_set( &error ) )
358     {
359         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
360                 error.message );
361         dbus_error_free( &error );
362         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
363     }
364
365     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
366     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
367             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , PLAYLIST_END, VLC_TRUE );
368     pl_Release( p_playlist );
369
370     REPLY_SEND;
371 }
372
373 DBUS_METHOD( GetCurrentTrack )
374 { //TODO
375     REPLY_INIT;
376     OUT_ARGUMENTS;
377     dbus_int32_t i_position = 0;
378     //TODO get position of playing element
379     ADD_INT32( &i_position );
380     REPLY_SEND;
381 }
382
383 DBUS_METHOD( GetMetadata )
384 { //TODO reads int, returns a{sv}
385     REPLY_INIT;
386     OUT_ARGUMENTS;
387     DBusError error;
388     dbus_error_init( &error );
389
390     dbus_int32_t i_position;
391
392     dbus_message_get_args( p_from, &error,
393             DBUS_TYPE_INT32, &i_position,
394             DBUS_TYPE_INVALID );
395
396     if( dbus_error_is_set( &error ) )
397     {
398         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
399                 error.message );
400         dbus_error_free( &error );
401         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
402     }
403
404     //TODO return a{sv}
405
406     REPLY_SEND;
407 }
408
409 DBUS_METHOD( GetLength )
410 { //TODO
411     REPLY_INIT;
412     OUT_ARGUMENTS;
413     dbus_int32_t i_elements = 0;
414     //TODO: return number of elements in playlist
415     ADD_INT32( &i_elements );
416     REPLY_SEND;
417 }
418
419 DBUS_METHOD( DelTrack )
420 { //TODO
421     REPLY_INIT;
422
423     DBusError error;
424     dbus_error_init( &error );
425
426     dbus_int32_t i_position;
427
428     dbus_message_get_args( p_from, &error,
429             DBUS_TYPE_INT32, &i_position,
430             DBUS_TYPE_INVALID );
431
432     if( dbus_error_is_set( &error ) )
433     {
434         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
435                 error.message );
436         dbus_error_free( &error );
437         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
438     }
439
440     //TODO delete the element
441
442     REPLY_SEND;
443 }
444
445 /*****************************************************************************
446  * Introspection method
447  *****************************************************************************/
448
449 DBUS_METHOD( handle_introspect_root )
450 { /* handles introspection of /org/videolan/vlc */
451     REPLY_INIT;
452     OUT_ARGUMENTS;
453     ADD_STRING( &psz_introspection_xml_data_root );
454     REPLY_SEND;
455 }
456
457 DBUS_METHOD( handle_introspect_player )
458 {
459     REPLY_INIT;
460     OUT_ARGUMENTS;
461     ADD_STRING( &psz_introspection_xml_data_player );
462     REPLY_SEND;
463 }
464
465 DBUS_METHOD( handle_introspect_tracklist )
466 {
467     REPLY_INIT;
468     OUT_ARGUMENTS;
469     ADD_STRING( &psz_introspection_xml_data_tracklist );
470     REPLY_SEND;
471 }
472
473 /*****************************************************************************
474  * handle_*: answer to incoming messages
475  *****************************************************************************/
476
477 #define METHOD_FUNC( method, function ) \
478     else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
479         return function( p_conn, p_from, p_this )
480
481 DBUS_METHOD( handle_root )
482 {
483
484     if( dbus_message_is_method_call( p_from,
485                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
486         return handle_introspect_root( p_conn, p_from, p_this );
487
488     /* here D-Bus method's names are associated to an handler */
489
490     METHOD_FUNC( "Identity",                Identity );
491
492     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
493 }
494
495
496 DBUS_METHOD( handle_player )
497 {
498     if( dbus_message_is_method_call( p_from,
499                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
500     return handle_introspect_player( p_conn, p_from, p_this );
501
502     /* here D-Bus method's names are associated to an handler */
503
504     METHOD_FUNC( "Prev",                    Prev );
505     METHOD_FUNC( "Next",                    Next );
506     METHOD_FUNC( "Quit",                    Quit );
507     METHOD_FUNC( "Stop",                    Stop );
508     METHOD_FUNC( "Play",                    Play );
509     METHOD_FUNC( "Pause",                   Pause );
510     METHOD_FUNC( "VolumeSet",               VolumeSet );
511     METHOD_FUNC( "VolumeGet",               VolumeGet );
512     METHOD_FUNC( "PositionSet",             PositionSet );
513     METHOD_FUNC( "PositionGet",             PositionGet );
514     METHOD_FUNC( "GetStatus",               GetStatus );
515
516     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
517 }
518
519 DBUS_METHOD( handle_tracklist )
520 {
521     if( dbus_message_is_method_call( p_from,
522                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
523     return handle_introspect_tracklist( p_conn, p_from, p_this );
524
525     /* here D-Bus method's names are associated to an handler */
526
527     METHOD_FUNC( "GetMetadata",             GetMetadata );
528     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
529     METHOD_FUNC( "GetLength",               GetLength );
530     METHOD_FUNC( "AddTrack",                AddTrack );
531     METHOD_FUNC( "DelTrack",                DelTrack );
532
533     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
534 }
535
536 /*****************************************************************************
537  * Open: initialize interface
538  *****************************************************************************/
539
540 static int Open( vlc_object_t *p_this )
541 { /* initialisation of the connection */
542     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
543     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
544     playlist_t      *p_playlist;
545     DBusConnection  *p_conn;
546     DBusError       error;
547
548     if( !p_sys )
549         return VLC_ENOMEM;
550
551     dbus_threads_init_default();
552
553     dbus_error_init( &error );
554
555     /* connect to the session bus */
556     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
557     if( !p_conn )
558     {
559         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
560                 error.message );
561         dbus_error_free( &error );
562         free( p_sys );
563         return VLC_EGENERIC;
564     }
565
566     /* register a well-known name on the bus */
567     dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
568     if( dbus_error_is_set( &error ) )
569     {
570         msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:"                " %s\n", error.message );
571         dbus_error_free( &error );
572         free( p_sys );
573         return VLC_EGENERIC;
574     }
575
576     /* we unregister the object /, registered by libvlc */
577     dbus_connection_unregister_object_path( p_conn, "/" );
578
579     /* we register the objects */
580     dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
581             &vlc_dbus_root_vtable, p_this );
582     dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
583             &vlc_dbus_player_vtable, p_this );
584     dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
585             &vlc_dbus_tracklist_vtable, p_this );
586
587     dbus_connection_flush( p_conn );
588
589     p_playlist = pl_Yield( p_intf );
590     PL_LOCK;
591     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
592     PL_UNLOCK;
593     pl_Release( p_playlist );
594
595     p_intf->pf_run = Run;
596     p_intf->p_sys = p_sys;
597     p_sys->p_conn = p_conn;
598
599     return VLC_SUCCESS;
600 }
601
602 /*****************************************************************************
603  * Close: destroy interface
604  *****************************************************************************/
605
606 static void Close   ( vlc_object_t *p_this )
607 {
608     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
609     playlist_t      *p_playlist = pl_Yield( p_intf );;
610
611     PL_LOCK;
612     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
613     PL_UNLOCK;
614     pl_Release( p_playlist );
615
616     dbus_connection_unref( p_intf->p_sys->p_conn );
617
618     free( p_intf->p_sys );
619 }
620
621 /*****************************************************************************
622  * Run: main loop
623  *****************************************************************************/
624
625 static void Run          ( intf_thread_t *p_intf )
626 {
627     while( !p_intf->b_die )
628     {
629         msleep( INTF_IDLE_SLEEP );
630         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
631     }
632 }
633
634 /*****************************************************************************
635  * TrackChange: Playlist item change callback
636  *****************************************************************************/
637
638 DBUS_SIGNAL( TrackChangeSignal )
639 { /* emit the name of the new item */
640     SIGNAL_INIT( "TrackChange" );
641     OUT_ARGUMENTS;
642
643     input_thread_t *p_input = (input_thread_t*) p_data;
644     ADD_STRING( &input_GetItem(p_input)->psz_name );
645
646     SIGNAL_SEND;
647 }
648
649 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
650             vlc_value_t oldval, vlc_value_t newval, void *p_data )
651 {
652     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
653     intf_sys_t          *p_sys      = p_intf->p_sys;
654     playlist_t          *p_playlist;
655     input_thread_t      *p_input    = NULL;
656     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
657
658     p_playlist = pl_Yield( p_intf );
659     PL_LOCK;
660     p_input = p_playlist->p_input;
661
662     if( !p_input )
663     {
664         PL_UNLOCK;
665         pl_Release( p_playlist );
666         return VLC_SUCCESS;
667     }
668
669     vlc_object_yield( p_input );
670     PL_UNLOCK;
671     pl_Release( p_playlist );
672
673     TrackChangeSignal( p_sys->p_conn, p_input );
674
675     vlc_object_release( p_input );
676     return VLC_SUCCESS;
677 }
678