]> git.sesse.net Git - vlc/blob - modules/control/dbus.c
Add a method to export playlists
[vlc] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright (C) 2006 Rafaël Carré
5  * $Id$
6  *
7  * Author:    Rafaël Carré <funman at videolanorg>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*
25  * D-Bus Specification:
26  *      http://dbus.freedesktop.org/doc/dbus-specification.html
27  * D-Bus low-level C API (libdbus)
28  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
29  */
30
31 /*
32  * TODO:
33  *  properties ?
34  *
35  *  macros to read incoming arguments
36  *
37  *  explore different possible types (arrays..)
38  *
39  *  what must we do if org.videolan.vlc already exist on the bus ?
40  *  ( there is more than one vlc instance )
41  */
42
43 /*****************************************************************************
44  * Preamble
45  *****************************************************************************/
46
47 #include <dbus/dbus.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include "dbus.h"
53
54 #include <vlc/vlc.h>
55 #include <vlc_aout.h>
56 #include <vlc_interface.h>
57 #include <vlc_meta.h>
58 #include <vlc_input.h>
59 #include <vlc_playlist.h>
60
61 /*****************************************************************************
62  * Local prototypes.
63  *****************************************************************************/
64
65 static int  Open    ( vlc_object_t * );
66 static void Close   ( vlc_object_t * );
67 static void Run        ( intf_thread_t * );
68
69
70 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
71                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
72
73 struct intf_sys_t
74 {
75     DBusConnection *p_conn;
76 };
77
78 /*****************************************************************************
79  * Module descriptor
80  *****************************************************************************/
81
82 vlc_module_begin();
83     set_shortname( _("dbus"));
84     set_category( CAT_INTERFACE );
85     set_subcategory( SUBCAT_INTERFACE_CONTROL );
86     set_description( _("D-Bus control interface") );
87     set_capability( "interface", 0 );
88     set_callbacks( Open, Close );
89 vlc_module_end();
90
91 /*****************************************************************************
92  * Methods
93  *****************************************************************************/
94
95 DBUS_METHOD( Nothing )
96 { /* do nothing */
97     REPLY_INIT;
98     REPLY_SEND;
99 }
100
101 DBUS_METHOD( PlaylistExport_XSPF )
102 { /* export playlist to an xspf file */
103
104   /* reads the filename to export to */
105   /* returns the status as uint16:
106    *    0 : success
107    *    1 : error
108    *    2 : playlist empty
109    */
110     REPLY_INIT;
111     OUT_ARGUMENTS;
112
113     DBusError error; 
114     dbus_error_init( &error );
115
116     char *psz_file;
117     dbus_uint16_t i_ret;
118
119     dbus_message_get_args( p_from, &error,
120             DBUS_TYPE_STRING, &psz_file,
121             DBUS_TYPE_INVALID );
122
123     if( dbus_error_is_set( &error ) )
124     {
125         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
126                 error.message );
127         dbus_error_free( &error );
128         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
129     }
130
131     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
132
133     if( ( !playlist_IsEmpty( p_playlist ) ) &&
134             ( p_playlist->p_root_category->i_children > 0 ) )
135     {
136         if( playlist_Export( p_playlist, psz_file,
137                          p_playlist->p_root_category->pp_children[0],
138                          "export-xspf" ) == VLC_SUCCESS )
139             i_ret = 0;
140         else
141             i_ret = 1;
142     }
143     else
144         i_ret = 2;
145
146     pl_Release( ((vlc_object_t*) p_this ) );
147
148     ADD_UINT16( &i_ret );
149     REPLY_SEND;
150 }
151
152 DBUS_METHOD( Quit )
153 { /* exits vlc */
154     REPLY_INIT;
155     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
156     playlist_Stop( p_playlist );
157     pl_Release( ((vlc_object_t*) p_this) );
158     ((vlc_object_t*)p_this)->p_libvlc->b_die = VLC_TRUE;
159     REPLY_SEND;
160 }
161
162 DBUS_METHOD( PositionGet )
163 { /* returns position as an int in the range [0;1000] */
164     REPLY_INIT;
165     OUT_ARGUMENTS;
166     vlc_value_t position;
167     dbus_uint16_t i_pos;
168
169     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
170     input_thread_t *p_input = p_playlist->p_input;
171
172     if( !p_input )
173         i_pos = 0;
174     else
175     {
176         var_Get( p_input, "position", &position );
177         i_pos = position.f_float * 1000 ;
178     }
179     ADD_UINT16( &i_pos );
180     pl_Release( ((vlc_object_t*) p_this) );
181     REPLY_SEND;
182 }
183
184 DBUS_METHOD( PositionSet )
185 { /* set position from an int in the range [0;1000] */
186
187     REPLY_INIT;
188     vlc_value_t position;
189     dbus_uint16_t i_pos;
190
191     DBusError error;
192     dbus_error_init( &error );
193
194     dbus_message_get_args( p_from, &error,
195             DBUS_TYPE_UINT16, &i_pos,
196             DBUS_TYPE_INVALID );
197
198     if( dbus_error_is_set( &error ) )
199     {
200         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
201                 error.message );
202         dbus_error_free( &error );
203         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
204     }
205     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
206     input_thread_t *p_input = p_playlist->p_input;
207
208     if( p_input )
209     {
210         position.f_float = ((float)i_pos) / 1000;
211         var_Set( p_input, "position", position );
212     }
213     pl_Release( ((vlc_object_t*) p_this) );
214     REPLY_SEND;
215 }
216
217 DBUS_METHOD( VolumeGet )
218 { /* returns volume in percentage */
219     REPLY_INIT;
220     OUT_ARGUMENTS;
221     dbus_uint16_t i_vol;
222     /* 2nd argument of aout_VolumeGet is uint16 */
223     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
224     i_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
225     ADD_UINT16( &i_vol );
226     REPLY_SEND;
227 }
228
229 DBUS_METHOD( VolumeSet )
230 { /* set volume in percentage */
231     REPLY_INIT;
232
233     DBusError error;
234     dbus_error_init( &error );
235
236     dbus_uint16_t i_vol;
237
238     dbus_message_get_args( p_from, &error,
239             DBUS_TYPE_UINT16, &i_vol,
240             DBUS_TYPE_INVALID );
241
242     if( dbus_error_is_set( &error ) )
243     {
244         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
245                 error.message );
246         dbus_error_free( &error );
247         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
248     }
249
250     aout_VolumeSet( (vlc_object_t*) p_this, ( AOUT_VOLUME_MAX / 100 ) * i_vol );
251
252     REPLY_SEND;
253 }
254
255 DBUS_METHOD( Next )
256 { /* next playlist item */
257     REPLY_INIT;
258     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
259     playlist_Next( p_playlist );
260     pl_Release( ((vlc_object_t*) p_this) );
261     REPLY_SEND;
262 }
263
264 DBUS_METHOD( Prev )
265 { /* previous playlist item */
266     REPLY_INIT;
267     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
268     playlist_Prev( p_playlist );
269     pl_Release( ((vlc_object_t*) p_this) );
270     REPLY_SEND;
271 }
272
273 DBUS_METHOD( Stop )
274 { /* stop playing */
275     REPLY_INIT;
276     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
277     playlist_Stop( p_playlist );
278     pl_Release( ((vlc_object_t*) p_this) );
279     REPLY_SEND;
280 }
281
282 DBUS_METHOD( GetPlayingItem )
283 { /* return the current item */
284     REPLY_INIT;
285     OUT_ARGUMENTS;
286     char psz_no_input = '\0';
287     char *p_psz_no_input = &psz_no_input;
288     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
289     input_thread_t *p_input = p_playlist->p_input;
290     ADD_STRING( ( p_input ) ? &input_GetItem(p_input)->psz_name :
291             &p_psz_no_input );
292     pl_Release( ((vlc_object_t*) p_this) );
293     REPLY_SEND;
294 }
295
296 DBUS_METHOD( GetPlayStatus )
297 { /* return a string */
298     REPLY_INIT;
299     OUT_ARGUMENTS;
300
301     char *psz_play;
302     vlc_value_t val;
303     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
304     input_thread_t *p_input = p_playlist->p_input;
305
306     if( !p_input )
307         psz_play = strdup( "stopped" );
308     else
309     {
310         var_Get( p_input, "state", &val );
311         if( val.i_int == PAUSE_S )
312             psz_play = strdup( "pause" );
313         else if( val.i_int == PLAYING_S )
314             psz_play = strdup( "playing" );
315         else psz_play = strdup( "unknown" );
316     }
317
318     pl_Release( p_playlist );
319
320     ADD_STRING( &psz_play );
321     free( psz_play );
322     REPLY_SEND;
323 }
324
325 DBUS_METHOD( TogglePause )
326 { /* return a bool: true if playing */
327     REPLY_INIT;
328     OUT_ARGUMENTS;
329
330     vlc_value_t val;
331     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
332     input_thread_t *p_input = p_playlist->p_input;
333     if( p_input != NULL )
334     {
335         var_Get( p_input, "state", &val );
336         if( val.i_int != PAUSE_S )
337         {
338             val.i_int = PAUSE_S;
339             playlist_Pause( p_playlist );
340         }
341         else
342         {
343             val.i_int = PLAYING_S;
344             playlist_Play( p_playlist );
345         }
346     }
347     else
348     {
349         val.i_int = PLAYING_S;
350         playlist_Play( p_playlist );
351     }
352     pl_Release( p_playlist );
353
354     dbus_bool_t pause = ( val.i_int == PLAYING_S ) ? TRUE : FALSE;
355     ADD_BOOL( &pause );
356     REPLY_SEND;
357 }
358
359 DBUS_METHOD( AddMRL )
360 { /* add the string to the playlist, and play it if the boolean is true */
361     REPLY_INIT;
362
363     DBusError error;
364     dbus_error_init( &error );
365
366     char *psz_mrl;
367     dbus_bool_t b_play;
368
369     dbus_message_get_args( p_from, &error,
370             DBUS_TYPE_STRING, &psz_mrl,
371             DBUS_TYPE_BOOLEAN, &b_play,
372             DBUS_TYPE_INVALID );
373
374     if( dbus_error_is_set( &error ) )
375     {
376         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
377                 error.message );
378         dbus_error_free( &error );
379         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
380     }
381
382     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
383     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
384             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , PLAYLIST_END, VLC_TRUE );
385     pl_Release( p_playlist );
386
387     REPLY_SEND;
388 }
389
390 /*****************************************************************************
391  * Introspection method
392  *****************************************************************************/
393
394 DBUS_METHOD( handle_introspect )
395 { /* handles introspection of /org/videolan/vlc */
396     REPLY_INIT;
397     OUT_ARGUMENTS;
398     ADD_STRING( &psz_introspection_xml_data );
399     REPLY_SEND;
400 }
401
402 /*****************************************************************************
403  * handle_messages: answer to incoming messages
404  *****************************************************************************/
405
406 #define METHOD_FUNC( method, function ) \
407     else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
408         return function( p_conn, p_from, p_this )
409
410 DBUS_METHOD( handle_messages )
411 { /* the main handler, that call methods */
412
413     if( dbus_message_is_method_call( p_from,
414                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
415         return handle_introspect( p_conn, p_from, p_this );
416
417     /* here D-Bus method's names are associated to an handler */
418
419     METHOD_FUNC( "GetPlayStatus",           GetPlayStatus );
420     METHOD_FUNC( "GetPlayingItem",          GetPlayingItem );
421     METHOD_FUNC( "AddMRL",                  AddMRL );
422     METHOD_FUNC( "TogglePause",             TogglePause );
423     METHOD_FUNC( "Nothing",                 Nothing );
424     METHOD_FUNC( "Prev",                    Prev );
425     METHOD_FUNC( "Next",                    Next );
426     METHOD_FUNC( "Quit",                    Quit );
427     METHOD_FUNC( "Stop",                    Stop );
428     METHOD_FUNC( "VolumeSet",               VolumeSet );
429     METHOD_FUNC( "VolumeGet",               VolumeGet );
430     METHOD_FUNC( "PositionSet",             PositionSet );
431     METHOD_FUNC( "PositionGet",             PositionGet );
432     METHOD_FUNC( "PlaylistExport_XSPF",     PlaylistExport_XSPF );
433
434     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
435 }
436
437 /*****************************************************************************
438  * Open: initialize interface
439  *****************************************************************************/
440
441 static int Open( vlc_object_t *p_this )
442 { /* initialisation of the connection */
443     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
444     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
445     playlist_t      *p_playlist;
446     DBusConnection  *p_conn;
447     DBusError       error;
448
449     if( !p_sys )
450         return VLC_ENOMEM;
451
452     dbus_threads_init_default();
453
454     dbus_error_init( &error );
455
456     /* connect to the session bus */
457     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
458     if( !p_conn )
459     {
460         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
461                 error.message );
462         dbus_error_free( &error );
463         free( p_sys );
464         return VLC_EGENERIC;
465     }
466
467     /* we unregister the object /, registered by libvlc */
468     dbus_connection_unregister_object_path( p_conn, "/" );
469
470     /* we register the object /org/videolan/vlc */
471     dbus_connection_register_object_path( p_conn, VLC_DBUS_OBJECT_PATH,
472             &vlc_dbus_vtable, p_this );
473
474     dbus_connection_flush( p_conn );
475
476     p_playlist = pl_Yield( p_intf );
477     PL_LOCK;
478     var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
479     PL_UNLOCK;
480     pl_Release( p_playlist );
481
482     p_intf->pf_run = Run;
483     p_intf->p_sys = p_sys;
484     p_sys->p_conn = p_conn;
485
486     return VLC_SUCCESS;
487 }
488
489 /*****************************************************************************
490  * Close: destroy interface
491  *****************************************************************************/
492
493 static void Close   ( vlc_object_t *p_this )
494 {
495     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
496     playlist_t      *p_playlist = pl_Yield( p_intf );;
497
498     PL_LOCK;
499     var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
500     PL_UNLOCK;
501     pl_Release( p_playlist );
502
503     dbus_connection_unref( p_intf->p_sys->p_conn );
504
505     free( p_intf->p_sys );
506 }
507
508 /*****************************************************************************
509  * Run: main loop
510  *****************************************************************************/
511
512 static void Run          ( intf_thread_t *p_intf )
513 {
514     while( !p_intf->b_die )
515     {
516         msleep( INTF_IDLE_SLEEP );
517         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
518     }
519 }
520
521 /*****************************************************************************
522  * ItemChange: Playlist item change callback
523  *****************************************************************************/
524
525 DBUS_SIGNAL( ItemChangeSignal )
526 { /* emit the name of the new item */
527     SIGNAL_INIT( "ItemChange" );
528     OUT_ARGUMENTS;
529
530     input_thread_t *p_input = (input_thread_t*) p_data;
531     ADD_STRING( &input_GetItem(p_input)->psz_name );
532
533     SIGNAL_SEND;
534 }
535
536 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
537             vlc_value_t oldval, vlc_value_t newval, void *p_data )
538 {
539     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
540     intf_sys_t          *p_sys      = p_intf->p_sys;
541     playlist_t          *p_playlist;
542     input_thread_t      *p_input    = NULL;
543     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
544
545     p_playlist = pl_Yield( p_intf );
546     PL_LOCK;
547     p_input = p_playlist->p_input;
548
549     if( !p_input )
550     {
551         PL_UNLOCK;
552         pl_Release( p_playlist );
553         return VLC_SUCCESS;
554     }
555
556     vlc_object_yield( p_input );
557     PL_UNLOCK;
558     pl_Release( p_playlist );
559
560     ItemChangeSignal( p_sys->p_conn, p_input );
561
562     vlc_object_release( p_input );
563     return VLC_SUCCESS;
564 }
565