1 /*****************************************************************************
2 * dbus_tracklist.c : dbus control module (mpris v2.1) - TrackList interface
3 *****************************************************************************
4 * Copyright © 2006-2011 Rafaël Carré
5 * Copyright © 2007-2011 Mirsal Ennaime
6 * Copyright © 2009-2011 The VideoLAN team
7 * Copyright © 2013 Alex Merry
10 * Authors: Mirsal Ennaime <mirsal at mirsal fr>
11 * Rafaël Carré <funman at videolanorg>
12 * Alex Merry <dev at randomguy3 me uk>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_playlist.h>
38 #include "dbus_tracklist.h"
39 #include "dbus_common.h"
42 * Retrieves the position of an input item in the playlist, given its id
44 * This function must be called with the playlist locked
46 * @param playlist_t* p_playlist The playlist
47 * @param input_item_t* i_input_id An input item ID
49 * @return int The position of the input item or a VLC error constant
51 static int getInputPosition( playlist_t* p_playlist, int i_input_id )
53 input_item_t* p_input = NULL;
56 assert( i_input_id >= 0 );
58 playlist_AssertLocked( p_playlist );
60 for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
62 p_input = p_playlist->current.p_elems[i]->p_input;
67 if( p_input->i_id == i_input_id )
74 DBUS_METHOD( AddTrack )
79 dbus_error_init( &error );
81 char *psz_mrl, *psz_aftertrack;
82 playlist_t *p_playlist = PL;
86 int i_mode = PLAYLIST_APPEND;
87 int i_pos = PLAYLIST_END;
89 size_t i_append_len = sizeof( DBUS_MPRIS_APPEND );
90 size_t i_notrack_len = sizeof( DBUS_MPRIS_NOTRACK );
92 dbus_message_get_args( p_from, &error,
93 DBUS_TYPE_STRING, &psz_mrl,
94 DBUS_TYPE_OBJECT_PATH, &psz_aftertrack,
95 DBUS_TYPE_BOOLEAN, &b_play,
98 if( dbus_error_is_set( &error ) )
100 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
103 dbus_error_free( &error );
104 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
107 if( !strncmp( DBUS_MPRIS_APPEND, psz_aftertrack, i_append_len ) )
109 i_mode = PLAYLIST_APPEND;
110 i_pos = PLAYLIST_END;
112 else if( !strncmp( DBUS_MPRIS_NOTRACK, psz_aftertrack, i_notrack_len ) )
114 i_mode = PLAYLIST_INSERT;
117 else if( 1 == sscanf( psz_aftertrack, MPRIS_TRACKID_FORMAT, &i_input_id ) )
120 int i_res = getInputPosition( p_playlist, i_input_id );
126 i_mode = PLAYLIST_INSERT;
132 msg_Warn( (vlc_object_t *) p_this,
133 "AfterTrack: Invalid track ID \"%s\", appending instead",
137 i_mode |= ( TRUE == b_play ) ? PLAYLIST_GO : 0;
138 playlist_Add( PL, psz_mrl, NULL, i_mode, i_pos, true, false );
143 DBUS_METHOD( GetTracksMetadata )
149 const char *psz_track_id = NULL;
151 playlist_t *p_playlist = PL;
152 input_item_t *p_input = NULL;
154 DBusMessageIter in_args, track_ids, meta;
155 dbus_message_iter_init( p_from, &in_args );
157 if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &in_args ) )
159 msg_Err( (vlc_object_t*) p_this, "Invalid arguments" );
160 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
163 dbus_message_iter_recurse( &in_args, &track_ids );
164 dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "a{sv}", &meta );
166 while( DBUS_TYPE_OBJECT_PATH ==
167 dbus_message_iter_get_arg_type( &track_ids ) )
169 dbus_message_iter_get_basic( &track_ids, &psz_track_id );
171 if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
173 msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s",
179 for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
181 p_input = p_playlist->current.p_elems[i]->p_input;
183 if( i_track_id == p_input->i_id )
185 GetInputMeta( p_input, &meta );
191 dbus_message_iter_next( &track_ids );
194 dbus_message_iter_close_container( &args, &meta );
203 const char *psz_track_id = NULL;
204 playlist_t *p_playlist = PL;
207 dbus_error_init( &error );
209 dbus_message_get_args( p_from, &error,
210 DBUS_TYPE_OBJECT_PATH, &psz_track_id,
213 if( dbus_error_is_set( &error ) )
215 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
217 dbus_error_free( &error );
218 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
221 if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
223 msg_Err( (vlc_object_t*) p_this, "Invalid track id %s", psz_track_id );
224 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
229 for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
231 if( i_track_id == p_playlist->current.p_elems[i]->p_input->i_id )
233 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, true,
234 p_playlist->current.p_elems[i]->p_parent,
235 p_playlist->current.p_elems[i] );
244 DBUS_METHOD( RemoveTrack )
249 dbus_error_init( &error );
253 playlist_t *p_playlist = PL;
254 input_item_t *p_input = NULL;
256 dbus_message_get_args( p_from, &error,
257 DBUS_TYPE_OBJECT_PATH, &psz_id,
260 if( dbus_error_is_set( &error ) )
262 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
264 dbus_error_free( &error );
265 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
268 if( 1 != sscanf( psz_id, MPRIS_TRACKID_FORMAT, &i_id ) )
270 msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s", psz_id );
271 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
276 for( i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
278 p_input = p_playlist->current.p_elems[i]->p_input;
280 if( i_id == p_input->i_id )
282 playlist_DeleteFromInput( p_playlist, p_input, true );
292 MarshalTracks( intf_thread_t *p_intf, DBusMessageIter *container )
294 DBusMessageIter tracks;
295 char *psz_track_id = NULL;
296 playlist_t *p_playlist = p_intf->p_sys->p_playlist;
297 input_item_t *p_input = NULL;
299 dbus_message_iter_open_container( container, DBUS_TYPE_ARRAY, "o",
304 for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
306 p_input = p_playlist->current.p_elems[i]->p_input;
308 if( ( -1 == asprintf( &psz_track_id,
309 MPRIS_TRACKID_FORMAT,
311 !dbus_message_iter_append_basic( &tracks,
312 DBUS_TYPE_OBJECT_PATH,
316 dbus_message_iter_abandon_container( container, &tracks );
320 free( psz_track_id );
325 if( !dbus_message_iter_close_container( container, &tracks ) )
332 MarshalCanEditTracks( intf_thread_t *p_intf, DBusMessageIter *container )
334 VLC_UNUSED( p_intf );
335 const dbus_bool_t b_ret = TRUE;
337 if( !dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ) )
343 #define PROPERTY_MAPPING_BEGIN if( 0 ) {}
344 #define PROPERTY_GET_FUNC( prop, signature ) \
345 else if( !strcmp( psz_property_name, #prop ) ) { \
346 if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, signature, &v ) ) \
347 return DBUS_HANDLER_RESULT_NEED_MEMORY; \
348 if( VLC_SUCCESS != Marshal##prop( p_this, &v ) ) { \
349 dbus_message_iter_abandon_container( &args, &v ); \
350 return DBUS_HANDLER_RESULT_NEED_MEMORY; \
352 if( !dbus_message_iter_close_container( &args, &v ) ) \
353 return DBUS_HANDLER_RESULT_NEED_MEMORY; \
356 #define PROPERTY_SET_FUNC( prop ) \
357 else if( !strcmp( psz_property_name, #prop ) ) { \
358 return prop##Set( p_conn, p_from, p_this ); \
360 #define PROPERTY_MAPPING_END else { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
363 DBUS_METHOD( GetProperty )
367 char *psz_interface_name = NULL;
368 char *psz_property_name = NULL;
370 dbus_error_init( &error );
371 dbus_message_get_args( p_from, &error,
372 DBUS_TYPE_STRING, &psz_interface_name,
373 DBUS_TYPE_STRING, &psz_property_name,
376 if( dbus_error_is_set( &error ) )
378 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
380 dbus_error_free( &error );
381 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
384 msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
387 if( strcmp( psz_interface_name, DBUS_MPRIS_TRACKLIST_INTERFACE ) ) {
388 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
395 PROPERTY_MAPPING_BEGIN
396 PROPERTY_GET_FUNC( Tracks, "ao" )
397 PROPERTY_GET_FUNC( CanEditTracks, "b" )
403 #undef PROPERTY_MAPPING_BEGIN
404 #undef PROPERTY_GET_FUNC
405 #undef PROPERTY_SET_FUNC
406 #undef PROPERTY_MAPPING_END
408 #define ADD_PROPERTY( prop, signature ) \
409 if( VLC_SUCCESS != AddProperty( (intf_thread_t*) p_this, \
410 &dict, #prop, signature, Marshal##prop ) ) { \
411 dbus_message_iter_abandon_container( &args, &dict ); \
415 DBUS_METHOD( GetAllProperties )
421 DBusMessageIter dict;
423 char *const psz_interface_name = NULL;
425 dbus_error_init( &error );
426 dbus_message_get_args( p_from, &error,
427 DBUS_TYPE_STRING, &psz_interface_name,
430 if( dbus_error_is_set( &error ) )
432 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
434 dbus_error_free( &error );
435 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
438 msg_Dbg( (vlc_object_t*) p_this, "Getting All properties" );
440 if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}", &dict ) )
441 return DBUS_HANDLER_RESULT_NEED_MEMORY;
443 ADD_PROPERTY ( Tracks, "ao" )
444 ADD_PROPERTY ( CanEditTracks, "b" )
446 if( !dbus_message_iter_close_container( &args, &dict ))
447 return DBUS_HANDLER_RESULT_NEED_MEMORY;
454 #define METHOD_FUNC( interface, method, function ) \
455 else if( dbus_message_is_method_call( p_from, interface, method ) )\
456 return function( p_conn, p_from, p_this )
459 handle_tracklist ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
463 METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Get", GetProperty );
464 METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "GetAll", GetAllProperties );
466 /* here D-Bus method names are associated to an handler */
468 METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GoTo", GoTo );
469 METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "AddTrack", AddTrack );
470 METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "RemoveTrack", RemoveTrack );
471 METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GetTracksMetadata",
474 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
480 * PropertiesChangedSignal: synthetizes and sends the
481 * org.freedesktop.DBus.Properties.PropertiesChanged signal
483 static DBusHandlerResult
484 PropertiesChangedSignal( intf_thread_t *p_intf,
485 vlc_dictionary_t *p_changed_properties )
487 DBusConnection *p_conn = p_intf->p_sys->p_conn;
488 DBusMessageIter changed_properties, invalidated_properties;
489 const char *psz_interface_name = DBUS_MPRIS_TRACKLIST_INTERFACE;
490 char **ppsz_properties = NULL;
491 int i_properties = 0;
493 SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
494 DBUS_MPRIS_OBJECT_PATH,
495 "PropertiesChanged" );
498 ADD_STRING( &psz_interface_name );
500 if( unlikely(!dbus_message_iter_open_container( &args,
501 DBUS_TYPE_ARRAY, "{sv}",
502 &changed_properties )) )
503 return DBUS_HANDLER_RESULT_NEED_MEMORY;
505 if( unlikely(!dbus_message_iter_close_container( &args,
506 &changed_properties )) )
507 return DBUS_HANDLER_RESULT_NEED_MEMORY;
509 if( unlikely(!dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
510 &invalidated_properties )) )
511 return DBUS_HANDLER_RESULT_NEED_MEMORY;
513 i_properties = vlc_dictionary_keys_count( p_changed_properties );
514 ppsz_properties = vlc_dictionary_all_keys( p_changed_properties );
516 if( unlikely(!ppsz_properties) )
518 dbus_message_iter_abandon_container( &args, &invalidated_properties );
519 return DBUS_HANDLER_RESULT_NEED_MEMORY;
522 for( int i = 0; i < i_properties; i++ )
524 if( !strcmp( ppsz_properties[i], "Tracks" ) )
525 dbus_message_iter_append_basic( &invalidated_properties,
527 &ppsz_properties[i] );
529 free( ppsz_properties[i] );
532 free( ppsz_properties );
534 if( unlikely(!dbus_message_iter_close_container( &args,
535 &invalidated_properties )) )
536 return DBUS_HANDLER_RESULT_NEED_MEMORY;
542 * TrackListPropertiesChangedEmit: Emits the
543 * org.freedesktop.DBus.Properties.PropertiesChanged signal
545 int TrackListPropertiesChangedEmit( intf_thread_t * p_intf,
546 vlc_dictionary_t * p_changed_properties )
548 if( p_intf->p_sys->b_dead )
551 PropertiesChangedSignal( p_intf, p_changed_properties );