diff --git a/modules/control/dbus/dbus.c b/modules/control/dbus/dbus.c index 47405c2d7e..999416bc9b 100644 --- a/modules/control/dbus/dbus.c +++ b/modules/control/dbus/dbus.c @@ -89,6 +89,10 @@ static const DBusObjectPathVTable dbus_mpris_vtable = { typedef struct { int signal; + union { + tracklist_append_event_t *items_appended; + tracklist_remove_event_t *items_removed; + }; } callback_info_t; enum @@ -639,6 +643,14 @@ static void ProcessEvents( intf_thread_t *p_intf, vlc_dictionary_init( &tracklist_properties, 0 ); vlc_dictionary_init( &root_properties, 0 ); + // In case multiple *_ITEM_APPEND or *_ITEM_DELETED events appear on the + // list, the elements in their respective map values will be linked in + // order. + // We keep the tail of the list in order to append the elements to the end + // of each list. + tracklist_append_event_t *last_append = NULL; + tracklist_remove_event_t *last_remove = NULL; + for( int i = 0; i < i_events; i++ ) { switch( p_events[i]->signal ) @@ -653,9 +665,27 @@ static void ProcessEvents( intf_thread_t *p_intf, vlc_dictionary_insert( &player_properties, "Metadata", NULL ); break; case SIGNAL_PLAYLIST_ITEM_APPEND: + if( !last_append ) { + assert (!vlc_dictionary_has_key( &tracklist_properties, "TrackAdded" ) ); + vlc_dictionary_insert( &tracklist_properties, "TrackAdded", p_events[i]->items_appended ); + + last_append = p_events[i]->items_appended; + } else { + last_append->change_ev.next = &p_events[i]->items_appended->change_ev; + last_append = p_events[i]->items_appended; + } ProcessPlaylistChanged( p_intf, &player_properties, &tracklist_properties ); break; case SIGNAL_PLAYLIST_ITEM_DELETED: + if( !last_remove ) { + assert (!vlc_dictionary_has_key( &tracklist_properties, "TrackRemoved" ) ); + vlc_dictionary_insert( &tracklist_properties, "TrackRemoved", p_events[i]->items_removed ); + + last_remove = p_events[i]->items_removed; + } else { + last_remove->change_ev.next = &p_events[i]->items_removed->change_ev; + last_remove = p_events[i]->items_removed; + } ProcessPlaylistChanged( p_intf, &player_properties, &tracklist_properties ); break; case SIGNAL_VOLUME_MUTED: @@ -1018,18 +1048,22 @@ playlist_on_items_added(vlc_playlist_t *playlist, size_t index, vlc_playlist_item_t *const items[], size_t count, void *data) { + tracklist_append_event_t *append_event = tracklist_append_event_create(index, items, count); add_event_signal(data, - &(callback_info_t){ .signal = SIGNAL_PLAYLIST_ITEM_APPEND }); - (void) playlist; (void) index; (void) items; (void) count; + &(callback_info_t){ .signal = SIGNAL_PLAYLIST_ITEM_APPEND, + .items_appended = append_event }); + (void) playlist; } static void playlist_on_items_removed(vlc_playlist_t *playlist, size_t index, size_t count, void *data) { + tracklist_remove_event_t *remove_event = tracklist_remove_event_create(index, count); add_event_signal(data, - &(callback_info_t){ .signal = SIGNAL_PLAYLIST_ITEM_DELETED }); - (void) playlist; (void) index; (void) count; + &(callback_info_t){ .signal = SIGNAL_PLAYLIST_ITEM_DELETED, + .items_removed = remove_event }); + (void) playlist; } static void diff --git a/modules/control/dbus/dbus_tracklist.c b/modules/control/dbus/dbus_tracklist.c index f33b9d5ce7..3ffd46efd8 100644 --- a/modules/control/dbus/dbus_tracklist.c +++ b/modules/control/dbus/dbus_tracklist.c @@ -36,6 +36,42 @@ #include "dbus_tracklist.h" #include "dbus_common.h" + +tracklist_append_event_t *tracklist_append_event_create(size_t index, vlc_playlist_item_t *const items[], size_t count) { + tracklist_append_event_t* result = malloc(sizeof(tracklist_append_event_t) + sizeof(vlc_playlist_item_t *[count])); + if (!result) + return result; + + *result = (tracklist_append_event_t) { .change_ev = { .index = index, .count = count } }; + for (size_t i = 0; i < count; ++i) { + result->items[i] = items[i]; + vlc_playlist_item_Hold(items[i]); + } + return result; +} + +tracklist_remove_event_t *tracklist_remove_event_create(size_t index, size_t count) { + tracklist_remove_event_t* result = malloc(sizeof(tracklist_remove_event_t)); + if (!result) + return result; + + *result = (tracklist_remove_event_t) { .change_ev = { .index = index, .count = count } }; + return result; +} + +void tracklist_append_event_destroy(tracklist_append_event_t *event) { + if (!event) + return; + for (size_t i = 0; i < event->change_ev.count; ++i) { + vlc_playlist_item_Release(event->items[i]); + } + free(event); +} + +void tracklist_remove_event_destroy(tracklist_remove_event_t *event) { + free(event); +} + DBUS_METHOD( AddTrack ) { REPLY_INIT; @@ -242,11 +278,29 @@ invalid_track_id: return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +static int MarshalTrack( DBusMessageIter *iter, size_t index ) +{ + char *psz_track_id = NULL; + int ret = VLC_SUCCESS; + + if (asprintf(&psz_track_id, MPRIS_TRACKID_FORMAT, index) == -1) + ret = VLC_ENOMEM; + + if (ret == VLC_SUCCESS && + !dbus_message_iter_append_basic( iter, + DBUS_TYPE_OBJECT_PATH, + &psz_track_id ) ) + { + ret = VLC_ENOMEM; + } + free( psz_track_id ); + return ret; +} + static int MarshalTracks( intf_thread_t *p_intf, DBusMessageIter *container ) { DBusMessageIter tracks; - char *psz_track_id = NULL; vlc_playlist_t *playlist = p_intf->p_sys->playlist; dbus_message_iter_open_container( container, DBUS_TYPE_ARRAY, "o", @@ -257,16 +311,12 @@ MarshalTracks( intf_thread_t *p_intf, DBusMessageIter *container ) vlc_playlist_Unlock(playlist); for (size_t i = 0; i < pl_size; i++) { - if (asprintf(&psz_track_id, MPRIS_TRACKID_FORMAT, i) == -1 || - !dbus_message_iter_append_basic( &tracks, - DBUS_TYPE_OBJECT_PATH, - &psz_track_id ) ) + int err = MarshalTrack( &tracks, i ); + if (err != VLC_SUCCESS) { dbus_message_iter_abandon_container( container, &tracks ); - return VLC_ENOMEM; + return err; } - - free( psz_track_id ); } if( !dbus_message_iter_close_container( container, &tracks ) ) @@ -467,8 +517,64 @@ PropertiesChangedSignal( intf_thread_t *p_intf, } /** - * TrackListPropertiesChangedEmit: Emits the - * org.freedesktop.DBus.Properties.PropertiesChanged signal + * TrackAddedSignal: synthetizes and sends the + * org.mpris.MediaPlayer2.TrackList.TrackAdded signal + */ +static DBusHandlerResult +TrackAddedSignal( intf_thread_t *p_intf, + size_t index, + vlc_playlist_item_t *item ) +{ + DBusConnection *p_conn = p_intf->p_sys->p_conn; + DBusMessageIter meta; + + SIGNAL_INIT( "MediaPlayer2.TrackList", + DBUS_MPRIS_OBJECT_PATH, + "TrackAdded" ); + + OUT_ARGUMENTS; + + if( unlikely(!dbus_message_iter_open_container( &args, + DBUS_TYPE_ARRAY, "a{sv}", + &meta )) ) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + GetInputMeta(index, item, &meta); + + if( unlikely(!dbus_message_iter_close_container( &args, + &meta )) ) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if ( MarshalTrack( &args, index ) != VLC_SUCCESS ) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + SIGNAL_SEND; +} + +/** + * TrackRemovedSignal: synthetizes and sends the + * org.mpris.MediaPlayer2.TrackList.TrackRemoved signal + */ +static DBusHandlerResult +TrackRemovedSignal( intf_thread_t *p_intf, size_t index ) +{ + DBusConnection *p_conn = p_intf->p_sys->p_conn; + + SIGNAL_INIT( "MediaPlayer2.TrackList", + DBUS_MPRIS_OBJECT_PATH, + "TrackRemoved" ); + + OUT_ARGUMENTS; + + if ( MarshalTrack( &args, index ) != VLC_SUCCESS ) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + SIGNAL_SEND; +} +/** + * TrackListPropertiesChangedEmit: Emits the following signals: + * - org.freedesktop.DBus.Properties.PropertiesChanged + * - org.mpris.MediaPlayer2.TrackList.TrackAdded */ int TrackListPropertiesChangedEmit( intf_thread_t * p_intf, vlc_dictionary_t * p_changed_properties ) @@ -477,5 +583,35 @@ int TrackListPropertiesChangedEmit( intf_thread_t * p_intf, return VLC_SUCCESS; PropertiesChangedSignal( p_intf, p_changed_properties ); + + if( vlc_dictionary_has_key( p_changed_properties, "TrackAdded" ) ) { + tracklist_append_event_t *added_tracks = + vlc_dictionary_value_for_key( p_changed_properties, "TrackAdded" ); + + while (added_tracks) { + for (size_t i = 0; i < added_tracks->change_ev.count; ++i) { + TrackAddedSignal( p_intf, + added_tracks->change_ev.index + i, + added_tracks->items[i] ); + } + added_tracks = tracklist_append_event_next(added_tracks); + } + tracklist_append_event_destroy( added_tracks ); + } + + if( vlc_dictionary_has_key( p_changed_properties, "TrackRemoved" ) ) { + tracklist_remove_event_t *removed_tracks = + vlc_dictionary_value_for_key( p_changed_properties, "TrackRemoved" ); + + while (removed_tracks) { + for (size_t i = 0; i < removed_tracks->change_ev.count; ++i) { + TrackRemovedSignal( p_intf, removed_tracks->change_ev.index + i ); + } + removed_tracks = tracklist_remove_event_next(removed_tracks); + } + + tracklist_remove_event_destroy( removed_tracks ); + } + return VLC_SUCCESS; } diff --git a/modules/control/dbus/dbus_tracklist.h b/modules/control/dbus/dbus_tracklist.h index c4bf6ac0bc..2b4a71fa30 100644 --- a/modules/control/dbus/dbus_tracklist.h +++ b/modules/control/dbus/dbus_tracklist.h @@ -36,6 +36,61 @@ #define DBUS_MPRIS_NOTRACK "/org/mpris/MediaPlayer2/TrackList/NoTrack" #define DBUS_MPRIS_APPEND "/org/mpris/MediaPlayer2/TrackList/Append" +struct tracklist_change_event { + size_t index; + size_t count; + struct tracklist_change_event *next; +}; + +struct tracklist_append_event { + struct tracklist_change_event change_ev; + vlc_playlist_item_t *items[]; +}; + +struct tracklist_remove_event { + struct tracklist_change_event change_ev; +}; + +typedef struct tracklist_append_event tracklist_append_event_t; +typedef struct tracklist_remove_event tracklist_remove_event_t; + +/* Creates an event holding what items have been appended to a tracklist. + * The event data will be used to generate TrackAdded DBus signals later on. + */ +tracklist_append_event_t * +tracklist_append_event_create( size_t index, + vlc_playlist_item_t *const items[], + size_t count ); + +/* Creates an event holding what items have been removed from a tracklist. + * The event data will be used to generate TrackRemoved DBus signals later on. + */ +tracklist_remove_event_t * +tracklist_remove_event_create( size_t index, size_t count ); + +/* Releases any resources reserved for this event */ +void tracklist_append_event_destroy( tracklist_append_event_t *event ); +void tracklist_remove_event_destroy( tracklist_remove_event_t *event ); + +/* Gets next event in the list */ +static tracklist_append_event_t * +tracklist_append_event_next( tracklist_append_event_t *event ) { + if( !event ) + return NULL; + char *p = (char *) event->change_ev.next; + return (tracklist_append_event_t *) + (p - offsetof(struct tracklist_append_event, change_ev)); +} + +static tracklist_remove_event_t * +tracklist_remove_event_next( tracklist_remove_event_t *event ) { + if( !event ) + return NULL; + char *p = (char *) event->change_ev.next; + return (tracklist_remove_event_t *) + (p - offsetof(struct tracklist_remove_event, change_ev)); +} + /* Handle incoming dbus messages */ DBusHandlerResult handle_tracklist ( DBusConnection *p_conn, DBusMessage *p_from,