1 /*****************************************************************************
2 * dbus.c : D-Bus control interface
3 *****************************************************************************
4 * Copyright (C) 2006 Rafaël Carré
7 * Authors: Rafaël Carré <funman at videolanorg>
8 * Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 * D-Bus Specification:
27 * http://dbus.freedesktop.org/doc/dbus-specification.html
28 * D-Bus low-level C API (libdbus)
29 * http://dbus.freedesktop.org/doc/dbus/api/html/index.html
36 * macros to read incoming arguments
38 * explore different possible types (arrays..)
40 * what must we do if org.videolan.vlc already exist on the bus ?
41 * ( there is more than one vlc instance )
44 /*****************************************************************************
46 *****************************************************************************/
48 #include <dbus/dbus.h>
57 #include <vlc_interface.h>
59 #include <vlc_input.h>
60 #include <vlc_playlist.h>
62 /*****************************************************************************
64 *****************************************************************************/
66 static int Open ( vlc_object_t * );
67 static void Close ( vlc_object_t * );
68 static void Run ( intf_thread_t * );
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 );
73 static int GetInputMeta ( input_item_t *p_input,
74 DBusMessageIter *args);
78 DBusConnection *p_conn;
81 /*****************************************************************************
83 *****************************************************************************/
86 set_shortname( _("dbus"));
87 set_category( CAT_INTERFACE );
88 set_subcategory( SUBCAT_INTERFACE_CONTROL );
89 set_description( _("D-Bus control interface") );
90 set_capability( "interface", 0 );
91 set_callbacks( Open, Close );
94 /*****************************************************************************
96 *****************************************************************************/
98 DBUS_METHOD( PlaylistExport_XSPF )
99 { /*export playlist to an xspf file */
101 /* reads the filename to export to */
102 /* returns the status as int32:
111 dbus_error_init( &error );
116 dbus_message_get_args( p_from, &error,
117 DBUS_TYPE_STRING, &psz_file,
120 if( dbus_error_is_set( &error ) )
122 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
124 dbus_error_free( &error );
125 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
128 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
130 if( ( !playlist_IsEmpty( p_playlist ) ) &&
131 ( p_playlist->p_root_category->i_children > 0 ) )
133 if( playlist_Export( p_playlist, psz_file,
134 p_playlist->p_root_category->pp_children[0],
135 "export-xspf" ) == VLC_SUCCESS )
143 pl_Release( ((vlc_object_t*) p_this ) );
155 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
156 playlist_Stop( p_playlist );
157 pl_Release( ((vlc_object_t*) p_this) );
158 vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
162 DBUS_METHOD( PositionGet )
163 { /* returns position as an int in the range [0;1000] */
166 vlc_value_t position;
169 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
170 input_thread_t *p_input = p_playlist->p_input;
176 var_Get( p_input, "position", &position );
177 i_pos = position.f_float * 1000 ;
179 pl_Release( ((vlc_object_t*) p_this) );
184 DBUS_METHOD( PositionSet )
185 { /* set position from an int in the range [0;1000] */
188 vlc_value_t position;
189 playlist_t* p_playlist = NULL;
193 dbus_error_init( &error );
195 dbus_message_get_args( p_from, &error,
196 DBUS_TYPE_INT32, &i_pos,
199 if( dbus_error_is_set( &error ) )
201 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
203 dbus_error_free( &error );
204 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206 p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
207 input_thread_t *p_input = p_playlist->p_input;
211 position.f_float = ((float)i_pos) / 1000;
212 var_Set( p_input, "position", position );
214 pl_Release( ((vlc_object_t*) p_this) );
218 DBUS_METHOD( VolumeGet )
219 { /* returns volume in percentage */
222 dbus_int32_t i_dbus_vol;
223 audio_volume_t i_vol;
224 /* 2nd argument of aout_VolumeGet is int32 */
225 aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
226 i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
227 ADD_INT32( &i_dbus_vol );
231 DBUS_METHOD( VolumeSet )
232 { /* set volume in percentage */
236 dbus_error_init( &error );
238 dbus_int32_t i_dbus_vol;
239 audio_volume_t i_vol;
241 dbus_message_get_args( p_from, &error,
242 DBUS_TYPE_INT32, &i_dbus_vol,
245 if( dbus_error_is_set( &error ) )
247 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
249 dbus_error_free( &error );
250 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
253 i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
254 aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
260 { /* next playlist item */
262 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
263 playlist_Next( p_playlist );
264 pl_Release( ((vlc_object_t*) p_this) );
269 { /* previous playlist item */
271 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
272 playlist_Prev( p_playlist );
273 pl_Release( ((vlc_object_t*) p_this) );
280 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
281 playlist_Stop( p_playlist );
282 pl_Release( ((vlc_object_t*) p_this) );
286 DBUS_METHOD( GetStatus )
287 { /* returns an int: 0=playing 1=paused 2=stopped */
291 dbus_int32_t i_status;
294 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
295 input_thread_t *p_input = p_playlist->p_input;
300 var_Get( p_input, "state", &val );
301 if( val.i_int == PAUSE_S )
303 else if( val.i_int == PLAYING_S )
307 pl_Release( p_playlist );
309 ADD_INT32( &i_status );
316 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
317 playlist_Pause( p_playlist );
318 pl_Release( p_playlist );
325 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
326 playlist_Play( p_playlist );
327 pl_Release( p_playlist );
331 DBUS_METHOD( GetCurrentMetadata )
335 playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );
337 GetInputMeta( p_playlist->status.p_item->p_input, &args );
339 pl_Release( p_playlist );
343 /* Media Player information */
345 DBUS_METHOD( Identity )
349 char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
350 sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
351 ADD_STRING( &psz_identity );
352 free( psz_identity );
358 DBUS_METHOD( AddTrack )
359 { /* add the string to the playlist, and play it if the boolean is true */
363 dbus_error_init( &error );
364 playlist_t* p_playlist = NULL;
369 dbus_message_get_args( p_from, &error,
370 DBUS_TYPE_STRING, &psz_mrl,
371 DBUS_TYPE_BOOLEAN, &b_play,
374 if( dbus_error_is_set( &error ) )
376 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
378 dbus_error_free( &error );
379 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
382 p_playlist = pl_Yield( (vlc_object_t*) p_this );
383 playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
384 ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
385 PLAYLIST_END, VLC_TRUE, VLC_FALSE );
386 pl_Release( p_playlist );
391 DBUS_METHOD( GetCurrentTrack )
395 dbus_int32_t i_position = 0;
396 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
397 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
399 while ( p_tested_item->p_input->i_id !=
400 p_playlist->status.p_item->p_input->i_id )
406 pl_Release( p_playlist );
408 ADD_INT32( &i_position );
412 DBUS_METHOD( GetMetadata )
417 dbus_error_init( &error );
419 dbus_int32_t i_position, i_count = 0;
421 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
422 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
424 dbus_message_get_args( p_from, &error,
425 DBUS_TYPE_INT32, &i_position,
428 if( dbus_error_is_set( &error ) )
430 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
432 dbus_error_free( &error );
433 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
436 while ( i_count < i_position )
442 GetInputMeta ( p_tested_item->p_input, &args );
444 pl_Release( p_playlist );
448 DBUS_METHOD( GetLength )
453 dbus_int32_t i_elements = 0;
454 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
455 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
456 playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist,
457 p_playlist->p_root_onelevel );
459 while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id )
465 pl_Release( p_playlist );
467 ADD_INT32( &i_elements );
471 DBUS_METHOD( DelTrack )
476 dbus_error_init( &error );
478 dbus_int32_t i_position, i_count = 0;
479 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
480 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
482 dbus_message_get_args( p_from, &error,
483 DBUS_TYPE_INT32, &i_position,
486 if( dbus_error_is_set( &error ) )
488 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
490 dbus_error_free( &error );
491 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
494 while ( i_count < i_position )
501 playlist_DeleteFromInput( p_playlist,
502 p_tested_item->p_input->i_id,
506 pl_Release( p_playlist );
519 playlist_t* p_playlist = NULL;
521 dbus_error_init( &error );
522 dbus_message_get_args( p_from, &error,
523 DBUS_TYPE_BOOLEAN, &b_loop,
526 if( dbus_error_is_set( &error ) )
528 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
530 dbus_error_free( &error );
531 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
534 val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ;
535 p_playlist = pl_Yield( (vlc_object_t*) p_this );
536 var_Set ( p_playlist, "loop", val );
537 pl_Release( ((vlc_object_t*) p_this) );
542 DBUS_METHOD( Repeat )
548 dbus_bool_t b_repeat;
550 playlist_t* p_playlist = NULL;
552 dbus_error_init( &error );
553 dbus_message_get_args( p_from, &error,
554 DBUS_TYPE_BOOLEAN, &b_repeat,
557 if( dbus_error_is_set( &error ) )
559 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
561 dbus_error_free( &error );
562 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
565 val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ;
567 p_playlist = pl_Yield( (vlc_object_t*) p_this );
568 var_Set ( p_playlist, "repeat", val );
569 pl_Release( ((vlc_object_t*) p_this) );
574 DBUS_METHOD( Random )
580 dbus_bool_t b_random;
582 playlist_t* p_playlist = NULL;
584 dbus_error_init( &error );
585 dbus_message_get_args( p_from, &error,
586 DBUS_TYPE_BOOLEAN, &b_random,
589 if( dbus_error_is_set( &error ) )
591 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
593 dbus_error_free( &error );
594 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
597 val.b_bool = ( b_random == TRUE ) ? VLC_TRUE : VLC_FALSE ;
599 p_playlist = pl_Yield( (vlc_object_t*) p_this );
600 var_Set ( p_playlist, "random", val );
601 pl_Release( ((vlc_object_t*) p_this) );
605 /*****************************************************************************
606 * Introspection method
607 *****************************************************************************/
609 DBUS_METHOD( handle_introspect_root )
610 { /* handles introspection of /org/videolan/vlc */
613 ADD_STRING( &psz_introspection_xml_data_root );
617 DBUS_METHOD( handle_introspect_player )
621 ADD_STRING( &psz_introspection_xml_data_player );
625 DBUS_METHOD( handle_introspect_tracklist )
629 ADD_STRING( &psz_introspection_xml_data_tracklist );
633 /*****************************************************************************
634 * handle_*: answer to incoming messages
635 *****************************************************************************/
637 #define METHOD_FUNC( method, function ) \
638 else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
639 return function( p_conn, p_from, p_this )
641 DBUS_METHOD( handle_root )
644 if( dbus_message_is_method_call( p_from,
645 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
646 return handle_introspect_root( p_conn, p_from, p_this );
648 /* here D-Bus method's names are associated to an handler */
650 METHOD_FUNC( "Identity", Identity );
652 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
656 DBUS_METHOD( handle_player )
658 if( dbus_message_is_method_call( p_from,
659 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
660 return handle_introspect_player( p_conn, p_from, p_this );
662 /* here D-Bus method's names are associated to an handler */
664 METHOD_FUNC( "Prev", Prev );
665 METHOD_FUNC( "Next", Next );
666 METHOD_FUNC( "Quit", Quit );
667 METHOD_FUNC( "Stop", Stop );
668 METHOD_FUNC( "Play", Play );
669 METHOD_FUNC( "Pause", Pause );
670 METHOD_FUNC( "Repeat", Repeat );
671 METHOD_FUNC( "VolumeSet", VolumeSet );
672 METHOD_FUNC( "VolumeGet", VolumeGet );
673 METHOD_FUNC( "PositionSet", PositionSet );
674 METHOD_FUNC( "PositionGet", PositionGet );
675 METHOD_FUNC( "GetStatus", GetStatus );
676 METHOD_FUNC( "GetMetadata", GetCurrentMetadata );
678 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
681 DBUS_METHOD( handle_tracklist )
683 if( dbus_message_is_method_call( p_from,
684 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
685 return handle_introspect_tracklist( p_conn, p_from, p_this );
687 /* here D-Bus method's names are associated to an handler */
689 METHOD_FUNC( "GetMetadata", GetMetadata );
690 METHOD_FUNC( "GetCurrentTrack", GetCurrentTrack );
691 METHOD_FUNC( "GetLength", GetLength );
692 METHOD_FUNC( "AddTrack", AddTrack );
693 METHOD_FUNC( "DelTrack", DelTrack );
694 METHOD_FUNC( "Loop", Loop );
695 METHOD_FUNC( "Random", Random );
697 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
700 /*****************************************************************************
701 * Open: initialize interface
702 *****************************************************************************/
704 static int Open( vlc_object_t *p_this )
705 { /* initialisation of the connection */
706 intf_thread_t *p_intf = (intf_thread_t*)p_this;
707 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
708 playlist_t *p_playlist;
709 DBusConnection *p_conn;
715 dbus_threads_init_default();
717 dbus_error_init( &error );
719 /* connect to the session bus */
720 p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
723 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
725 dbus_error_free( &error );
730 /* register a well-known name on the bus */
731 dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
732 if( dbus_error_is_set( &error ) )
734 msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:" " %s\n", error.message );
735 dbus_error_free( &error );
740 /* we register the objects */
741 dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
742 &vlc_dbus_root_vtable, p_this );
743 dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
744 &vlc_dbus_player_vtable, p_this );
745 dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
746 &vlc_dbus_tracklist_vtable, p_this );
748 dbus_connection_flush( p_conn );
750 p_playlist = pl_Yield( p_intf );
752 var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
754 pl_Release( p_playlist );
756 p_intf->pf_run = Run;
757 p_intf->p_sys = p_sys;
758 p_sys->p_conn = p_conn;
763 /*****************************************************************************
764 * Close: destroy interface
765 *****************************************************************************/
767 static void Close ( vlc_object_t *p_this )
769 intf_thread_t *p_intf = (intf_thread_t*) p_this;
770 playlist_t *p_playlist = pl_Yield( p_intf );;
773 var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
775 pl_Release( p_playlist );
777 dbus_connection_unref( p_intf->p_sys->p_conn );
779 free( p_intf->p_sys );
782 /*****************************************************************************
784 *****************************************************************************/
786 static void Run ( intf_thread_t *p_intf )
788 while( !p_intf->b_die )
790 msleep( INTF_IDLE_SLEEP );
791 dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
795 /*****************************************************************************
796 * TrackChange: Playlist item change callback
797 *****************************************************************************/
799 DBUS_SIGNAL( TrackChangeSignal )
800 { /* emit the metadata of the new item */
801 SIGNAL_INIT( "TrackChange" );
804 input_thread_t *p_input = (input_thread_t*) p_data;
805 GetInputMeta ( input_GetItem(p_input), &args );
810 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
811 vlc_value_t oldval, vlc_value_t newval, void *p_data )
813 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
814 intf_sys_t *p_sys = p_intf->p_sys;
815 playlist_t *p_playlist;
816 input_thread_t *p_input = NULL;
817 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
819 p_playlist = pl_Yield( p_intf );
821 p_input = p_playlist->p_input;
826 pl_Release( p_playlist );
830 vlc_object_yield( p_input );
832 pl_Release( p_playlist );
834 TrackChangeSignal( p_sys->p_conn, p_input );
836 vlc_object_release( p_input );
840 /*****************************************************************************
841 * GetInputMeta: Fill a DBusMessage with the given input item metadata
842 *****************************************************************************/
844 #define ADD_META( entry, type, data ) \
846 dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
847 NULL, &dict_entry ); \
848 dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
849 &ppsz_meta_items[entry] ); \
850 dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
851 type##_AS_STRING, &variant ); \
852 dbus_message_iter_append_basic( &variant, \
855 dbus_message_iter_close_container( &dict_entry, &variant ); \
856 dbus_message_iter_close_container( &dict, &dict_entry ); }\
858 #define ADD_VLC_META_STRING( entry, item ) \
859 ADD_META( entry, DBUS_TYPE_STRING, \
860 p_input->p_meta->psz_##item );
862 static int GetInputMeta( input_item_t* p_input,
863 DBusMessageIter *args )
864 { /*FIXME: Works only for already read metadata. */
866 DBusMessageIter dict, dict_entry, variant;
867 /* We need the track length to be expressed in seconds
868 * instead of milliseconds */
869 dbus_int64_t i_length = (p_input->i_duration / 1000);
872 const char* ppsz_meta_items[] =
874 "title", "artist", "genre", "copyright", "album", "tracknum",
875 "description", "rating", "date", "setting", "url", "language",
876 "nowplaying", "publisher", "encodedby", "arturl", "trackid",
877 "status", "URI", "length", "video-codec", "audio-codec",
878 "video-bitrate", "audio-bitrate", "audio-samplerate"
881 dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
883 ADD_VLC_META_STRING( 0, title );
884 ADD_VLC_META_STRING( 1, artist );
885 ADD_VLC_META_STRING( 2, genre );
886 ADD_VLC_META_STRING( 3, copyright );
887 ADD_VLC_META_STRING( 4, album );
888 ADD_VLC_META_STRING( 5, tracknum );
889 ADD_VLC_META_STRING( 6, description );
890 ADD_VLC_META_STRING( 7, rating );
891 ADD_VLC_META_STRING( 8, date );
892 ADD_VLC_META_STRING( 9, setting );
893 ADD_VLC_META_STRING( 10, url );
894 ADD_VLC_META_STRING( 11, language );
895 ADD_VLC_META_STRING( 12, nowplaying );
896 ADD_VLC_META_STRING( 13, publisher );
897 ADD_VLC_META_STRING( 14, encodedby );
898 ADD_VLC_META_STRING( 15, arturl );
899 ADD_VLC_META_STRING( 16, trackid );
901 ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
902 ADD_META( 18, DBUS_TYPE_STRING, p_input->psz_uri );
903 ADD_META( 19, DBUS_TYPE_INT64, i_length );
905 dbus_message_iter_close_container( args, &dict );
910 #undef ADD_VLC_META_STRING