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->p_input->i_id !=
383 p_playlist->status.p_item->p_input->i_id )
386 p_tested_item = playlist_GetNextLeaf( p_playlist,
387 p_playlist->p_root_onelevel,
393 pl_Release( p_playlist );
395 ADD_INT32( &i_position );
399 DBUS_METHOD( GetMetadata )
400 { //TODO reads int, returns a{sv}
404 dbus_error_init( &error );
406 dbus_int32_t i_position;
408 dbus_message_get_args( p_from, &error,
409 DBUS_TYPE_INT32, &i_position,
412 if( dbus_error_is_set( &error ) )
414 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
416 dbus_error_free( &error );
417 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
425 DBUS_METHOD( GetLength )
430 dbus_int32_t i_elements = 0;
431 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
432 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
433 playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist,
434 p_playlist->p_root_onelevel );
436 while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id )
439 p_tested_item = playlist_GetNextLeaf( p_playlist,
440 p_playlist->p_root_onelevel,
446 pl_Release( p_playlist );
448 ADD_INT32( &i_elements );
452 DBUS_METHOD( DelTrack )
457 dbus_error_init( &error );
459 dbus_int32_t i_position, i_count = 0;
460 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
461 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
463 dbus_message_get_args( p_from, &error,
464 DBUS_TYPE_INT32, &i_position,
467 if( dbus_error_is_set( &error ) )
469 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
471 dbus_error_free( &error );
472 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
475 while ( i_count < i_position )
478 p_tested_item = playlist_GetNextLeaf( p_playlist,
479 p_playlist->p_root_onelevel,
486 playlist_DeleteFromInput( p_playlist,
487 p_tested_item->p_input->i_id,
491 pl_Release( p_playlist );
496 /*****************************************************************************
497 * Introspection method
498 *****************************************************************************/
500 DBUS_METHOD( handle_introspect_root )
501 { /* handles introspection of /org/videolan/vlc */
504 ADD_STRING( &psz_introspection_xml_data_root );
508 DBUS_METHOD( handle_introspect_player )
512 ADD_STRING( &psz_introspection_xml_data_player );
516 DBUS_METHOD( handle_introspect_tracklist )
520 ADD_STRING( &psz_introspection_xml_data_tracklist );
524 /*****************************************************************************
525 * handle_*: answer to incoming messages
526 *****************************************************************************/
528 #define METHOD_FUNC( method, function ) \
529 else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
530 return function( p_conn, p_from, p_this )
532 DBUS_METHOD( handle_root )
535 if( dbus_message_is_method_call( p_from,
536 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
537 return handle_introspect_root( p_conn, p_from, p_this );
539 /* here D-Bus method's names are associated to an handler */
541 METHOD_FUNC( "Identity", Identity );
543 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
547 DBUS_METHOD( handle_player )
549 if( dbus_message_is_method_call( p_from,
550 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
551 return handle_introspect_player( p_conn, p_from, p_this );
553 /* here D-Bus method's names are associated to an handler */
555 METHOD_FUNC( "Prev", Prev );
556 METHOD_FUNC( "Next", Next );
557 METHOD_FUNC( "Quit", Quit );
558 METHOD_FUNC( "Stop", Stop );
559 METHOD_FUNC( "Play", Play );
560 METHOD_FUNC( "Pause", Pause );
561 METHOD_FUNC( "VolumeSet", VolumeSet );
562 METHOD_FUNC( "VolumeGet", VolumeGet );
563 METHOD_FUNC( "PositionSet", PositionSet );
564 METHOD_FUNC( "PositionGet", PositionGet );
565 METHOD_FUNC( "GetStatus", GetStatus );
567 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
570 DBUS_METHOD( handle_tracklist )
572 if( dbus_message_is_method_call( p_from,
573 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
574 return handle_introspect_tracklist( p_conn, p_from, p_this );
576 /* here D-Bus method's names are associated to an handler */
578 METHOD_FUNC( "GetMetadata", GetMetadata );
579 METHOD_FUNC( "GetCurrentTrack", GetCurrentTrack );
580 METHOD_FUNC( "GetLength", GetLength );
581 METHOD_FUNC( "AddTrack", AddTrack );
582 METHOD_FUNC( "DelTrack", DelTrack );
584 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
587 /*****************************************************************************
588 * Open: initialize interface
589 *****************************************************************************/
591 static int Open( vlc_object_t *p_this )
592 { /* initialisation of the connection */
593 intf_thread_t *p_intf = (intf_thread_t*)p_this;
594 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
595 playlist_t *p_playlist;
596 DBusConnection *p_conn;
602 dbus_threads_init_default();
604 dbus_error_init( &error );
606 /* connect to the session bus */
607 p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
610 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
612 dbus_error_free( &error );
617 /* register a well-known name on the bus */
618 dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
619 if( dbus_error_is_set( &error ) )
621 msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:" " %s\n", error.message );
622 dbus_error_free( &error );
627 /* we register the objects */
628 dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
629 &vlc_dbus_root_vtable, p_this );
630 dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
631 &vlc_dbus_player_vtable, p_this );
632 dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
633 &vlc_dbus_tracklist_vtable, p_this );
635 dbus_connection_flush( p_conn );
637 p_playlist = pl_Yield( p_intf );
639 var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
641 pl_Release( p_playlist );
643 p_intf->pf_run = Run;
644 p_intf->p_sys = p_sys;
645 p_sys->p_conn = p_conn;
650 /*****************************************************************************
651 * Close: destroy interface
652 *****************************************************************************/
654 static void Close ( vlc_object_t *p_this )
656 intf_thread_t *p_intf = (intf_thread_t*) p_this;
657 playlist_t *p_playlist = pl_Yield( p_intf );;
660 var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
662 pl_Release( p_playlist );
664 dbus_connection_unref( p_intf->p_sys->p_conn );
666 free( p_intf->p_sys );
669 /*****************************************************************************
671 *****************************************************************************/
673 static void Run ( intf_thread_t *p_intf )
675 while( !p_intf->b_die )
677 msleep( INTF_IDLE_SLEEP );
678 dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
682 /*****************************************************************************
683 * TrackChange: Playlist item change callback
684 *****************************************************************************/
686 DBUS_SIGNAL( TrackChangeSignal )
687 { /* emit the name of the new item */
688 SIGNAL_INIT( "TrackChange" );
691 input_thread_t *p_input = (input_thread_t*) p_data;
692 ADD_STRING( &input_GetItem(p_input)->psz_name );
697 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
698 vlc_value_t oldval, vlc_value_t newval, void *p_data )
700 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
701 intf_sys_t *p_sys = p_intf->p_sys;
702 playlist_t *p_playlist;
703 input_thread_t *p_input = NULL;
704 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
706 p_playlist = pl_Yield( p_intf );
708 p_input = p_playlist->p_input;
713 pl_Release( p_playlist );
717 vlc_object_yield( p_input );
719 pl_Release( p_playlist );
721 TrackChangeSignal( p_sys->p_conn, p_input );
723 vlc_object_release( p_input );