]> git.sesse.net Git - vlc/blob - modules/control/dbus/dbus_tracklist.c
dbus: Fix the tracklist's PropertyChanged signal
[vlc] / modules / control / dbus / dbus_tracklist.c
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  * $Id$
8  *
9  * Authors:    Mirsal Ennaime <mirsal at mirsal fr>
10  *             Rafaël Carré <funman at videolanorg>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_playlist.h>
33
34 #include <assert.h>
35
36 #include "dbus_tracklist.h"
37 #include "dbus_common.h"
38
39 DBUS_METHOD( AddTrack )
40 { /* add the string to the playlist, and play it if the boolean is true */
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     dbus_message_get_args( p_from, &error,
50             DBUS_TYPE_STRING, &psz_mrl,
51             DBUS_TYPE_OBJECT_PATH, &psz_aftertrack,
52             DBUS_TYPE_BOOLEAN, &b_play,
53             DBUS_TYPE_INVALID );
54
55     if( dbus_error_is_set( &error ) )
56     {
57         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
58                 error.message );
59         dbus_error_free( &error );
60         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
61     }
62
63 #warning psz_aftertrack is not used
64     playlist_Add( PL, psz_mrl, NULL, PLAYLIST_APPEND |
65             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
66             PLAYLIST_END, true, false );
67
68     REPLY_SEND;
69 }
70
71 DBUS_METHOD( GetTracksMetadata )
72 {
73     REPLY_INIT;
74     OUT_ARGUMENTS;
75
76     int i_track_id = -1;
77     const char *psz_track_id = NULL;
78
79     playlist_t   *p_playlist = PL;
80     input_item_t *p_input = NULL;
81
82     DBusMessageIter in_args, track_ids, meta;
83     dbus_message_iter_init( p_from, &in_args );
84
85     if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &in_args ) )
86     {
87         msg_Err( (vlc_object_t*) p_this, "Invalid arguments" );
88         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
89     }
90
91     dbus_message_iter_recurse( &in_args, &track_ids );
92     dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "a{sv}", &meta );
93
94     while( DBUS_TYPE_OBJECT_PATH ==
95            dbus_message_iter_get_arg_type( &track_ids ) )
96     {
97         dbus_message_iter_get_basic( &track_ids, &psz_track_id );
98
99         if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
100         {
101             msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s",
102                                              psz_track_id );
103             continue;
104         }
105
106         PL_LOCK;
107         for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
108         {
109             p_input = p_playlist->current.p_elems[i]->p_input;
110
111             if( i_track_id == p_input->i_id )
112             {
113                 GetInputMeta( p_input, &meta );
114                 break;
115             }
116         }
117         PL_UNLOCK;
118
119         dbus_message_iter_next( &track_ids );
120     }
121
122     dbus_message_iter_close_container( &args, &meta );
123     REPLY_SEND;
124 }
125
126 DBUS_METHOD( GoTo )
127 {
128     REPLY_INIT;
129
130     int i_track_id = -1;
131     const char *psz_track_id = NULL;
132     playlist_t *p_playlist = PL;
133
134     DBusError error;
135     dbus_error_init( &error );
136
137     dbus_message_get_args( p_from, &error,
138             DBUS_TYPE_OBJECT_PATH, &psz_track_id,
139             DBUS_TYPE_INVALID );
140
141     if( dbus_error_is_set( &error ) )
142     {
143         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
144                 error.message );
145         dbus_error_free( &error );
146         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
147     }
148
149     if( 1 != sscanf( psz_track_id, MPRIS_TRACKID_FORMAT, &i_track_id ) )
150     {
151         msg_Err( (vlc_object_t*) p_this, "Invalid track id %s", psz_track_id );
152         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
153     }
154
155     PL_LOCK;
156
157     for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
158     {
159         if( i_track_id == p_playlist->current.p_elems[i]->p_input->i_id )
160         {
161             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, true,
162                               p_playlist->current.p_elems[i]->p_parent,
163                               p_playlist->current.p_elems[i] );
164             break;
165         }
166     }
167
168     PL_UNLOCK;
169     REPLY_SEND;
170 }
171
172 DBUS_METHOD( RemoveTrack )
173 {
174     REPLY_INIT;
175
176     DBusError error;
177     dbus_error_init( &error );
178
179     int   i_id = -1, i;
180     char *psz_id = NULL;
181     playlist_t *p_playlist = PL;
182     input_item_t *p_input  = NULL;
183
184     dbus_message_get_args( p_from, &error,
185             DBUS_TYPE_OBJECT_PATH, &psz_id,
186             DBUS_TYPE_INVALID );
187
188     if( dbus_error_is_set( &error ) )
189     {
190         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
191                 error.message );
192         dbus_error_free( &error );
193         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
194     }
195
196     if( 1 != sscanf( psz_id, MPRIS_TRACKID_FORMAT, &i_id ) )
197     {
198         msg_Err( (vlc_object_t*) p_this, "Invalid track id: %s", psz_id );
199         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
200     }
201
202     PL_LOCK;
203
204     for( i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
205     {
206         p_input = p_playlist->current.p_elems[i]->p_input;
207
208         if( i_id == p_input->i_id )
209         {
210             playlist_DeleteFromInput( p_playlist, p_input, true );
211             break;
212         }
213     }
214
215     PL_UNLOCK;
216     REPLY_SEND;
217 }
218
219 DBUS_METHOD( Tracks )
220 { /* Tracks property */
221     VLC_UNUSED( p_this );
222
223     REPLY_INIT;
224     OUT_ARGUMENTS;
225
226     DBusMessageIter tracks, v;
227     char *psz_track_id = NULL;
228     playlist_t   *p_playlist   = PL;
229     input_item_t *p_input      = NULL;
230
231     dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "ao", &v );
232     dbus_message_iter_open_container( &v,    DBUS_TYPE_ARRAY, "o", &tracks );
233
234     PL_LOCK;
235
236     for( int i = 0; i < playlist_CurrentSize( p_playlist ); i++ )
237     {
238         p_input = p_playlist->current.p_elems[i]->p_input;
239
240         if( ( -1 == asprintf( &psz_track_id,
241                               MPRIS_TRACKID_FORMAT,
242                               p_input->i_id ) ) ||
243             !dbus_message_iter_append_basic( &tracks,
244                                              DBUS_TYPE_OBJECT_PATH,
245                                              &psz_track_id ) )
246         {
247             PL_UNLOCK;
248             dbus_message_iter_abandon_container( &v, &tracks );
249             dbus_message_iter_abandon_container( &args, &v );
250             return DBUS_HANDLER_RESULT_NEED_MEMORY;
251         }
252
253         free( psz_track_id );
254     }
255
256     PL_UNLOCK;
257
258     if( !dbus_message_iter_close_container( &v, &tracks ) ||
259         !dbus_message_iter_close_container( &args, &v ) )
260         return DBUS_HANDLER_RESULT_NEED_MEMORY;
261
262     REPLY_SEND;
263 }
264
265 DBUS_METHOD( CanEditTracks )
266 { /* CanEditTracks property */
267     VLC_UNUSED( p_this );
268     REPLY_INIT;
269     OUT_ARGUMENTS;
270
271     DBusMessageIter v;
272     const dbus_bool_t b_ret = TRUE;
273
274     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "b", &v ) )
275         return DBUS_HANDLER_RESULT_NEED_MEMORY;
276
277     if( !dbus_message_iter_append_basic( &v, DBUS_TYPE_BOOLEAN, &b_ret ) )
278     {
279         dbus_message_iter_abandon_container( &args, &v );
280         return DBUS_HANDLER_RESULT_NEED_MEMORY;
281     }
282
283     if( !dbus_message_iter_close_container( &args, &v ) )
284     {
285         dbus_message_iter_abandon_container( &args, &v );
286         return DBUS_HANDLER_RESULT_NEED_MEMORY;
287     }
288
289     REPLY_SEND;
290 }
291
292 #define PROPERTY_MAPPING_BEGIN if( 0 ) {}
293 #define PROPERTY_FUNC( interface, property, function ) \
294     else if( !strcmp( psz_interface_name, interface ) && \
295              !strcmp( psz_property_name,  property ) ) \
296         return function( p_conn, p_from, p_this );
297 #define PROPERTY_MAPPING_END return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
298
299 DBUS_METHOD( GetProperty )
300 {
301     DBusError error;
302
303     char *psz_interface_name = NULL;
304     char *psz_property_name  = NULL;
305
306     dbus_error_init( &error );
307     dbus_message_get_args( p_from, &error,
308             DBUS_TYPE_STRING, &psz_interface_name,
309             DBUS_TYPE_STRING, &psz_property_name,
310             DBUS_TYPE_INVALID );
311
312     if( dbus_error_is_set( &error ) )
313     {
314         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
315                                          error.message );
316         dbus_error_free( &error );
317         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
318     }
319
320     msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
321                                      psz_property_name );
322
323     PROPERTY_MAPPING_BEGIN
324     PROPERTY_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "Tracks", Tracks )
325     PROPERTY_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "CanEditTracks",
326                                                    CanEditTracks )
327     PROPERTY_MAPPING_END
328 }
329
330 #undef PROPERTY_MAPPING_BEGIN
331 #undef PROPERTY_GET_FUNC
332 #undef PROPERTY_MAPPING_END
333
334 #define METHOD_FUNC( interface, method, function ) \
335     else if( dbus_message_is_method_call( p_from, interface, method ) )\
336         return function( p_conn, p_from, p_this )
337
338 DBusHandlerResult
339 handle_tracklist ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
340 {
341     if(0);
342
343     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Get",    GetProperty );
344
345     /* here D-Bus method names are associated to an handler */
346
347     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GoTo",        GoTo );
348     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "AddTrack",    AddTrack );
349     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "RemoveTrack", RemoveTrack );
350     METHOD_FUNC( DBUS_MPRIS_TRACKLIST_INTERFACE, "GetTracksMetadata",
351                                                   GetTracksMetadata );
352
353     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
354 }
355
356 #undef METHOD_FUNC
357
358 /**
359  * PropertiesChangedSignal: synthetizes and sends the
360  * org.freedesktop.DBus.Properties.PropertiesChanged signal
361  */
362 static DBusHandlerResult
363 PropertiesChangedSignal( intf_thread_t    *p_intf,
364                          vlc_dictionary_t *p_changed_properties )
365 {
366     DBusConnection  *p_conn = p_intf->p_sys->p_conn;
367     DBusMessageIter changed_properties, invalidated_properties, entry, variant;
368     const char *psz_interface_name = DBUS_MPRIS_TRACKLIST_INTERFACE;
369     char **ppsz_properties = NULL;
370     int i_properties = 0;
371
372     SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
373                  DBUS_MPRIS_OBJECT_PATH,
374                  "PropertiesChanged" );
375
376     OUT_ARGUMENTS;
377     ADD_STRING( &psz_interface_name );
378     dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}",
379                                       &changed_properties );
380
381     dbus_message_iter_close_container( &args, &changed_properties );
382
383     dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
384                                       &invalidated_properties );
385
386     i_properties    = vlc_dictionary_keys_count( p_changed_properties );
387     ppsz_properties = vlc_dictionary_all_keys( p_changed_properties );
388
389     for( int i = 0; i < i_properties; i++ )
390         if( !strcmp( ppsz_properties[i], "Tracks" ) )
391             dbus_message_iter_append_basic( &invalidated_properties,
392                                             DBUS_TYPE_STRING,
393                                             &ppsz_properties[i] );
394
395     dbus_message_iter_close_container( &args, &invalidated_properties );
396     SIGNAL_SEND;
397 }
398
399 /**
400  * TrackListPropertiesChangedEmit: Emits the
401  * org.freedesktop.DBus.Properties.PropertiesChanged signal
402  */
403 int TrackListPropertiesChangedEmit( intf_thread_t    * p_intf,
404                                     vlc_dictionary_t * p_changed_properties )
405 {
406     if( p_intf->p_sys->b_dead )
407         return VLC_SUCCESS;
408
409     PropertiesChangedSignal( p_intf, p_changed_properties );
410     return VLC_SUCCESS;
411 }