1 /*****************************************************************************
2 * dbus.c : D-Bus control interface
3 *****************************************************************************
4 * Copyright (C) 2006 Rafaël Carré
7 * Author: Rafaël Carré <funman at videolanorg>
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.
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.
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 *****************************************************************************/
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
35 * macros to read incoming arguments
37 * explore different possible types (arrays..)
39 * what must we do if org.videolan.vlc already exist on the bus ?
40 * ( there is more than one vlc instance )
43 /*****************************************************************************
45 *****************************************************************************/
47 #include <dbus/dbus.h>
56 #include <vlc_interface.h>
58 #include <vlc_input.h>
59 #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 * );
71 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
72 vlc_value_t oldval, vlc_value_t newval, void *p_data );
76 DBusConnection *p_conn;
79 /*****************************************************************************
81 *****************************************************************************/
84 set_shortname( _("dbus"));
85 set_category( CAT_INTERFACE );
86 set_subcategory( SUBCAT_INTERFACE_CONTROL );
87 set_description( _("D-Bus control interface") );
88 set_capability( "interface", 0 );
89 set_callbacks( Open, Close );
92 /*****************************************************************************
94 *****************************************************************************/
96 DBUS_METHOD( PlaylistExport_XSPF )
97 { /*export playlist to an xspf file */
99 /* reads the filename to export to */
100 /* returns the status as int32:
109 dbus_error_init( &error );
114 dbus_message_get_args( p_from, &error,
115 DBUS_TYPE_STRING, &psz_file,
118 if( dbus_error_is_set( &error ) )
120 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
122 dbus_error_free( &error );
123 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
128 if( ( !playlist_IsEmpty( p_playlist ) ) &&
129 ( p_playlist->p_root_category->i_children > 0 ) )
131 if( playlist_Export( p_playlist, psz_file,
132 p_playlist->p_root_category->pp_children[0],
133 "export-xspf" ) == VLC_SUCCESS )
141 pl_Release( ((vlc_object_t*) p_this ) );
153 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
154 playlist_Stop( p_playlist );
155 pl_Release( ((vlc_object_t*) p_this) );
156 ((vlc_object_t*)p_this)->p_libvlc->b_die = VLC_TRUE;
160 DBUS_METHOD( PositionGet )
161 { /* returns position as an int in the range [0;1000] */
164 vlc_value_t position;
167 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
168 input_thread_t *p_input = p_playlist->p_input;
174 var_Get( p_input, "position", &position );
175 i_pos = position.f_float * 1000 ;
178 pl_Release( ((vlc_object_t*) p_this) );
182 DBUS_METHOD( PositionSet )
183 { /* set position from an int in the range [0;1000] */
186 vlc_value_t position;
190 dbus_error_init( &error );
192 dbus_message_get_args( p_from, &error,
193 DBUS_TYPE_INT32, &i_pos,
196 if( dbus_error_is_set( &error ) )
198 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
200 dbus_error_free( &error );
201 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
203 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
204 input_thread_t *p_input = p_playlist->p_input;
208 position.f_float = ((float)i_pos) / 1000;
209 var_Set( p_input, "position", position );
211 pl_Release( ((vlc_object_t*) p_this) );
215 DBUS_METHOD( VolumeGet )
216 { /* returns volume in percentage */
219 dbus_int32_t i_dbus_vol;
220 audio_volume_t i_vol;
221 /* 2nd argument of aout_VolumeGet is int32 */
222 aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
223 i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
224 ADD_INT32( &i_dbus_vol );
228 DBUS_METHOD( VolumeSet )
229 { /* set volume in percentage */
233 dbus_error_init( &error );
235 dbus_int32_t i_dbus_vol;
236 audio_volume_t i_vol;
238 dbus_message_get_args( p_from, &error,
239 DBUS_TYPE_INT32, &i_dbus_vol,
242 if( dbus_error_is_set( &error ) )
244 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
246 dbus_error_free( &error );
247 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
250 i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
251 aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
257 { /* next playlist item */
259 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
260 playlist_Next( p_playlist );
261 pl_Release( ((vlc_object_t*) p_this) );
266 { /* previous playlist item */
268 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
269 playlist_Prev( p_playlist );
270 pl_Release( ((vlc_object_t*) p_this) );
277 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
278 playlist_Stop( p_playlist );
279 pl_Release( ((vlc_object_t*) p_this) );
283 DBUS_METHOD( GetStatus )
284 { /* returns an int: 0=playing 1=paused 2=stopped */
288 dbus_int32_t i_status;
291 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
292 input_thread_t *p_input = p_playlist->p_input;
297 var_Get( p_input, "state", &val );
298 if( val.i_int == PAUSE_S )
300 else if( val.i_int == PLAYING_S )
304 pl_Release( p_playlist );
306 ADD_INT32( &i_status );
313 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
314 playlist_Pause( p_playlist );
315 pl_Release( p_playlist );
322 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
323 playlist_Play( p_playlist );
324 pl_Release( p_playlist );
328 /* Media Player information */
330 DBUS_METHOD( Identity )
334 char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
335 sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
336 ADD_STRING( &psz_identity );
337 free( psz_identity );
343 DBUS_METHOD( AddTrack )
344 { /* add the string to the playlist, and play it if the boolean is true */
348 dbus_error_init( &error );
353 dbus_message_get_args( p_from, &error,
354 DBUS_TYPE_STRING, &psz_mrl,
355 DBUS_TYPE_BOOLEAN, &b_play,
358 if( dbus_error_is_set( &error ) )
360 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
362 dbus_error_free( &error );
363 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
366 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
367 playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
368 ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , PLAYLIST_END, VLC_TRUE );
369 pl_Release( p_playlist );
374 DBUS_METHOD( GetCurrentTrack )
378 dbus_int32_t i_position = 0;
379 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
380 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
382 while ( p_tested_item->i_id != p_playlist->status.p_item->i_id )
385 p_tested_item = playlist_GetNextLeaf( p_playlist,
386 p_playlist->p_root_onelevel,
392 pl_Release( p_playlist );
394 ADD_INT32( &i_position );
398 DBUS_METHOD( GetMetadata )
399 { //TODO reads int, returns a{sv}
403 dbus_error_init( &error );
405 dbus_int32_t i_position;
407 dbus_message_get_args( p_from, &error,
408 DBUS_TYPE_INT32, &i_position,
411 if( dbus_error_is_set( &error ) )
413 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
415 dbus_error_free( &error );
416 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
424 DBUS_METHOD( GetLength )
429 dbus_int32_t i_elements = 0;
430 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
431 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
432 playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist, p_playlist->p_root_onelevel );
434 while ( p_tested_item->i_id != p_last_item->i_id )
437 p_tested_item = playlist_GetNextLeaf( p_playlist,
438 p_playlist->p_root_onelevel,
444 pl_Release( p_playlist );
446 ADD_INT32( &i_elements );
450 DBUS_METHOD( DelTrack )
452 /*FIXME: Doesn't work.*/
456 dbus_error_init( &error );
458 dbus_int32_t i_position, i_count = 0;
459 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
460 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
462 dbus_message_get_args( p_from, &error,
463 DBUS_TYPE_INT32, &i_position,
466 if( dbus_error_is_set( &error ) )
468 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
470 dbus_error_free( &error );
471 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
474 while ( i_count < i_position )
477 p_tested_item = playlist_GetNextLeaf( p_playlist,
478 p_playlist->p_root_onelevel,
484 playlist_NodeRemoveItem( p_playlist,
486 p_playlist->p_root_onelevel );
487 pl_Release( p_playlist );
492 /*****************************************************************************
493 * Introspection method
494 *****************************************************************************/
496 DBUS_METHOD( handle_introspect_root )
497 { /* handles introspection of /org/videolan/vlc */
500 ADD_STRING( &psz_introspection_xml_data_root );
504 DBUS_METHOD( handle_introspect_player )
508 ADD_STRING( &psz_introspection_xml_data_player );
512 DBUS_METHOD( handle_introspect_tracklist )
516 ADD_STRING( &psz_introspection_xml_data_tracklist );
520 /*****************************************************************************
521 * handle_*: answer to incoming messages
522 *****************************************************************************/
524 #define METHOD_FUNC( method, function ) \
525 else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
526 return function( p_conn, p_from, p_this )
528 DBUS_METHOD( handle_root )
531 if( dbus_message_is_method_call( p_from,
532 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
533 return handle_introspect_root( p_conn, p_from, p_this );
535 /* here D-Bus method's names are associated to an handler */
537 METHOD_FUNC( "Identity", Identity );
539 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
543 DBUS_METHOD( handle_player )
545 if( dbus_message_is_method_call( p_from,
546 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
547 return handle_introspect_player( p_conn, p_from, p_this );
549 /* here D-Bus method's names are associated to an handler */
551 METHOD_FUNC( "Prev", Prev );
552 METHOD_FUNC( "Next", Next );
553 METHOD_FUNC( "Quit", Quit );
554 METHOD_FUNC( "Stop", Stop );
555 METHOD_FUNC( "Play", Play );
556 METHOD_FUNC( "Pause", Pause );
557 METHOD_FUNC( "VolumeSet", VolumeSet );
558 METHOD_FUNC( "VolumeGet", VolumeGet );
559 METHOD_FUNC( "PositionSet", PositionSet );
560 METHOD_FUNC( "PositionGet", PositionGet );
561 METHOD_FUNC( "GetStatus", GetStatus );
563 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
566 DBUS_METHOD( handle_tracklist )
568 if( dbus_message_is_method_call( p_from,
569 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
570 return handle_introspect_tracklist( p_conn, p_from, p_this );
572 /* here D-Bus method's names are associated to an handler */
574 METHOD_FUNC( "GetMetadata", GetMetadata );
575 METHOD_FUNC( "GetCurrentTrack", GetCurrentTrack );
576 METHOD_FUNC( "GetLength", GetLength );
577 METHOD_FUNC( "AddTrack", AddTrack );
578 METHOD_FUNC( "DelTrack", DelTrack );
580 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
583 /*****************************************************************************
584 * Open: initialize interface
585 *****************************************************************************/
587 static int Open( vlc_object_t *p_this )
588 { /* initialisation of the connection */
589 intf_thread_t *p_intf = (intf_thread_t*)p_this;
590 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
591 playlist_t *p_playlist;
592 DBusConnection *p_conn;
598 dbus_threads_init_default();
600 dbus_error_init( &error );
602 /* connect to the session bus */
603 p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
606 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
608 dbus_error_free( &error );
613 /* register a well-known name on the bus */
614 dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
615 if( dbus_error_is_set( &error ) )
617 msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:" " %s\n", error.message );
618 dbus_error_free( &error );
623 /* we register the objects */
624 dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
625 &vlc_dbus_root_vtable, p_this );
626 dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
627 &vlc_dbus_player_vtable, p_this );
628 dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
629 &vlc_dbus_tracklist_vtable, p_this );
631 dbus_connection_flush( p_conn );
633 p_playlist = pl_Yield( p_intf );
635 var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
637 pl_Release( p_playlist );
639 p_intf->pf_run = Run;
640 p_intf->p_sys = p_sys;
641 p_sys->p_conn = p_conn;
646 /*****************************************************************************
647 * Close: destroy interface
648 *****************************************************************************/
650 static void Close ( vlc_object_t *p_this )
652 intf_thread_t *p_intf = (intf_thread_t*) p_this;
653 playlist_t *p_playlist = pl_Yield( p_intf );;
656 var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
658 pl_Release( p_playlist );
660 dbus_connection_unref( p_intf->p_sys->p_conn );
662 free( p_intf->p_sys );
665 /*****************************************************************************
667 *****************************************************************************/
669 static void Run ( intf_thread_t *p_intf )
671 while( !p_intf->b_die )
673 msleep( INTF_IDLE_SLEEP );
674 dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
678 /*****************************************************************************
679 * TrackChange: Playlist item change callback
680 *****************************************************************************/
682 DBUS_SIGNAL( TrackChangeSignal )
683 { /* emit the name of the new item */
684 SIGNAL_INIT( "TrackChange" );
687 input_thread_t *p_input = (input_thread_t*) p_data;
688 ADD_STRING( &input_GetItem(p_input)->psz_name );
693 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
694 vlc_value_t oldval, vlc_value_t newval, void *p_data )
696 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
697 intf_sys_t *p_sys = p_intf->p_sys;
698 playlist_t *p_playlist;
699 input_thread_t *p_input = NULL;
700 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
702 p_playlist = pl_Yield( p_intf );
704 p_input = p_playlist->p_input;
709 pl_Release( p_playlist );
713 vlc_object_yield( p_input );
715 pl_Release( p_playlist );
717 TrackChangeSignal( p_sys->p_conn, p_input );
719 vlc_object_release( p_input );