dbus_tracklist.c 14.2 KB
Newer Older
1
/*****************************************************************************
2
 * dbus_tracklist.c : dbus control module (mpris v2.2) - TrackList interface
3
 *****************************************************************************
4
5
 * Copyright © 2006-2011 Rafaël Carré
 * Copyright © 2007-2011 Mirsal Ennaime
6
 * Copyright © 2009-2011 The VideoLAN team
7
 * Copyright © 2013      Alex Merry
8
 *
Mirsal Ennaime's avatar
Mirsal Ennaime committed
9
10
 * Authors:    Mirsal Ennaime <mirsal at mirsal fr>
 *             Rafaël Carré <funman at videolanorg>
11
 *             Alex Merry <dev at randomguy3 me uk>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>

#include <assert.h>

#include "dbus_tracklist.h"
#include "dbus_common.h"

DBUS_METHOD( AddTrack )
40
{
41
42
43
44
45
    REPLY_INIT;

    DBusError error;
    dbus_error_init( &error );

Rafaël Carré's avatar
Rafaël Carré committed
46
    char *psz_mrl, *psz_aftertrack;
47
48
    dbus_bool_t b_play;

49
50
    bool b_append = false;
    size_t i_pos;
51
52
53
54

    size_t i_append_len  = sizeof( DBUS_MPRIS_APPEND );
    size_t i_notrack_len = sizeof( DBUS_MPRIS_NOTRACK );

55
56
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_STRING, &psz_mrl,
Rafaël Carré's avatar
Rafaël Carré committed
57
            DBUS_TYPE_OBJECT_PATH, &psz_aftertrack,
58
59
60
61
62
63
64
            DBUS_TYPE_BOOLEAN, &b_play,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
                error.message );
65

66
67
68
69
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

70
    if( !strncmp( DBUS_MPRIS_APPEND, psz_aftertrack, i_append_len ) )
71
        b_append = true;
72
    else if( !strncmp( DBUS_MPRIS_NOTRACK, psz_aftertrack, i_notrack_len ) )
73
        i_pos = 0;
74
    else if(sscanf( psz_aftertrack, MPRIS_TRACKID_FORMAT, &i_pos) == 1)
75
        ;
76
77
78
79
80
    else
    {
        msg_Warn( (vlc_object_t *) p_this,
                "AfterTrack: Invalid track ID \"%s\", appending instead",
                psz_aftertrack );
81
        b_append = true;
82
83
84
85
86
87
    }

    input_item_t *item = input_item_New( psz_mrl, NULL );
    if( unlikely(item == NULL) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;

88
89
90
91
92
93
94
95
96
    vlc_playlist_t *playlist = PL;
    vlc_playlist_Lock(playlist);
    size_t count = vlc_playlist_Count(playlist);
    if (b_append || i_pos > count)
        i_pos = count;
    vlc_playlist_InsertOne(playlist, i_pos, item);
    if (b_play)
        vlc_playlist_PlayAt(playlist, i_pos);
    vlc_playlist_Unlock(playlist);
97

98
    input_item_Release( item );
99
100
101
102

    REPLY_SEND;
}

103
DBUS_METHOD( GetTracksMetadata )
104
105
106
107
{
    REPLY_INIT;
    OUT_ARGUMENTS;

108
    size_t i_track_id;
109
    const char *psz_track_id = NULL;
110

111
    vlc_playlist_t *playlist = PL;
112

113
114
115
116
    DBusMessageIter in_args, track_ids, meta;
    dbus_message_iter_init( p_from, &in_args );

    if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &in_args ) )
117
    {
118
        msg_Err( (vlc_object_t*) p_this, "Invalid arguments" );
119
120
121
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

122
123
124
125
126
    dbus_message_iter_recurse( &in_args, &track_ids );
    dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "a{sv}", &meta );

    while( DBUS_TYPE_OBJECT_PATH ==
           dbus_message_iter_get_arg_type( &track_ids ) )
127
    {
128
129
130
        dbus_message_iter_get_basic( &track_ids, &psz_track_id );

        if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
131
132
133
134
135
            goto invalid_track_id;

        vlc_playlist_Lock(playlist);
        bool id_valid = i_track_id < vlc_playlist_Count(playlist);
        if (id_valid)
136
        {
137
138
139
140
141
142
143
            vlc_playlist_item_t *item = vlc_playlist_Get(playlist, i_track_id);
            GetInputMeta(playlist, item, &meta);
        }
        vlc_playlist_Unlock(playlist);
        if (!id_valid)
        {
invalid_track_id:
144
145
146
147
148
149
            msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s",
                                             psz_track_id );
            continue;
        }

        dbus_message_iter_next( &track_ids );
150
151
    }

152
    dbus_message_iter_close_container( &args, &meta );
153
154
155
    REPLY_SEND;
}

156
DBUS_METHOD( GoTo )
157
158
159
{
    REPLY_INIT;

160
    size_t i_track_id;
161
    const char *psz_track_id = NULL;
162
163
164
165
166

    DBusError error;
    dbus_error_init( &error );

    dbus_message_get_args( p_from, &error,
167
            DBUS_TYPE_OBJECT_PATH, &psz_track_id,
168
169
170
171
172
173
174
175
176
177
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

178
    if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
179
        goto invalid_track_id;
180

181
182
183
184
185
186
187
188
    vlc_playlist_t *playlist = PL;
    vlc_playlist_Lock(playlist);
    bool id_valid = i_track_id < vlc_playlist_Count(playlist);
    if (id_valid)
        vlc_playlist_PlayAt(playlist, i_track_id);
    vlc_playlist_Unlock(playlist);
    if (!id_valid)
        goto invalid_track_id;
189

190
    REPLY_SEND;
191
192
193
194

invalid_track_id:
    msg_Err( (vlc_object_t*) p_this, "Invalid track id %s", psz_track_id );
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
195
196
}

197
DBUS_METHOD( RemoveTrack )
198
199
200
201
202
{
    REPLY_INIT;

    DBusError error;
    dbus_error_init( &error );
203

204
    size_t i_id;
205
206
    char *psz_id = NULL;

207
    dbus_message_get_args( p_from, &error,
208
            DBUS_TYPE_OBJECT_PATH, &psz_id,
209
210
211
212
213
214
215
216
217
218
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

219
    if( 1 != sscanf( psz_id, MPRIS_TRACKID_FORMAT, &i_id ) )
220
        goto invalid_track_id;
221

222
223
224
225
226
227
228
229
    vlc_playlist_t *playlist = PL;
    vlc_playlist_Lock(playlist);
    bool valid_id = i_id < vlc_playlist_Count(playlist);
    if (valid_id)
        vlc_playlist_RemoveOne(playlist, i_id);
    vlc_playlist_Unlock(playlist);
    if (!valid_id)
        goto invalid_track_id;
230
231

    REPLY_SEND;
232
233
234
235

invalid_track_id:
    msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s", psz_id );
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
236
237
}

238
239
240
241
static int
MarshalTracks( intf_thread_t *p_intf, DBusMessageIter *container )
{
    DBusMessageIter tracks;
242
243
    char *psz_track_id = NULL;
    vlc_playlist_t *playlist = p_intf->p_sys->playlist;
244

245
246
    dbus_message_iter_open_container( container, DBUS_TYPE_ARRAY, "o",
                                      &tracks );
247

248
249
250
251
    vlc_playlist_Lock(playlist);
    size_t pl_size = vlc_playlist_Count(playlist);
    vlc_playlist_Unlock(playlist);
    for (size_t i = 0; i < pl_size; i++)
252
    {
253
        if (asprintf(&psz_track_id, MPRIS_TRACKID_FORMAT, i) == -1 ||
254
255
256
257
            !dbus_message_iter_append_basic( &tracks,
                                             DBUS_TYPE_OBJECT_PATH,
                                             &psz_track_id ) )
        {
258
259
            dbus_message_iter_abandon_container( container, &tracks );
            return VLC_ENOMEM;
260
261
262
263
264
        }

        free( psz_track_id );
    }

265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
    if( !dbus_message_iter_close_container( container, &tracks ) )
        return VLC_ENOMEM;

    return VLC_SUCCESS;
}

static int
MarshalCanEditTracks( intf_thread_t *p_intf, DBusMessageIter *container )
{
    VLC_UNUSED( p_intf );
    const dbus_bool_t b_ret = TRUE;

    if( !dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ) )
        return VLC_ENOMEM;

    return VLC_SUCCESS;
}

283
284
285
286
287
288
289
290
291
292
293
#define PROPERTY_MAPPING_BEGIN if( 0 ) {}
#define PROPERTY_GET_FUNC( prop, signature ) \
    else if( !strcmp( psz_property_name,  #prop ) ) { \
        if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, signature, &v ) ) \
            return DBUS_HANDLER_RESULT_NEED_MEMORY; \
        if( VLC_SUCCESS != Marshal##prop( p_this, &v ) ) { \
            dbus_message_iter_abandon_container( &args, &v ); \
            return DBUS_HANDLER_RESULT_NEED_MEMORY; \
        } \
        if( !dbus_message_iter_close_container( &args, &v ) ) \
            return DBUS_HANDLER_RESULT_NEED_MEMORY; \
294
295
    }

296
297
298
#define PROPERTY_SET_FUNC( prop ) \
    else if( !strcmp( psz_property_name,  #prop ) ) { \
        return prop##Set( p_conn, p_from, p_this ); \
299
    }
300
#define PROPERTY_MAPPING_END else { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326


DBUS_METHOD( GetProperty )
{
    DBusError error;

    char *psz_interface_name = NULL;
    char *psz_property_name  = NULL;

    dbus_error_init( &error );
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_STRING, &psz_interface_name,
            DBUS_TYPE_STRING, &psz_property_name,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
                                         error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
                                     psz_property_name );

327
328
329
330
331
332
333
334
    if( strcmp( psz_interface_name, DBUS_MPRIS_TRACKLIST_INTERFACE ) ) {
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    REPLY_INIT;
    OUT_ARGUMENTS;
    DBusMessageIter v;

335
    PROPERTY_MAPPING_BEGIN
336
337
    PROPERTY_GET_FUNC( Tracks, "ao" )
    PROPERTY_GET_FUNC( CanEditTracks, "b" )
338
    PROPERTY_MAPPING_END
339
340

    REPLY_SEND;
341
342
343
344
}

#undef PROPERTY_MAPPING_BEGIN
#undef PROPERTY_GET_FUNC
345
#undef PROPERTY_SET_FUNC
346
347
#undef PROPERTY_MAPPING_END

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#define ADD_PROPERTY( prop, signature ) \
    if( VLC_SUCCESS != AddProperty( (intf_thread_t*) p_this, \
                &dict, #prop, signature, Marshal##prop ) ) { \
        dbus_message_iter_abandon_container( &args, &dict ); \
        return VLC_ENOMEM; \
    }

DBUS_METHOD( GetAllProperties )
{
    REPLY_INIT;
    OUT_ARGUMENTS;

    DBusError error;
    DBusMessageIter dict;

    char *const psz_interface_name = NULL;

    dbus_error_init( &error );
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_STRING, &psz_interface_name,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
                                         error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}", &dict ) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;

    ADD_PROPERTY ( Tracks,        "ao" )
    ADD_PROPERTY ( CanEditTracks, "b"  )

    if( !dbus_message_iter_close_container( &args, &dict ))
        return DBUS_HANDLER_RESULT_NEED_MEMORY;

    REPLY_SEND;
}

#undef ADD_PROPERTY

392
393
394
395
396
397
398
#define METHOD_FUNC( interface, method, function ) \
    else if( dbus_message_is_method_call( p_from, interface, method ) )\
        return function( p_conn, p_from, p_this )

DBusHandlerResult
handle_tracklist ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
{
399
400
    if(0);

401
    METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Get",    GetProperty );
402
    METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "GetAll", GetAllProperties );
403
404
405

    /* here D-Bus method names are associated to an handler */

