Reorganize the dbus control module code
[vlc.git] / modules / control / dbus / dbus_player.c
1 /*****************************************************************************
2  * dbus-player.h : dbus control module (mpris v1.0) - /Player object
3  *****************************************************************************
4  * Copyright © 2006-2008 Rafaël Carré
5  * Copyright © 2007-2010 Mirsal Ennaime
6  * Copyright © 2009-2010 The VideoLAN team
7  * $Id$
8  *
9  * Authors:    Mirsal ENNAIME <mirsal dot ennaime at gmail dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_playlist.h>
32 #include <vlc_interface.h>
33 #include <vlc_aout.h>
34
35 #include <math.h>
36
37 #include "dbus_player.h"
38 #include "dbus_common.h"
39
40 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
41
42 /* XML data to answer org.freedesktop.DBus.Introspectable.Introspect requests */
43 static const char* psz_player_introspection_xml =
44 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
45 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
46 "<node>"
47 "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
48 "    <method name=\"Introspect\">\n"
49 "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
50 "    </method>\n"
51 "  </interface>\n"
52 "  <interface name=\"org.freedesktop.MediaPlayer\">\n"
53 "    <method name=\"GetStatus\">\n"
54 "      <arg type=\"(iiii)\" direction=\"out\" />\n"
55 "    </method>\n"
56 "    <method name=\"Prev\">\n"
57 "    </method>\n"
58 "    <method name=\"Next\">\n"
59 "    </method>\n"
60 "    <method name=\"Stop\">\n"
61 "    </method>\n"
62 "    <method name=\"Play\">\n"
63 "    </method>\n"
64 "    <method name=\"Pause\">\n"
65 "    </method>\n"
66 "    <method name=\"Repeat\">\n"
67 "      <arg type=\"b\" direction=\"in\" />\n"
68 "    </method>\n"
69 "    <method name=\"VolumeSet\">\n"
70 "      <arg type=\"i\" direction=\"in\" />\n"
71 "    </method>\n"
72 "    <method name=\"VolumeGet\">\n"
73 "      <arg type=\"i\" direction=\"out\" />\n"
74 "    </method>\n"
75 "    <method name=\"PositionSet\">\n"
76 "      <arg type=\"i\" direction=\"in\" />\n"
77 "    </method>\n"
78 "    <method name=\"PositionGet\">\n"
79 "      <arg type=\"i\" direction=\"out\" />\n"
80 "    </method>\n"
81 "    <method name=\"GetMetadata\">\n"
82 "      <arg type=\"a{sv}\" direction=\"out\" />\n"
83 "    </method>\n"
84 "    <method name=\"GetCaps\">\n"
85 "      <arg type=\"i\" direction=\"out\" />\n"
86 "    </method>\n"
87 "    <signal name=\"TrackChange\">\n"
88 "      <arg type=\"a{sv}\"/>\n"
89 "    </signal>\n"
90 "    <signal name=\"StatusChange\">\n"
91 "      <arg type=\"(iiii)\"/>\n"
92 "    </signal>\n"
93 "    <signal name=\"CapsChange\">\n"
94 "      <arg type=\"i\"/>\n"
95 "    </signal>\n"
96 "  </interface>\n"
97 "</node>\n"
98 ;
99
100 DBUS_METHOD( PositionGet )
101 { /* returns position in milliseconds */
102     REPLY_INIT;
103     OUT_ARGUMENTS;
104     dbus_int32_t i_pos;
105
106     input_thread_t *p_input = playlist_CurrentInput( PL );
107
108     if( !p_input )
109         i_pos = 0;
110     else
111     {
112         i_pos = var_GetTime( p_input, "time" ) / 1000;
113         vlc_object_release( p_input );
114     }
115     ADD_INT32( &i_pos );
116     REPLY_SEND;
117 }
118
119 DBUS_METHOD( PositionSet )
120 { /* set position in milliseconds */
121
122     REPLY_INIT;
123     vlc_value_t position;
124     dbus_int32_t i_pos;
125
126     DBusError error;
127     dbus_error_init( &error );
128
129     dbus_message_get_args( p_from, &error,
130             DBUS_TYPE_INT32, &i_pos,
131             DBUS_TYPE_INVALID );
132
133     if( dbus_error_is_set( &error ) )
134     {
135         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
136                 error.message );
137         dbus_error_free( &error );
138         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
139     }
140     input_thread_t *p_input = playlist_CurrentInput( PL );
141
142     if( p_input )
143     {
144         position.i_time = ((mtime_t)i_pos) * 1000;
145         var_Set( p_input, "time", position );
146         vlc_object_release( p_input );
147     }
148     REPLY_SEND;
149 }
150
151 DBUS_METHOD( VolumeGet )
152 { /* returns volume in percentage */
153     REPLY_INIT;
154     OUT_ARGUMENTS;
155     dbus_int32_t i_dbus_vol;
156     audio_volume_t i_vol;
157
158     /* 2nd argument of aout_VolumeGet is int32 */
159     aout_VolumeGet( PL, &i_vol );
160
161     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
162     i_dbus_vol = round( f_vol );
163     ADD_INT32( &i_dbus_vol );
164     REPLY_SEND;
165 }
166
167 DBUS_METHOD( VolumeSet )
168 { /* set volume in percentage */
169     REPLY_INIT;
170
171     DBusError error;
172     dbus_error_init( &error );
173
174     dbus_int32_t i_dbus_vol;
175     audio_volume_t i_vol;
176
177     dbus_message_get_args( p_from, &error,
178             DBUS_TYPE_INT32, &i_dbus_vol,
179             DBUS_TYPE_INVALID );
180
181     if( dbus_error_is_set( &error ) )
182     {
183         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
184                 error.message );
185         dbus_error_free( &error );
186         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
187     }
188
189     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
190     i_vol = round( f_vol );
191     aout_VolumeSet( PL, i_vol );
192     REPLY_SEND;
193 }
194
195 DBUS_METHOD( Next )
196 { /* next playlist item */
197     REPLY_INIT;
198     playlist_Next( PL );
199     REPLY_SEND;
200 }
201
202 DBUS_METHOD( Prev )
203 { /* previous playlist item */
204     REPLY_INIT;
205     playlist_Prev( PL );
206     REPLY_SEND;
207 }
208
209 DBUS_METHOD( Stop )
210 { /* stop playing */
211     REPLY_INIT;
212     playlist_Stop( PL );
213     REPLY_SEND;
214 }
215
216 DBUS_METHOD( GetStatus )
217 { /* returns the current status as a struct of 4 ints */
218 /*
219     First   0 = Playing, 1 = Paused, 2 = Stopped.
220     Second  0 = Playing linearly , 1 = Playing randomly.
221     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
222     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
223  */
224     REPLY_INIT;
225     OUT_ARGUMENTS;
226
227     MarshalStatus( p_this, &args );
228
229     REPLY_SEND;
230 }
231
232 DBUS_METHOD( Pause )
233 {
234     REPLY_INIT;
235     playlist_Pause( PL );
236     REPLY_SEND;
237 }
238
239 DBUS_METHOD( Play )
240 {
241     REPLY_INIT;
242
243     input_thread_t *p_input =  playlist_CurrentInput( PL );
244
245     if( p_input )
246     {
247         double i_pos = 0;
248         input_Control( p_input, INPUT_SET_POSITION, i_pos );
249         vlc_object_release( p_input );
250     }
251     else
252         playlist_Play( PL );
253
254     REPLY_SEND;
255 }
256
257 DBUS_METHOD( Repeat )
258 {
259     REPLY_INIT;
260     OUT_ARGUMENTS;
261
262     DBusError error;
263     dbus_bool_t b_repeat;
264
265     dbus_error_init( &error );
266     dbus_message_get_args( p_from, &error,
267             DBUS_TYPE_BOOLEAN, &b_repeat,
268             DBUS_TYPE_INVALID );
269
270     if( dbus_error_is_set( &error ) )
271     {
272         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
273                 error.message );
274         dbus_error_free( &error );
275         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
276     }
277
278     var_SetBool( PL, "repeat", ( b_repeat == TRUE ) );
279
280     REPLY_SEND;
281 }
282
283 DBUS_METHOD( GetCurrentMetadata )
284 {
285     REPLY_INIT;
286     OUT_ARGUMENTS;
287     playlist_t *p_playlist = PL;
288
289     PL_LOCK;
290     playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
291     if( p_item )
292         GetInputMeta( p_item->p_input, &args );
293     PL_UNLOCK;
294     REPLY_SEND;
295 }
296
297 DBUS_METHOD( GetCaps )
298 {
299     REPLY_INIT;
300     OUT_ARGUMENTS;
301
302     ADD_INT32( &INTF->p_sys->i_caps );
303
304     REPLY_SEND;
305 }
306
307 /*****************************************************************************
308  * StatusChange: Player status change signal
309  *****************************************************************************/
310
311 DBUS_SIGNAL( StatusChangeSignal )
312 { /* send the updated status info on the bus */
313     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
314                  DBUS_MPRIS_PLAYER_PATH,
315                  "StatusChange" );
316
317     OUT_ARGUMENTS;
318
319     /* we're called from a callback of input_thread_t, so it can not be
320      * destroyed before we return */
321     MarshalStatus( (intf_thread_t*) p_data, &args );
322
323     SIGNAL_SEND;
324 }
325
326 /*****************************************************************************
327  * TrackChange: Playlist item change callback
328  *****************************************************************************/
329
330 DBUS_SIGNAL( TrackChangeSignal )
331 { /* emit the metadata of the new item */
332     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
333                  DBUS_MPRIS_PLAYER_PATH,
334                  "TrackChange" );
335
336     OUT_ARGUMENTS;
337
338     input_item_t *p_item = (input_item_t*) p_data;
339     GetInputMeta ( p_item, &args );
340
341     SIGNAL_SEND;
342 }
343
344 /******************************************************************************
345  * CapsChange: player capabilities change signal
346  *****************************************************************************/
347 DBUS_SIGNAL( CapsChangeSignal )
348 {
349     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
350                  DBUS_MPRIS_PLAYER_PATH,
351                  "CapsChange" );
352
353     OUT_ARGUMENTS;
354
355     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
356     SIGNAL_SEND;
357 }
358
359 DBUS_METHOD( handle_introspect_player )
360 {
361     VLC_UNUSED(p_this);
362     REPLY_INIT;
363     OUT_ARGUMENTS;
364     ADD_STRING( &psz_player_introspection_xml );
365     REPLY_SEND;
366 }
367
368 #define METHOD_FUNC( interface, method, function ) \
369     else if( dbus_message_is_method_call( p_from, interface, method ) )\
370         return function( p_conn, p_from, p_this )
371
372 DBusHandlerResult
373 handle_player ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
374 {
375     if( dbus_message_is_method_call( p_from,
376                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
377         return handle_introspect_player( p_conn, p_from, p_this );
378
379     /* here D-Bus method names are associated to an handler */
380
381     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Prev",        Prev );
382     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Next",        Next );
383     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Stop",        Stop );
384     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Play",        Play );
385     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Pause",       Pause );
386     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Repeat",      Repeat );
387     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeSet",   VolumeSet );
388     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeGet",   VolumeGet );
389     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionSet", PositionSet );
390     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionGet", PositionGet );
391     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetStatus",   GetStatus );
392     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetMetadata", GetCurrentMetadata );
393     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetCaps",     GetCaps );
394
395     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
396 }
397
398 #undef METHOD_FUNC
399
400 /*****************************************************************************
401  * StatusChangeEmit: Emits the StatusChange signal
402  *****************************************************************************/
403 int StatusChangeEmit( intf_thread_t * p_intf )
404 {
405     if( p_intf->p_sys->b_dead )
406         return VLC_SUCCESS;
407
408     UpdateCaps( p_intf );
409     StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
410     return VLC_SUCCESS;
411 }
412
413 /*****************************************************************************
414  * CapsChangeEmit: Emits the CapsChange signal
415  *****************************************************************************/
416 int CapsChangeEmit( intf_thread_t * p_intf )
417 {
418     if( p_intf->p_sys->b_dead )
419         return VLC_SUCCESS;
420
421     CapsChangeSignal( p_intf->p_sys->p_conn, p_intf );
422     return VLC_SUCCESS;
423 }
424
425 /*****************************************************************************
426  * TrackChangeEmit: Emits the TrackChange signal
427  *****************************************************************************/
428 int TrackChangeEmit( intf_thread_t * p_intf, input_item_t* p_item )
429 {
430     if( p_intf->p_sys->b_dead )
431         return VLC_SUCCESS;
432
433     UpdateCaps( p_intf );
434     TrackChangeSignal( p_intf->p_sys->p_conn, p_item );
435     return VLC_SUCCESS;
436 }
437
438 /*****************************************************************************
439  * MarshalStatus: Fill a DBusMessage with the current player status
440  *****************************************************************************/
441
442 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
443 { /* This is NOT the right way to do that, it would be better to sore
444      the status information in p_sys and update it on change, thus
445      avoiding a long lock */
446
447     DBusMessageIter status;
448     dbus_int32_t i_state, i_random, i_repeat, i_loop;
449     int i_val;
450     playlist_t* p_playlist = p_intf->p_sys->p_playlist;
451     input_thread_t* p_input = NULL;
452
453     i_state = 2;
454
455     p_input = playlist_CurrentInput( p_playlist );
456     if( p_input )
457     {
458         i_val = var_GetInteger( p_input, "state" );
459         if( i_val >= END_S )
460             i_state = 2;
461         else if( i_val == PAUSE_S )
462             i_state = 1;
463         else if( i_val <= PLAYING_S )
464             i_state = 0;
465         vlc_object_release( p_input );
466     }
467
468     i_random = var_CreateGetBool( p_playlist, "random" );
469
470     i_repeat = var_CreateGetBool( p_playlist, "repeat" );
471
472     i_loop = var_CreateGetBool( p_playlist, "loop" );
473
474     dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
475     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
476     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
477     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
478     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
479     dbus_message_iter_close_container( args, &status );
480
481     return VLC_SUCCESS;
482 }