qml: fix scrollbar in NetworkHomeDisplay
[vlc.git] / modules / control / dbus / dbus_tracklist.c
1 /*****************************************************************************
2  * dbus_tracklist.c : dbus control module (mpris v2.2) - 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
8  *
9  * Authors:    Mirsal Ennaime <mirsal at mirsal fr>
10  *             Rafaël Carré <funman at videolanorg>
11  *             Alex Merry <dev at randomguy3 me uk>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33
34 #include <assert.h>
35
36 #include "dbus_tracklist.h"
37 #include "dbus_common.h"
38
39 DBUS_METHOD( AddTrack )
40 {
41     REPLY_INIT;
42
43     DBusError error;
44     dbus_error_init( &error );
45
46     char *psz_mrl, *psz_aftertrack;
47     dbus_bool_t b_play;
48
49     bool b_append = false;
50     size_t i_pos;
51
52     size_t i_append_len  = sizeof( DBUS_MPRIS_APPEND );
53     size_t i_notrack_len = sizeof( DBUS_MPRIS_NOTRACK );
54
55     dbus_message_get_args( p_from, &error,
56             DBUS_TYPE_STRING, &psz_mrl,
57             DBUS_TYPE_OBJECT_PATH, &psz_aftertrack,
58             DBUS_TYPE_BOOLEAN, &b_play,
59             DBUS_TYPE_INVALID );
60
61     if( dbus_error_is_set( &error ) )
62     {
63         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
64                 error.message );
65
66         dbus_error_free( &error );
67         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
68     }
69
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     else
77     {
78         msg_Warn( (vlc_object_t *) p_this,
79                 "AfterTrack: Invalid track ID \"%s\", appending instead",
80                 psz_aftertrack );
81         b_append = true;
82     }
83
84     input_item_t *item = input_item_New( psz_mrl, NULL );
85     if( unlikely(item == NULL) )
86         return DBUS_HANDLER_RESULT_NEED_MEMORY;
87
88     vlc_playlist_t *playlist = PL;
89     vlc_playlist_Lock(playlist);
90     size_t count = vlc_playlist_Count(playlist);
91     if (b_append || i_pos > count)
92         i_pos = count;
93     vlc_playlist_InsertOne(playlist, i_pos, item);
94     if (b_play)
95         vlc_playlist_PlayAt(playlist, i_pos);
96     vlc_playlist_Unlock(playlist);
97
98     input_item_Release( item );
99
100     REPLY_SEND;
101 }
102
103 DBUS_METHOD( GetTracksMetadata )
104 {
105     REPLY_INIT;
106     OUT_ARGUMENTS;
107
108     size_t i_track_id;
109     const char *psz_track_id = NULL;
110
111     vlc_playlist_t *playlist = PL;
112
113     DBusMessageIter in_args, track_ids, meta;
114     dbus_message_iter_init( p_from, &in_args );
115
116     if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &in_args ) )
117     {
118         msg_Err( (vlc_object_t*) p_this, "Invalid arguments" );
119         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
120     }
121
122     dbus_message_iter_recurse( &in_args, &track_ids );
123     dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "a{sv}", &meta );
124
125     while( DBUS_TYPE_OBJECT_PATH ==
126            dbus_message_iter_get_arg_type( &track_ids ) )
127     {
128         dbus_message_iter_get_basic( &track_ids, &psz_track_id );
129
130         if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
131             goto invalid_track_id;
132
133         vlc_playlist_Lock(playlist);
134         bool id_valid = i_track_id < vlc_playlist_Count(playlist);
135         if (id_valid)
136         {
137             vlc_playlist_item_t *item = vlc_playlist_Get(playlist, i_track_id);
138             GetInputMeta(playlist, item, &meta);
139         }
140         vlc_playlist_Unlock(playlist);
141         if (!id_valid)
142         {
143 invalid_track_id:
144             msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s",
145                                              psz_track_id );
146             continue;
147         }
148
149         dbus_message_iter_next( &track_ids );
150     }
151
152     dbus_message_iter_close_container( &args, &meta );
153     REPLY_SEND;
154 }
155
156 DBUS_METHOD( GoTo )
157 {
158     REPLY_INIT;
159
160     size_t i_track_id;
161     const char *psz_track_id = NULL;
162
163     DBusError error;
164     dbus_error_init( &error );
165
166     dbus_message_get_args( p_from, &error,
167             DBUS_TYPE_OBJECT_PATH, &psz_track_id,
168             DBUS_TYPE_INVALID );
169
170     if( dbus_error_is_set( &error ) )
171     {
172         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
173                 error.message );
174         dbus_error_free( &error );
175         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
176     }
177
178     if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
179         goto invalid_track_id;
180
181     vlc_playlist_t *playlist = PL;
182     vlc_playlist_Lock(playlist);
183     bool id_valid = i_track_id < vlc_playlist_Count(playlist);
184     if (id_valid)
185         vlc_playlist_PlayAt(playlist, i_track_id);
186     vlc_playlist_Unlock(playlist);
187     if (!id_valid)
188         goto invalid_track_id;
189
190     REPLY_SEND;
191
192 invalid_track_id:
193     msg_Err( (vlc_object_t*) p_this, "Invalid track id %s", psz_track_id );
194     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
195 }
196
197 DBUS_METHOD( RemoveTrack )
198 {
199     REPLY_INIT;
200
201     DBusError error;
202     dbus_error_init( &error );
203
204     size_t i_id;
205     char *psz_id = NULL;
206
207     dbus_message_get_args( p_from, &error,
208             DBUS_TYPE_OBJECT_PATH, &psz_id,
209             DBUS_TYPE_INVALID );
210
211     if( dbus_error_is_set( &error ) )
212     {
213         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
214                 error.message );
215         dbus_error_free( &error );
216         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
217     }
218
219     if( 1 != sscanf( psz_id, MPRIS_TRACKID_FORMAT, &i_id ) )
220         goto invalid_track_id;
221
222     vlc_playlist_t *playlist = PL;
223     vlc_playlist_Lock(playlist);
224     bool valid_id = i_id < vlc_playlist_Count(playlist);
225     if (valid_id)
226         vlc_playlist_RemoveOne(playlist, i_id);
227     vlc_playlist_Unlock(playlist);
228     if (!valid_id)
229         goto invalid_track_id;
230
231     REPLY_SEND;
232
233 invalid_track_id:
234     msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s", psz_id );
235     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
236 }
237
238 static int
239 MarshalTracks( intf_thread_t *p_intf, DBusMessageIter *container )
240 {
241     DBusMessageIter tracks;
242     char *psz_track_id = NULL;
243     vlc_playlist_t *playlist = p_intf->p_sys->playlist;
244
245     dbus_message_iter_open_container( container, DBUS_TYPE_ARRAY, "o",
246                                       &tracks );
247
248     vlc_playlist_Lock(playlist);
249     size_t pl_size = vlc_playlist_Count(playlist);
250     vlc_playlist_Unlock(playlist);
251     for (size_t i = 0; i < pl_size; i++)
252     {
253         if (asprintf(&psz_track_id, MPRIS_TRACKID_FORMAT, i) == -1 ||
254             !dbus_message_iter_append_basic( &tracks,
255                                              DBUS_TYPE_OBJECT_PATH,
256                                              &psz_track_id ) )
257         {
258             dbus_message_iter_abandon_container( container, &tracks );
259             return VLC_ENOMEM;
260         }
261
262         free( psz_track_id );
263     }
264
265     if( !dbus_message_iter_close_container( container, &tracks ) )
266         return VLC_ENOMEM;
267
268     return VLC_SUCCESS;
269 }
270
271 static int
272 MarshalCanEditTracks( intf_thread_t *p_intf, DBusMessageIter *container )
273 {
274     VLC_UNUSED( p_intf );
275     const dbus_bool_t b_ret = TRUE;
276
277     if( !dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ) )
278         return VLC_ENOMEM;
279
280     return VLC_SUCCESS;
281 }
282
283 #define PROPERTY_MAPPING_BEGIN if( 0 ) {}
284 #define PROPERTY_GET_FUNC( prop, signature ) \
285     else if( !strcmp( psz_property_name,  #prop ) ) { \
286         if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, signature, &v ) ) \
287             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
288         if( VLC_SUCCESS != Marshal##prop( p_this, &v ) ) { \
289             dbus_message_iter_abandon_container( &args, &v ); \
290             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
291         } \
292         if( !dbus_message_iter_close_container( &args, &v ) ) \
293             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
294     }
295
296 #define PROPERTY_SET_FUNC( prop ) \
297     else if( !strcmp( psz_property_name,  #prop ) ) { \
298         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 DBUS_METHOD( GetProperty )
304 {
305     DBusError error;
306
307     char *psz_interface_name = NULL;
308     char *psz_property_name  = NULL;
309
310     dbus_error_init( &error );
311     dbus_message_get_args( p_from, &error,
312             DBUS_TYPE_STRING, &psz_interface_name,
313             DBUS_TYPE_STRING, &psz_property_name,
314             DBUS_TYPE_INVALID );
315
316     if( dbus_error_is_set( &error ) )
317     {
318         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
319                                          error.message );
320         dbus_error_free( &error );
321         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
322     }
323
324     msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
325                                      psz_property_name );
326
327     if( strcmp( psz_interface_name, DBUS_MPRIS_TRACKLIST_INTERFACE ) ) {
328         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
329     }
330
331     REPLY_INIT;
332     OUT_ARGUMENTS;
333     DBusMessageIter v;
334
335     PROPERTY_MAPPING_BEGIN
336     PROPERTY_GET_FUNC( Tracks, "ao" )
337     PROPERTY_GET_FUNC( CanEditTracks, "b" )
338     PROPERTY_MAPPING_END
339
340     REPLY_SEND;
341 }
342
343 #undef PROPERTY_MAPPING_BEGIN
344 #undef PROPERTY_GET_FUNC
345 #undef PROPERTY_SET_FUNC
346 #undef PROPERTY_MAPPING_END
347
348 #define ADD_PROPERTY( prop, signature ) \
349     if( VLC_SUCCESS != AddProperty( (intf_thread_t*) p_this, \
350                 &dict, #prop, signature, Marshal##prop ) ) { \
351         dbus_message_iter_abandon_container( &args, &dict ); \
352         return VLC_ENOMEM; \
353     }
354
355 DBUS_METHOD( GetAllProperties )
356 {
357     REPLY_INIT;
358     OUT_ARGUMENTS;
359
360     DBusError error;
361     DBusMessageIter dict;
362
363     char *const psz_interface_name = NULL;
364
365     dbus_error_init( &error );
366     dbus_message_get_args( p_from, &error,
367             DBUS_TYPE_STRING, &psz_interface_name,
368             DBUS_TYPE_INVALID );
369
370     if( dbus_error_is_set( &error ) )
371     {
372         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
373                                          error.message );
374         dbus_error_free( &error );
375         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
376     }
377
378     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}", &dict ) )
379         return DBUS_HANDLER_RESULT_NEED_MEMORY;
380
381     ADD_PROPERTY ( Tracks,        "ao" )
382     ADD_PROPERTY ( CanEditTracks, "b"  )
383
384     if( !dbus_message_iter_close_container( &args, &dict ))
385         return DBUS_HANDLER_RESULT_NEED_MEMORY;
386
387     REPLY_SEND;
388 }
389
390 #undef ADD_PROPERTY
391
392 #define METHOD_FUNC( interface, method, function ) \
393     else if( dbus_message_is_method_call( p_from, interface, method ) )\
394         return function( p_conn, p_from, p_this )
395
396 DBusHandlerResult
397 handle_tracklist ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
398 {
399     if(0);
400
401     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Get",    GetProperty );
402     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "GetAll", GetAllProperties );
403
404     /* here D-Bus method names are associated to an handler */
405
406     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GoTo",        GoTo );
407     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "AddTrack",    AddTrack );
408     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "RemoveTrack", RemoveTrack );
409     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GetTracksMetadata",
410                                                   GetTracksMetadata );
411
412     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
413 }
414
415 #undef METHOD_FUNC
416
417 /**
418  * PropertiesChangedSignal: synthetizes and sends the
419  * org.freedesktop.DBus.Properties.PropertiesChanged signal
420  */
421 static DBusHandlerResult
422 PropertiesChangedSignal( intf_thread_t    *p_intf,
423                          vlc_dictionary_t *p_changed_properties )
424 {
425     DBusConnection  *p_conn = p_intf->p_sys->p_conn;
426     DBusMessageIter changed_properties, invalidated_properties;
427     const char *psz_interface_name = DBUS_MPRIS_TRACKLIST_INTERFACE;
428
429     SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
430                  DBUS_MPRIS_OBJECT_PATH,
431                  "PropertiesChanged" );
432
433     OUT_ARGUMENTS;
434     ADD_STRING( &psz_interface_name );
435
436     if( unlikely(!dbus_message_iter_open_container( &args,
437                                                     DBUS_TYPE_ARRAY, "{sv}",
438                                                     &changed_properties )) )
439         return DBUS_HANDLER_RESULT_NEED_MEMORY;
440
441     if( unlikely(!dbus_message_iter_close_container( &args,
442                                                      &changed_properties )) )
443         return DBUS_HANDLER_RESULT_NEED_MEMORY;
444
445     if( unlikely(!dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
446                                                     &invalidated_properties )) )
447         return DBUS_HANDLER_RESULT_NEED_MEMORY;
448
449
450     if( vlc_dictionary_has_key( p_changed_properties, "Tracks" ) )
451         dbus_message_iter_append_basic( &invalidated_properties,
452                                         DBUS_TYPE_STRING,
453                                         &(char const*){ "Tracks" } );
454
455     if( unlikely(!dbus_message_iter_close_container( &args,
456                     &invalidated_properties )) )
457         return DBUS_HANDLER_RESULT_NEED_MEMORY;
458
459     SIGNAL_SEND;
460 }
461
462 /**
463  * TrackListPropertiesChangedEmit: Emits the
464  * org.freedesktop.DBus.Properties.PropertiesChanged signal
465  */
466 int TrackListPropertiesChangedEmit( intf_thread_t    * p_intf,
467                                     vlc_dictionary_t * p_changed_properties )
468 {
469     if( p_intf->p_sys->b_dead )
470         return VLC_SUCCESS;
471
472     PropertiesChangedSignal( p_intf, p_changed_properties );
473     return VLC_SUCCESS;
474 }