406
407
408
409
410
    METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GoTo",        GoTo );
    METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "AddTrack",    AddTrack );
    METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "RemoveTrack", RemoveTrack );
    METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GetTracksMetadata",
                                                  GetTracksMetadata );
411
412
413
414
415

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

#undef METHOD_FUNC
416
417
418
419
420
421
422
423
424
425

/**
 * PropertiesChangedSignal: synthetizes and sends the
 * org.freedesktop.DBus.Properties.PropertiesChanged signal
 */
static DBusHandlerResult
PropertiesChangedSignal( intf_thread_t    *p_intf,
                         vlc_dictionary_t *p_changed_properties )
{
    DBusConnection  *p_conn = p_intf->p_sys->p_conn;
426
    DBusMessageIter changed_properties, invalidated_properties;
427
428
429
430
431
432
433
434
435
    const char *psz_interface_name = DBUS_MPRIS_TRACKLIST_INTERFACE;

    SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
                 DBUS_MPRIS_OBJECT_PATH,
                 "PropertiesChanged" );

    OUT_ARGUMENTS;
    ADD_STRING( &psz_interface_name );

436
437
438
439
    if( unlikely(!dbus_message_iter_open_container( &args,
                                                    DBUS_TYPE_ARRAY, "{sv}",
                                                    &changed_properties )) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;
440

441
442
443
444
445
446
447
    if( unlikely(!dbus_message_iter_close_container( &args,
                                                     &changed_properties )) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;

    if( unlikely(!dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
                                                    &invalidated_properties )) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;
448
449


450
451
452
453
    if( vlc_dictionary_has_key( p_changed_properties, "Tracks" ) )
        dbus_message_iter_append_basic( &invalidated_properties,
                                        DBUS_TYPE_STRING,
                                        &(char const*){ "Tracks" } );
Mirsal Ennaime's avatar
Mirsal Ennaime committed
454

455
456
457
458
    if( unlikely(!dbus_message_iter_close_container( &args,
                    &invalidated_properties )) )
        return DBUS_HANDLER_RESULT_NEED_MEMORY;

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
    SIGNAL_SEND;
}

/**
 * TrackListPropertiesChangedEmit: Emits the
 * org.freedesktop.DBus.Properties.PropertiesChanged signal
 */
int TrackListPropertiesChangedEmit( intf_thread_t    * p_intf,
                                    vlc_dictionary_t * p_changed_properties )
{
    if( p_intf->p_sys->b_dead )
        return VLC_SUCCESS;

    PropertiesChangedSignal( p_intf, p_changed_properties );
    return VLC_SUCCESS;
}