dbus: remove evil intractable macro, fix memory leak
[vlc.git] / modules / control / dbus / dbus_root.c
1 /*****************************************************************************
2  * dbus_root.c : dbus control module (mpris v2.2) - Root object
3  *****************************************************************************
4  * Copyright © 2006-2008 Rafaël Carré
5  * Copyright © 2007-2011 Mirsal Ennaime
6  * Copyright © 2009-2011 The VideoLAN team
7  * Copyright © 2013      Alex Merry
8  * $Id$
9  *
10  * Authors:    Mirsal Ennaime <mirsal at mirsal fr>
11  *             Rafaël Carré <funman at videolanorg>
12  *             Alex Merry <dev at randomguy3 me uk>
13  *
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.
18  *
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.
23  *
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  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_interface.h>
35 #include <vlc_input.h>
36 #include <vlc_vout.h>
37 #include <vlc_plugin.h>
38 #include <vlc_playlist.h>
39
40 #include <unistd.h>
41 #include <limits.h>
42
43 #include "dbus_root.h"
44 #include "dbus_common.h"
45
46 static const char ppsz_supported_uri_schemes[][9] = {
47     "file", "http", "https", "rtsp", "realrtsp", "pnm", "ftp", "mtp", "smb",
48     "mms", "mmsu", "mmst", "mmsh", "unsv", "itpc", "icyx", "rtmp", "rtp",
49     "dccp", "dvd", "vcd", "vcdx"
50 };
51
52 static const char ppsz_supported_mime_types[][26] = {
53     "audio/mpeg", "audio/x-mpeg",
54     "video/mpeg", "video/x-mpeg",
55     "video/mpeg-system", "video/x-mpeg-system",
56     "video/mp4",
57     "audio/mp4",
58     "video/x-msvideo",
59     "video/quicktime",
60     "application/ogg", "application/x-ogg",
61     "video/x-ms-asf",  "video/x-ms-asf-plugin",
62     "application/x-mplayer2",
63     "video/x-ms-wmv",
64     "video/x-google-vlc-plugin",
65     "audio/wav", "audio/x-wav",
66     "audio/3gpp",
67     "video/3gpp",
68     "audio/3gpp2",
69     "video/3gpp2",
70     "video/divx",
71     "video/flv", "video/x-flv",
72     "video/x-matroska",
73     "audio/x-matroska",
74     "application/xspf+xml"
75 };
76
77 static int
78 MarshalIdentity( intf_thread_t *p_intf, DBusMessageIter *container )
79 {
80     VLC_UNUSED( p_intf );
81     const char *psz_id = _("VLC media player");
82
83     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_STRING, &psz_id ))
84         return VLC_ENOMEM;
85
86     return VLC_SUCCESS;
87 }
88
89 static int
90 MarshalCanSetFullscreen( intf_thread_t *p_intf, DBusMessageIter *container )
91 {
92     input_thread_t *p_input = NULL;
93     dbus_bool_t     b_ret   = FALSE;
94
95     if (p_intf->p_sys->p_input)
96     {
97         p_input = (input_thread_t*) vlc_object_hold( p_intf->p_sys->p_input );
98         vout_thread_t* p_vout = input_GetVout( p_input );
99         vlc_object_release( p_input );
100
101         if ( p_vout )
102         {
103             b_ret = TRUE;
104             vlc_object_release( p_vout );
105         }
106     }
107
108     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ))
109         return VLC_ENOMEM;
110
111     return VLC_SUCCESS;
112 }
113
114 static int
115 MarshalFullscreen( intf_thread_t *p_intf, DBusMessageIter *container )
116 {
117     dbus_bool_t b_fullscreen;
118
119     if ( p_intf->p_sys->p_playlist )
120         b_fullscreen = var_GetBool( p_intf->p_sys->p_playlist , "fullscreen" );
121     else
122         b_fullscreen = FALSE;
123
124     if (!dbus_message_iter_append_basic( container,
125             DBUS_TYPE_BOOLEAN, &b_fullscreen ))
126         return VLC_ENOMEM;
127
128     return VLC_SUCCESS;
129 }
130
131 DBUS_METHOD( FullscreenSet )
132 {
133     REPLY_INIT;
134     dbus_bool_t b_fullscreen;
135     input_thread_t *p_input = NULL;
136
137     if( VLC_SUCCESS != DemarshalSetPropertyValue( p_from, &b_fullscreen ) )
138         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
139
140     if (INTF->p_sys->p_input)
141     {
142         p_input = (input_thread_t*) vlc_object_hold( INTF->p_sys->p_input );
143         vout_thread_t* p_vout = input_GetVout( p_input );
144         vlc_object_release( p_input );
145
146         if ( p_vout )
147             var_SetBool( p_vout, "fullscreen", ( b_fullscreen == TRUE ) );
148         if ( PL )
149             var_SetBool( PL , "fullscreen", ( b_fullscreen == TRUE ) );
150     }
151
152     REPLY_SEND;
153 }
154
155 static int
156 MarshalCanQuit( intf_thread_t *p_intf, DBusMessageIter *container )
157 {
158     VLC_UNUSED( p_intf );
159     const dbus_bool_t b_ret = TRUE;
160
161     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ))
162         return VLC_ENOMEM;
163
164     return VLC_SUCCESS;
165 }
166
167 static int
168 MarshalCanRaise( intf_thread_t *p_intf, DBusMessageIter *container )
169 {
170     VLC_UNUSED( p_intf );
171     const dbus_bool_t b_ret = FALSE;
172
173     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ))
174         return VLC_ENOMEM;
175
176     return VLC_SUCCESS;
177 }
178
179 static int
180 MarshalHasTrackList( intf_thread_t *p_intf, DBusMessageIter *container )
181 {
182     VLC_UNUSED( p_intf );
183     const dbus_bool_t b_ret = FALSE;
184
185     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_ret ))
186         return VLC_ENOMEM;
187
188     return VLC_SUCCESS;
189 }
190
191 static int
192 MarshalDesktopEntry( intf_thread_t *p_intf, DBusMessageIter *container )
193 {
194     VLC_UNUSED( p_intf );
195     const char* psz_ret = PACKAGE;
196
197     if (!dbus_message_iter_append_basic( container, DBUS_TYPE_STRING, &psz_ret ))
198         return VLC_ENOMEM;
199
200     return VLC_SUCCESS;
201 }
202
203 static int
204 MarshalSupportedMimeTypes( intf_thread_t *p_intf, DBusMessageIter *container )
205 {
206     VLC_UNUSED( p_intf );
207     DBusMessageIter ret;
208
209     size_t i_len = sizeof( ppsz_supported_mime_types ) /
210         sizeof( *ppsz_supported_mime_types );
211
212     if( !dbus_message_iter_open_container( container,
213                                            DBUS_TYPE_ARRAY, "s",
214                                            &ret ) )
215         return VLC_ENOMEM;
216
217     for( size_t i = 0; i < i_len; ++i )
218     {
219         const char* const psz_mime_type = ppsz_supported_mime_types[i];
220
221         if( !dbus_message_iter_append_basic( &ret, DBUS_TYPE_STRING,
222                                              &psz_mime_type ) )
223             return VLC_ENOMEM;
224     }
225
226     if( !dbus_message_iter_close_container( container, &ret ) )
227         return VLC_ENOMEM;
228
229     return VLC_SUCCESS;
230 }
231
232 static int
233 MarshalSupportedUriSchemes( intf_thread_t *p_intf, DBusMessageIter *container )
234 {
235     VLC_UNUSED( p_intf );
236     DBusMessageIter ret;
237
238     size_t i_len = sizeof( ppsz_supported_uri_schemes ) /
239         sizeof( *ppsz_supported_uri_schemes );
240
241     if( !dbus_message_iter_open_container( container,
242                                            DBUS_TYPE_ARRAY, "s",
243                                            &ret ) )
244         return VLC_ENOMEM;
245
246     for( size_t i = 0; i < i_len; ++i )
247     {
248         const char* const psz_scheme = ppsz_supported_uri_schemes[i];
249
250         if( !dbus_message_iter_append_basic( &ret, DBUS_TYPE_STRING,
251                                              &psz_scheme ) )
252             return VLC_ENOMEM;
253     }
254
255     if( !dbus_message_iter_close_container( container, &ret ) )
256         return VLC_ENOMEM;
257
258     return VLC_SUCCESS;
259 }
260
261 DBUS_METHOD( Quit )
262 { /* exits vlc */
263     REPLY_INIT;
264     libvlc_Quit(INTF->p_libvlc);
265     REPLY_SEND;
266 }
267
268 DBUS_METHOD( Raise )
269 {/* shows vlc's main window */
270     REPLY_INIT;
271     var_ToggleBool( INTF->p_libvlc, "intf-show" );
272     REPLY_SEND;
273 }
274
275 #define PROPERTY_MAPPING_BEGIN if( 0 ) {}
276 #define PROPERTY_GET_FUNC( prop, signature ) \
277     else if( !strcmp( psz_property_name,  #prop ) ) { \
278         if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, signature, &v ) ) \
279             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
280         if( VLC_SUCCESS != Marshal##prop( p_this, &v ) ) { \
281             dbus_message_iter_abandon_container( &args, &v ); \
282             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
283         } \
284         if( !dbus_message_iter_close_container( &args, &v ) ) \
285             return DBUS_HANDLER_RESULT_NEED_MEMORY; \
286     }
287 #define PROPERTY_SET_FUNC( prop ) \
288     else if( !strcmp( psz_property_name,  #prop ) ) { \
289         return prop##Set( p_conn, p_from, p_this ); \
290     }
291 #define PROPERTY_MAPPING_END else { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; }
292
293 DBUS_METHOD( GetProperty )
294 {
295     DBusError error;
296
297     char *psz_interface_name = NULL;
298     char *psz_property_name  = NULL;
299
300     dbus_error_init( &error );
301     dbus_message_get_args( p_from, &error,
302             DBUS_TYPE_STRING, &psz_interface_name,
303             DBUS_TYPE_STRING, &psz_property_name,
304             DBUS_TYPE_INVALID );
305
306     if( dbus_error_is_set( &error ) )
307     {
308         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
309                                          error.message );
310         dbus_error_free( &error );
311         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
312     }
313
314     msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
315                                      psz_property_name );
316
317     if( strcmp( psz_interface_name, DBUS_MPRIS_ROOT_INTERFACE ) ) {
318         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
319     }
320
321     REPLY_INIT;
322     OUT_ARGUMENTS;
323     DBusMessageIter v;
324
325     PROPERTY_MAPPING_BEGIN
326     PROPERTY_GET_FUNC( Identity,            "s" )
327     PROPERTY_GET_FUNC( CanSetFullscreen,    "b" )
328     PROPERTY_GET_FUNC( Fullscreen,          "b" )
329     PROPERTY_GET_FUNC( CanQuit,             "b" )
330     PROPERTY_GET_FUNC( CanRaise,            "b" )
331     PROPERTY_GET_FUNC( HasTrackList,        "b" )
332     PROPERTY_GET_FUNC( DesktopEntry,        "s" )
333     PROPERTY_GET_FUNC( SupportedMimeTypes,  "as" )
334     PROPERTY_GET_FUNC( SupportedUriSchemes, "as" )
335     PROPERTY_MAPPING_END
336
337     REPLY_SEND;
338 }
339
340 DBUS_METHOD( SetProperty )
341 {
342     DBusError error;
343
344     char *psz_interface_name = NULL;
345     char *psz_property_name  = NULL;
346
347     dbus_error_init( &error );
348     dbus_message_get_args( p_from, &error,
349             DBUS_TYPE_STRING, &psz_interface_name,
350             DBUS_TYPE_STRING, &psz_property_name,
351             DBUS_TYPE_INVALID );
352
353     if( dbus_error_is_set( &error ) )
354     {
355         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
356                                         error.message );
357         dbus_error_free( &error );
358         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
359     }
360
361     PROPERTY_MAPPING_BEGIN
362     PROPERTY_SET_FUNC( Fullscreen )
363     PROPERTY_MAPPING_END
364 }
365
366 #undef PROPERTY_MAPPING_BEGIN
367 #undef PROPERTY_GET_FUNC
368 #undef PROPERTY_SET_FUNC
369 #undef PROPERTY_MAPPING_END
370
371 #define ADD_PROPERTY( prop, signature ) \
372     if( VLC_SUCCESS != AddProperty( (intf_thread_t*) p_this, \
373                 &dict, #prop, signature, Marshal##prop ) ) { \
374         dbus_message_iter_abandon_container( &args, &dict ); \
375         return VLC_ENOMEM; \
376     }
377
378 DBUS_METHOD( GetAllProperties )
379 {
380     REPLY_INIT;
381     OUT_ARGUMENTS;
382
383     DBusError error;
384     DBusMessageIter dict;
385
386     char *const psz_interface_name = NULL;
387
388     dbus_error_init( &error );
389     dbus_message_get_args( p_from, &error,
390             DBUS_TYPE_STRING, &psz_interface_name,
391             DBUS_TYPE_INVALID );
392
393     if( dbus_error_is_set( &error ) )
394     {
395         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
396                                          error.message );
397         dbus_error_free( &error );
398         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
399     }
400
401     msg_Dbg( (vlc_object_t*) p_this, "Getting All properties" );
402
403     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}", &dict ) )
404         return DBUS_HANDLER_RESULT_NEED_MEMORY;
405
406     ADD_PROPERTY( Identity,            "s"  );
407     ADD_PROPERTY( DesktopEntry,        "s"  );
408     ADD_PROPERTY( SupportedMimeTypes,  "as" );
409     ADD_PROPERTY( SupportedUriSchemes, "as" );
410     ADD_PROPERTY( HasTrackList,        "b"  );
411     ADD_PROPERTY( CanQuit,             "b"  );
412     ADD_PROPERTY( CanSetFullscreen,    "b"  );
413     ADD_PROPERTY( Fullscreen,          "b"  );
414     ADD_PROPERTY( CanRaise,            "b"  );
415
416     if( !dbus_message_iter_close_container( &args, &dict ))
417         return DBUS_HANDLER_RESULT_NEED_MEMORY;
418
419     REPLY_SEND;
420 }
421
422 #undef ADD_PROPERTY
423
424 #define METHOD_MAPPING_BEGIN if( 0 ) {}
425 #define METHOD_FUNC( interface, method, function ) \
426     else if( dbus_message_is_method_call( p_from, interface, method ) )\
427         return function( p_conn, p_from, p_this )
428 #define METHOD_MAPPING_END return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
429
430 DBusHandlerResult
431 handle_root ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
432 {
433     METHOD_MAPPING_BEGIN
434     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Get",          GetProperty );
435     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "Set",          SetProperty );
436     METHOD_FUNC( DBUS_INTERFACE_PROPERTIES, "GetAll",       GetAllProperties );
437     METHOD_FUNC( DBUS_MPRIS_ROOT_INTERFACE, "Quit",         Quit );
438     METHOD_FUNC( DBUS_MPRIS_ROOT_INTERFACE, "Raise",        Raise );
439     METHOD_MAPPING_END
440 }
441
442 #undef METHOD_MAPPING_BEGIN
443 #undef METHOD_FUNC
444 #undef METHOD_MAPPING_END
445 /**
446  * PropertiesChangedSignal() synthetizes and sends the
447  * org.freedesktop.DBus.Properties.PropertiesChanged signal
448  */
449 static DBusHandlerResult
450 PropertiesChangedSignal( intf_thread_t    *p_intf,
451                          vlc_dictionary_t *p_changed_properties )
452 {
453     DBusConnection  *p_conn = p_intf->p_sys->p_conn;
454     DBusMessageIter changed_properties, invalidated_properties;
455     const char *psz_interface_name = DBUS_MPRIS_ROOT_INTERFACE;
456     char **ppsz_properties = NULL;
457     int i_properties = 0;
458
459     SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
460                  DBUS_MPRIS_OBJECT_PATH,
461                  "PropertiesChanged" );
462
463     OUT_ARGUMENTS;
464     ADD_STRING( &psz_interface_name );
465
466     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}",
467                                            &changed_properties ) )
468         return DBUS_HANDLER_RESULT_NEED_MEMORY;
469
470     i_properties = vlc_dictionary_keys_count( p_changed_properties );
471     ppsz_properties = vlc_dictionary_all_keys( p_changed_properties );
472
473     if( unlikely(!ppsz_properties) )
474     {
475         dbus_message_iter_abandon_container( &args, &changed_properties );
476         return DBUS_HANDLER_RESULT_NEED_MEMORY;
477     }
478
479     for( int i = 0; i < i_properties; i++ )
480     {
481         if( !strcmp( ppsz_properties[i], "Fullscreen" ) )
482              AddProperty( p_intf, &changed_properties, "Fullscreen", "b",
483                           MarshalFullscreen );
484         free( ppsz_properties[i] );
485     }
486     free( ppsz_properties );
487
488     if( !dbus_message_iter_close_container( &args, &changed_properties ) )
489         return DBUS_HANDLER_RESULT_NEED_MEMORY;
490
491     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
492                                            &invalidated_properties ) )
493         return DBUS_HANDLER_RESULT_NEED_MEMORY;
494
495     if( !dbus_message_iter_close_container( &args, &invalidated_properties ) )
496         return DBUS_HANDLER_RESULT_NEED_MEMORY;
497
498     SIGNAL_SEND;
499 }
500
501 /*****************************************************************************
502  * RootPropertiesChangedEmit: Emits the Seeked signal
503  *****************************************************************************/
504 int RootPropertiesChangedEmit( intf_thread_t    *p_intf,
505                                vlc_dictionary_t *p_changed_properties )
506 {
507     if( p_intf->p_sys->b_dead )
508         return VLC_SUCCESS;
509
510     PropertiesChangedSignal( p_intf, p_changed_properties );
511     return VLC_SUCCESS;
512 }