1 /*****************************************************************************
2 * dbus.c : D-Bus control interface
3 *****************************************************************************
4 * Copyright © 2006-2008 Rafaël Carré
5 * Copyright © 2007-2010 Mirsal Ennaime
6 * Copyright © 2009-2010 The VideoLAN team
9 * Authors: Rafaël Carré <funman at videolanorg>
10 * Mirsal Ennaime <mirsal at mirsal fr>
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.
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.
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 *****************************************************************************/
28 * D-Bus Specification:
29 * http://dbus.freedesktop.org/doc/dbus-specification.html
30 * D-Bus low-level C API (libdbus)
31 * http://dbus.freedesktop.org/doc/dbus/api/html/index.html
33 * "If you use this low-level API directly, you're signing up for some pain."
35 * MPRIS Specification version 1.0
36 * http://wiki.xmms2.xmms.se/index.php/MPRIS
39 /*****************************************************************************
41 *****************************************************************************/
47 #include <dbus/dbus.h>
49 #include "dbus_common.h"
50 #include "dbus_root.h"
51 #include "dbus_player.h"
52 #include "dbus_tracklist.h"
54 #include <vlc_common.h>
55 #include <vlc_fixups.h>
56 #include <vlc_plugin.h>
57 #include <vlc_interface.h>
58 #include <vlc_playlist.h>
60 #include <vlc_mtime.h>
70 /*****************************************************************************
72 *****************************************************************************/
83 DBusTimeout *p_timeout;
92 static int Open ( vlc_object_t * );
93 static void Close ( vlc_object_t * );
94 static void Run ( intf_thread_t * );
96 static int StateChange( intf_thread_t * );
97 static int TrackChange( intf_thread_t * );
98 static int AllCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
100 static void dispatch_status_cb( DBusConnection *p_conn,
101 DBusDispatchStatus i_status,
104 static dbus_bool_t add_timeout ( DBusTimeout *p_timeout, void *p_data );
105 static dbus_bool_t add_watch ( DBusWatch *p_watch, void *p_data );
107 static void remove_timeout ( DBusTimeout *p_timeout, void *p_data );
108 static void remove_watch ( DBusWatch *p_watch, void *p_data );
110 static void timeout_toggled ( DBusTimeout *p_timeout, void *p_data );
111 static void watch_toggled ( DBusWatch *p_watch, void *p_data );
113 static void wakeup_main_loop( void *p_data );
115 static int GetPollFds( intf_thread_t *p_intf, struct pollfd *p_fds );
116 static int UpdateTimeouts( intf_thread_t *p_intf, mtime_t i_lastrun );
118 static void ProcessEvents ( intf_thread_t *p_intf,
119 callback_info_t **p_events,
122 static void ProcessWatches ( intf_thread_t *p_intf,
123 DBusWatch **p_watches,
125 struct pollfd *p_fds,
128 static void ProcessTimeouts( intf_thread_t *p_intf,
129 DBusTimeout **p_timeouts,
132 static void DispatchDBusMessages( intf_thread_t *p_intf );
134 /*****************************************************************************
136 *****************************************************************************/
137 #define DBUS_UNIQUE_TEXT N_("Unique DBUS service id (org.mpris.vlc-<pid>)")
138 #define DBUS_UNIQUE_LONGTEXT N_( \
139 "Use a unique dbus service id to identify this VLC instance on the DBUS bus. " \
140 "The process identifier (PID) is added to the service name: org.mpris.vlc-<pid>" )
143 set_shortname( N_("dbus"))
144 set_category( CAT_INTERFACE )
145 set_subcategory( SUBCAT_INTERFACE_CONTROL )
146 set_description( N_("D-Bus control interface") )
147 set_capability( "interface", 0 )
148 set_callbacks( Open, Close )
149 add_bool( "dbus-unique-service-id", false,
150 DBUS_UNIQUE_TEXT, DBUS_UNIQUE_LONGTEXT, true )
153 /*****************************************************************************
154 * Open: initialize interface
155 *****************************************************************************/
157 static int Open( vlc_object_t *p_this )
158 { /* initialisation of the connection */
159 intf_thread_t *p_intf = (intf_thread_t*)p_this;
160 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
161 playlist_t *p_playlist;
162 DBusConnection *p_conn;
164 char *psz_service_name = NULL;
166 if( !p_sys || !dbus_threads_init_default())
169 p_sys->b_meta_read = false;
170 p_sys->i_caps = CAPS_NONE;
171 p_sys->b_dead = false;
172 p_sys->p_input = NULL;
173 p_sys->i_playing_state = -1;
175 if( vlc_pipe( p_sys->p_pipe_fds ) )
178 msg_Err( p_intf, "Could not create pipe" );
182 p_sys->b_unique = var_CreateGetBool( p_intf, "dbus-unique-service-id" );
183 if( p_sys->b_unique )
185 if( asprintf( &psz_service_name, "%s-%d",
186 DBUS_MPRIS_BUS_NAME, getpid() ) < 0 )
194 psz_service_name = strdup(DBUS_MPRIS_BUS_NAME);
197 dbus_error_init( &error );
199 /* connect privately to the session bus
200 * the connection will not be shared with other vlc modules which use dbus,
201 * thus avoiding a whole class of concurrency issues */
202 p_conn = dbus_bus_get_private( DBUS_BUS_SESSION, &error );
205 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
207 dbus_error_free( &error );
208 free( psz_service_name );
213 dbus_connection_set_exit_on_disconnect( p_conn, FALSE );
215 /* register a well-known name on the bus */
216 dbus_bus_request_name( p_conn, psz_service_name, 0, &error );
217 if( dbus_error_is_set( &error ) )
219 msg_Err( p_this, "Error requesting service %s: %s",
220 psz_service_name, error.message );
221 dbus_error_free( &error );
222 free( psz_service_name );
226 msg_Info( p_intf, "listening on dbus as: %s", psz_service_name );
227 free( psz_service_name );
229 /* we register the objects */
230 dbus_connection_register_object_path( p_conn, DBUS_MPRIS_ROOT_PATH,
231 &dbus_mpris_root_vtable, p_this );
232 dbus_connection_register_object_path( p_conn, DBUS_MPRIS_PLAYER_PATH,
233 &dbus_mpris_player_vtable, p_this );
234 dbus_connection_register_object_path( p_conn, DBUS_MPRIS_TRACKLIST_PATH,
235 &dbus_mpris_tracklist_vtable, p_this );
237 dbus_connection_flush( p_conn );
239 p_intf->pf_run = Run;
240 p_intf->p_sys = p_sys;
241 p_sys->p_conn = p_conn;
242 p_sys->p_events = vlc_array_new();
243 p_sys->p_timeouts = vlc_array_new();
244 p_sys->p_watches = vlc_array_new();
245 vlc_mutex_init( &p_sys->lock );
247 p_playlist = pl_Get( p_intf );
248 p_sys->p_playlist = p_playlist;
250 var_AddCallback( p_playlist, "item-current", AllCallback, p_intf );
251 var_AddCallback( p_playlist, "intf-change", AllCallback, p_intf );
252 var_AddCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
253 var_AddCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
254 var_AddCallback( p_playlist, "random", AllCallback, p_intf );
255 var_AddCallback( p_playlist, "repeat", AllCallback, p_intf );
256 var_AddCallback( p_playlist, "loop", AllCallback, p_intf );
258 dbus_connection_set_dispatch_status_function( p_conn,
262 if( !dbus_connection_set_timeout_functions( p_conn,
268 dbus_connection_unref( p_conn );
269 free( psz_service_name );
274 if( !dbus_connection_set_watch_functions( p_conn,
280 dbus_connection_unref( p_conn );
281 free( psz_service_name );
286 /* dbus_connection_set_wakeup_main_function( p_conn,
290 UpdateCaps( p_intf );
295 /*****************************************************************************
296 * Close: destroy interface
297 *****************************************************************************/
299 static void Close ( vlc_object_t *p_this )
301 intf_thread_t *p_intf = (intf_thread_t*) p_this;
302 intf_sys_t *p_sys = p_intf->p_sys;
303 playlist_t *p_playlist = p_sys->p_playlist;
305 var_DelCallback( p_playlist, "item-current", AllCallback, p_intf );
306 var_DelCallback( p_playlist, "intf-change", AllCallback, p_intf );
307 var_DelCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
308 var_DelCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
309 var_DelCallback( p_playlist, "random", AllCallback, p_intf );
310 var_DelCallback( p_playlist, "repeat", AllCallback, p_intf );
311 var_DelCallback( p_playlist, "loop", AllCallback, p_intf );
315 var_DelCallback( p_sys->p_input, "intf-event", AllCallback, p_intf );
316 vlc_object_release( p_sys->p_input );
319 /* The dbus connection is private, so we are responsible
321 dbus_connection_close( p_sys->p_conn );
322 dbus_connection_unref( p_sys->p_conn );
324 // Free the events array
325 for( int i = 0; i < vlc_array_count( p_sys->p_events ); i++ )
327 callback_info_t* info = vlc_array_item_at_index( p_sys->p_events, i );
330 vlc_mutex_destroy( &p_sys->lock );
331 vlc_array_destroy( p_sys->p_events );
332 vlc_array_destroy( p_sys->p_timeouts );
333 vlc_array_destroy( p_sys->p_watches );
337 static void dispatch_status_cb( DBusConnection *p_conn,
338 DBusDispatchStatus i_status,
342 intf_thread_t *p_intf = (intf_thread_t*) p_data;
344 static const char *p_statuses[] = { "DATA_REMAINS",
349 "DBus dispatch status changed to %s.",
350 p_statuses[i_status]);
353 static dbus_bool_t add_timeout( DBusTimeout *p_timeout, void *p_data )
355 intf_thread_t *p_intf = (intf_thread_t*) p_data;
356 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
358 timeout_info_t *p_info = calloc( 1, sizeof( timeout_info_t ) );
359 p_info->i_remaining = dbus_timeout_get_interval( p_timeout ) * 1000;/* µs */
360 p_info->p_timeout = p_timeout;
362 dbus_timeout_set_data( p_timeout, p_info, free );
364 vlc_mutex_lock( &p_sys->lock );
365 vlc_array_append( p_sys->p_timeouts, p_timeout );
366 vlc_mutex_unlock( &p_sys->lock );
371 static void remove_timeout( DBusTimeout *p_timeout, void *p_data )
373 intf_thread_t *p_intf = (intf_thread_t*) p_data;
374 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
376 vlc_mutex_lock( &p_sys->lock );
378 vlc_array_remove( p_sys->p_timeouts,
379 vlc_array_index_of_item( p_sys->p_timeouts, p_timeout ) );
381 vlc_mutex_unlock( &p_sys->lock );
384 static void timeout_toggled( DBusTimeout *p_timeout, void *p_data )
386 intf_thread_t *p_intf = (intf_thread_t*) p_data;
388 msg_Dbg( p_intf, "Toggling dbus timeout" );
390 if( dbus_timeout_get_enabled( p_timeout ) )
392 msg_Dbg( p_intf, "Timeout is enabled, main loop needs to wake up" );
393 wakeup_main_loop( p_intf );
397 static dbus_bool_t add_watch( DBusWatch *p_watch, void *p_data )
399 intf_thread_t *p_intf = (intf_thread_t*) p_data;
400 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
401 int i_fd = dbus_watch_get_unix_fd( p_watch );
403 msg_Dbg( p_intf, "Adding dbus watch on fd %d", i_fd );
405 if( dbus_watch_get_flags( p_watch ) & DBUS_WATCH_READABLE )
406 msg_Dbg( p_intf, "Watching fd %d for readability", i_fd );
408 if( dbus_watch_get_flags( p_watch ) & DBUS_WATCH_WRITABLE )
409 msg_Dbg( p_intf, "Watching fd %d for writeability", i_fd );
411 vlc_mutex_lock( &p_sys->lock );
412 vlc_array_append( p_sys->p_watches, p_watch );
413 vlc_mutex_unlock( &p_sys->lock );
418 static void remove_watch( DBusWatch *p_watch, void *p_data )
420 intf_thread_t *p_intf = (intf_thread_t*) p_data;
421 intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
423 msg_Dbg( p_intf, "Removing dbus watch on fd %d",
424 dbus_watch_get_unix_fd( p_watch ) );
426 vlc_mutex_lock( &p_sys->lock );
428 vlc_array_remove( p_sys->p_watches,
429 vlc_array_index_of_item( p_sys->p_watches, p_watch ) );
431 vlc_mutex_unlock( &p_sys->lock );
434 static void watch_toggled( DBusWatch *p_watch, void *p_data )
436 intf_thread_t *p_intf = (intf_thread_t*) p_data;
438 msg_Dbg( p_intf, "Toggling dbus watch on fd %d",
439 dbus_watch_get_unix_fd( p_watch ) );
441 if( dbus_watch_get_enabled( p_watch ) )
444 "Watch on fd %d has been enabled, "
445 "the main loops needs to wake up",
446 dbus_watch_get_unix_fd( p_watch ) );
448 wakeup_main_loop( p_intf );
453 * GetPollFds() fills an array of pollfd data structures with :
454 * - the set of enabled dbus watches
455 * - the unix pipe which we use to manually wake up the main loop
457 * This function must be called with p_sys->lock locked
459 * @return The number of file descriptors
461 * @param intf_thread_t *p_intf this interface thread's state
462 * @param struct pollfd *p_fds a pointer to a pollfd array large enough to
463 * contain all the returned data (number of enabled dbus watches + 1)
465 static int GetPollFds( intf_thread_t *p_intf, struct pollfd *p_fds )
467 intf_sys_t *p_sys = p_intf->p_sys;
468 int i_fds = 1, i_watches = vlc_array_count( p_sys->p_watches );
470 p_fds[0].fd = p_sys->p_pipe_fds[PIPE_OUT];
471 p_fds[0].events = POLLIN | POLLPRI;
473 for( int i = 0; i < i_watches; i++ )
475 DBusWatch *p_watch = NULL;
476 p_watch = vlc_array_item_at_index( p_sys->p_watches, i );
477 if( !dbus_watch_get_enabled( p_watch ) )
480 p_fds[i_fds].fd = dbus_watch_get_unix_fd( p_watch );
481 int i_flags = dbus_watch_get_flags( p_watch );
483 if( i_flags & DBUS_WATCH_READABLE )
484 p_fds[i_fds].events |= POLLIN | POLLPRI;
486 if( i_flags & DBUS_WATCH_WRITABLE )
487 p_fds[i_fds].events |= POLLOUT;
496 * UpdateTimeouts() updates the remaining time for each timeout and
497 * returns how much time is left until the next timeout.
499 * This function must be called with p_sys->lock locked
501 * @return int The time remaining until the next timeout, in milliseconds
502 * or -1 if there are no timeouts
504 * @param intf_thread_t *p_intf This interface thread's state
505 * @param mtime_t i_loop_interval The time which has elapsed since the last
506 * call to this function
508 static int UpdateTimeouts( intf_thread_t *p_intf, mtime_t i_loop_interval )
510 intf_sys_t *p_sys = p_intf->p_sys;
511 mtime_t i_next_timeout = LAST_MDATE;
512 unsigned int i_timeouts = vlc_array_count( p_sys->p_timeouts );
514 if( 0 == i_timeouts )
517 for( unsigned int i = 0; i < i_timeouts; i++ )
519 timeout_info_t *p_info = NULL;
520 DBusTimeout *p_timeout = NULL;
521 mtime_t i_interval = 0;
523 p_timeout = vlc_array_item_at_index( p_sys->p_timeouts, i );
524 i_interval = dbus_timeout_get_interval( p_timeout ) * 1000; /* µs */
525 p_info = (timeout_info_t*) dbus_timeout_get_data( p_timeout );
527 p_info->i_remaining -= __MAX( 0, i_loop_interval ) % i_interval;
529 if( !dbus_timeout_get_enabled( p_timeout ) )
532 /* The correct poll timeout value is the shortest one
533 * in the dbus timeouts list */
534 i_next_timeout = __MIN( i_next_timeout,
535 __MAX( 0, p_info->i_remaining ) );
538 /* next timeout in milliseconds */
539 return i_next_timeout / 1000;
543 * ProcessEvents() reacts to a list of events originating from other VLC threads
545 * This function must be called with p_sys->lock unlocked
547 * @param intf_thread_t *p_intf This interface thread state
548 * @param callback_info_t *p_events the list of events to process
550 static void ProcessEvents( intf_thread_t *p_intf,
551 callback_info_t **p_events, int i_events )
553 for( int i = 0; i < i_events; i++ )
555 switch( p_events[i]->signal )
557 case SIGNAL_ITEM_CURRENT:
558 TrackChange( p_intf );
560 case SIGNAL_INTF_CHANGE:
561 case SIGNAL_PLAYLIST_ITEM_APPEND:
562 case SIGNAL_PLAYLIST_ITEM_DELETED:
563 TrackListChangeEmit( p_intf,
565 p_events[i]->i_node );
570 StatusChangeEmit( p_intf );
573 StateChange( p_intf );
575 case SIGNAL_INPUT_METADATA:
585 * ProcessWatches() handles a list of dbus watches after poll() has returned
587 * This function must be called with p_sys->lock unlocked
589 * @param intf_thread_t *p_intf This interface thread state
590 * @param DBusWatch **p_watches The list of dbus watches to process
591 * @param int i_watches The size of the p_watches array
592 * @param struct pollfd *p_fds The result of a poll() call
593 * @param int i_fds The number of file descriptors processed by poll()
595 static void ProcessWatches( intf_thread_t *p_intf,
596 DBusWatch **p_watches, int i_watches,
597 struct pollfd *p_fds, int i_fds )
599 /* Process watches */
600 for( int i = 0; i < i_watches; i++ )
602 DBusWatch *p_watch = p_watches[i];
603 if( !dbus_watch_get_enabled( p_watch ) )
606 for( int j = 0; j < i_fds; j++ )
608 if( p_fds[j].fd != dbus_watch_get_unix_fd( p_watch ) )
612 int i_revents = p_fds[j].revents;
613 int i_fd = p_fds[j].fd;
615 if( i_revents & POLLIN )
617 msg_Dbg( p_intf, "fd %d is ready for reading", i_fd );
618 i_flags |= DBUS_WATCH_READABLE;
621 if( i_revents & POLLOUT )
623 msg_Dbg( p_intf, "fd %d is ready for writing", i_fd );
624 i_flags |= DBUS_WATCH_WRITABLE;
627 if( i_revents & POLLERR )
629 msg_Dbg( p_intf, "error when polling fd %d", i_fd );
630 i_flags |= DBUS_WATCH_ERROR;
633 if( i_revents & POLLHUP )
635 msg_Dbg( p_intf, "Hangup signal on fd %d", i_fd );
636 i_flags |= DBUS_WATCH_HANGUP;
641 msg_Dbg( p_intf, "Handling dbus watch on fd %d", i_fd );
642 dbus_watch_handle( p_watch, i_flags );
645 msg_Dbg( p_intf, "Nothing happened on fd %d", i_fd );
651 * ProcessTimeouts() handles DBus timeouts
653 * This function must be called with p_sys->lock locked
655 * @param intf_thread_t *p_intf This interface thread state
656 * @param DBusTimeout **p_timeouts List of timeouts to process
657 * @param int i_timeouts Size of p_timeouts
659 static void ProcessTimeouts( intf_thread_t *p_intf,
660 DBusTimeout **p_timeouts, int i_timeouts )
662 VLC_UNUSED( p_intf );
664 for( int i = 0; i < i_timeouts; i++ )
666 timeout_info_t *p_info = NULL;
668 p_info = (timeout_info_t*) dbus_timeout_get_data( p_timeouts[i] );
670 if( !dbus_timeout_get_enabled( p_info->p_timeout ) )
673 if( p_info->i_remaining > 0 )
676 dbus_timeout_handle( p_info->p_timeout );
677 p_info->i_remaining = dbus_timeout_get_interval( p_info->p_timeout );
682 * DispatchDBusMessages() dispatches incoming dbus messages
683 * (indirectly invoking the callbacks), then it sends outgoing
684 * messages which needs to be sent on the bus (method replies and signals)
686 * This function must be called with p_sys->lock unlocked
688 * @param intf_thread_t *p_intf This interface thread state
690 static void DispatchDBusMessages( intf_thread_t *p_intf )
692 DBusDispatchStatus status;
693 intf_sys_t *p_sys = p_intf->p_sys;
695 /* Dispatch incoming messages */
696 status = dbus_connection_get_dispatch_status( p_sys->p_conn );
697 while( status != DBUS_DISPATCH_COMPLETE )
699 msg_Dbg( p_intf, "Dispatching incoming dbus message" );
700 dbus_connection_dispatch( p_sys->p_conn );
701 status = dbus_connection_get_dispatch_status( p_sys->p_conn );
704 /* Send outgoing data */
705 if( dbus_connection_has_messages_to_send( p_sys->p_conn ) )
707 msg_Dbg( p_intf, "Sending outgoing data" );
708 dbus_connection_flush( p_sys->p_conn );
712 /*****************************************************************************
714 *****************************************************************************/
716 static void Run ( intf_thread_t *p_intf )
718 intf_sys_t *p_sys = p_intf->p_sys;
719 mtime_t i_last_run = mdate();
723 int canc = vlc_savecancel();
724 vlc_mutex_lock( &p_sys->lock );
726 int i_watches = vlc_array_count( p_sys->p_watches );
727 struct pollfd *p_fds = calloc( i_watches, sizeof( struct pollfd ) );
729 int i_fds = GetPollFds( p_intf, p_fds );
731 mtime_t i_now = mdate(), i_loop_interval = i_now - i_last_run;
734 "%lld µs elapsed since last wakeup",
735 (long long) i_loop_interval );
737 int i_next_timeout = UpdateTimeouts( p_intf, i_loop_interval );
740 vlc_mutex_unlock( &p_sys->lock );
742 if( -1 != i_next_timeout )
743 msg_Dbg( p_intf, "next timeout is in %d ms", i_next_timeout );
744 msg_Dbg( p_intf, "Sleeping until something happens" );
746 /* thread cancellation is allowed while the main loop sleeps */
747 vlc_restorecancel( canc );
749 int i_pollres = poll( p_fds, i_fds, i_next_timeout );
752 canc = vlc_savecancel();
754 msg_Dbg( p_intf, "the main loop has been woken up" );
756 if( -1 == i_pollres )
757 { /* XXX: What should we do when poll() fails ? */
759 msg_Err( p_intf, "poll() failed: %s", strerror_r( i_errsv, buf, 64 ) );
760 free( p_fds ); p_fds = NULL;
761 vlc_restorecancel( canc );
765 /* Was the main loop woken up manually ? */
766 if( 0 < i_pollres && ( p_fds[0].revents & POLLIN ) )
769 msg_Dbg( p_intf, "Removing a byte from the self-pipe" );
770 (void)read( p_fds[0].fd, &buf, 1 );
773 /* We need to lock the mutex while building lists of events,
774 * timeouts and watches to process but we can't keep the lock while
775 * processing them, or else we risk a deadlock:
777 * The signal functions could lock mutex X while p_events is locked;
778 * While some other function in vlc (playlist) might lock mutex X
779 * and then set a variable which would call AllCallback(), which itself
780 * needs to lock p_events to add a new event.
782 vlc_mutex_lock( &p_intf->p_sys->lock );
784 /* Get the list of timeouts to process */
785 unsigned int i_timeouts = vlc_array_count( p_sys->p_timeouts );
786 DBusTimeout *p_timeouts[i_timeouts];
787 for( unsigned int i = 0; i < i_timeouts; i++ )
789 p_timeouts[i] = vlc_array_item_at_index( p_sys->p_timeouts, i );
792 /* Get the list of watches to process */
793 i_watches = vlc_array_count( p_sys->p_watches );
794 DBusWatch *p_watches[i_watches];
795 for( int i = 0; i < i_watches; i++ )
797 p_watches[i] = vlc_array_item_at_index( p_sys->p_watches, i );
800 /* Get the list of events to process */
801 int i_events = vlc_array_count( p_intf->p_sys->p_events );
802 callback_info_t* p_info[i_events];
803 for( int i = i_events - 1; i >= 0; i-- )
805 p_info[i] = vlc_array_item_at_index( p_intf->p_sys->p_events, i );
806 vlc_array_remove( p_intf->p_sys->p_events, i );
809 /* now we can release the lock and process what's pending */
810 vlc_mutex_unlock( &p_intf->p_sys->lock );
812 ProcessEvents( p_intf, p_info, i_events );
813 ProcessWatches( p_intf, p_watches, i_watches, p_fds, i_fds );
815 free( p_fds ); p_fds = NULL;
817 ProcessTimeouts( p_intf, p_timeouts, i_timeouts );
818 DispatchDBusMessages( p_intf );
820 vlc_restorecancel( canc );
824 static void wakeup_main_loop( void *p_data )
826 intf_thread_t *p_intf = (intf_thread_t*) p_data;
828 msg_Dbg( p_intf, "Sending wakeup signal to the main loop" );
830 if( !write( p_intf->p_sys->p_pipe_fds[PIPE_IN], "\0", 1 ) )
833 "Could not wake up the main loop: %s", strerror( errno ) );
837 /*****************************************************************************
838 * UpdateCaps: update p_sys->i_caps
839 * This function have to be called with the playlist unlocked
840 ****************************************************************************/
841 int UpdateCaps( intf_thread_t* p_intf )
843 intf_sys_t* p_sys = p_intf->p_sys;
844 dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
845 playlist_t* p_playlist = p_sys->p_playlist;
848 if( p_playlist->current.i_size > 0 )
849 i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
852 input_thread_t* p_input = playlist_CurrentInput( p_playlist );
855 /* XXX: if UpdateCaps() is called too early, these are
856 * unconditionnaly true */
857 if( var_GetBool( p_input, "can-pause" ) )
858 i_caps |= CAPS_CAN_PAUSE;
859 if( var_GetBool( p_input, "can-seek" ) )
860 i_caps |= CAPS_CAN_SEEK;
861 vlc_object_release( p_input );
864 if( p_sys->b_meta_read )
865 i_caps |= CAPS_CAN_PROVIDE_METADATA;
867 if( i_caps != p_intf->p_sys->i_caps )
869 p_sys->i_caps = i_caps;
870 CapsChangeEmit( p_intf );
876 /* InputIntfEventCallback() fills a callback_info_t data structure in response
877 * to an "intf-event" input event.
879 * Caution: This function executes in the input thread
881 * This function must be called with p_sys->lock locked
883 * @return int VLC_SUCCESS on success, VLC_E* on error
884 * @param intf_thread_t *p_intf the interface thread
885 * @param input_thread_t *p_input This input thread
886 * @param const int i_event input event type
887 * @param callback_info_t *p_info Location of the callback info to fill
889 static int InputIntfEventCallback( intf_thread_t *p_intf,
890 input_thread_t *p_input,
892 callback_info_t *p_info )
894 dbus_int32_t i_state = PLAYBACK_STATE_INVALID;
895 assert(!p_info->signal);
899 case INPUT_EVENT_DEAD:
900 case INPUT_EVENT_ABORT:
901 i_state = PLAYBACK_STATE_STOPPED;
903 case INPUT_EVENT_STATE:
904 i_state = ( var_GetInteger( p_input, "state" ) == PAUSE_S ) ?
905 PLAYBACK_STATE_PAUSED : PLAYBACK_STATE_PLAYING;
907 case INPUT_EVENT_ITEM_META:
908 p_info->signal = SIGNAL_INPUT_METADATA;
914 if( i_state != p_intf->p_sys->i_playing_state )
916 p_intf->p_sys->i_playing_state = i_state;
917 p_info->signal = SIGNAL_STATE;
920 return p_info->signal ? VLC_SUCCESS : VLC_EGENERIC;
923 // Get all the callbacks
924 static int AllCallback( vlc_object_t *p_this, const char *psz_var,
925 vlc_value_t oldval, vlc_value_t newval, void *p_data )
930 intf_thread_t *p_intf = (intf_thread_t*)p_data;
931 callback_info_t *info = calloc( 1, sizeof( callback_info_t ) );
936 vlc_mutex_lock( &p_intf->p_sys->lock );
938 // Wich event is it ?
939 if( !strcmp( "item-current", psz_var ) )
940 info->signal = SIGNAL_ITEM_CURRENT;
942 else if( !strcmp( "intf-change", psz_var ) )
943 info->signal = SIGNAL_INTF_CHANGE;
945 else if( !strcmp( "playlist-item-append", psz_var ) )
947 info->signal = SIGNAL_PLAYLIST_ITEM_APPEND;
948 info->i_node = ((playlist_add_t*)newval.p_address)->i_node;
951 else if( !strcmp( "playlist-item-deleted", psz_var ) )
952 info->signal = SIGNAL_PLAYLIST_ITEM_DELETED;
954 else if( !strcmp( "random", psz_var ) )
955 info->signal = SIGNAL_RANDOM;
957 else if( !strcmp( "repeat", psz_var ) )
958 info->signal = SIGNAL_REPEAT;
960 else if( !strcmp( "loop", psz_var ) )
961 info->signal = SIGNAL_LOOP;
963 else if( !strcmp( "intf-event", psz_var ) )
966 i_res = InputIntfEventCallback( p_intf, p_this, newval.i_int, info );
968 if( VLC_SUCCESS != i_res )
970 vlc_mutex_unlock( &p_intf->p_sys->lock );
981 vlc_array_append( p_intf->p_sys->p_events, info );
982 vlc_mutex_unlock( &p_intf->p_sys->lock );
985 "Got a VLC event on %s. The main loop needs to wake up "
986 "in order to process it", psz_var );
988 wakeup_main_loop( p_intf );
993 /*****************************************************************************
994 * StateChange: callback on input "state"
995 *****************************************************************************/
996 static int StateChange( intf_thread_t *p_intf )
998 intf_sys_t *p_sys = p_intf->p_sys;
999 playlist_t *p_playlist = p_sys->p_playlist;
1000 input_thread_t *p_input;
1001 input_item_t *p_item;
1003 if( p_intf->p_sys->b_dead )
1006 UpdateCaps( p_intf );
1008 if( !p_sys->b_meta_read && p_sys->i_playing_state == 0)
1010 p_input = playlist_CurrentInput( p_playlist );
1013 p_item = input_GetItem( p_input );
1016 p_sys->b_meta_read = true;
1017 TrackChangeEmit( p_intf, p_item );
1019 vlc_object_release( p_input );
1023 StatusChangeEmit( p_intf );
1028 /*****************************************************************************
1029 * TrackChange: callback on playlist "item-current"
1030 *****************************************************************************/
1031 static int TrackChange( intf_thread_t *p_intf )
1033 intf_sys_t *p_sys = p_intf->p_sys;
1034 playlist_t *p_playlist = p_sys->p_playlist;
1035 input_thread_t *p_input = NULL;
1036 input_item_t *p_item = NULL;
1038 if( p_intf->p_sys->b_dead )
1041 if( p_sys->p_input )
1043 var_DelCallback( p_sys->p_input, "intf-event", AllCallback, p_intf );
1044 vlc_object_release( p_sys->p_input );
1045 p_sys->p_input = NULL;
1048 p_sys->b_meta_read = false;
1050 p_input = playlist_CurrentInput( p_playlist );
1056 p_item = input_GetItem( p_input );
1059 vlc_object_release( p_input );
1060 return VLC_EGENERIC;
1063 if( input_item_IsPreparsed( p_item ) )
1065 p_sys->b_meta_read = true;
1066 TrackChangeEmit( p_intf, p_item );
1069 p_sys->p_input = p_input;
1070 var_AddCallback( p_input, "intf-event", AllCallback, p_intf );
1075 /*****************************************************************************
1076 * GetInputMeta: Fill a DBusMessage with the given input item metadata
1077 *****************************************************************************/
1079 #define ADD_META( entry, type, data ) \
1081 dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
1082 NULL, &dict_entry ); \
1083 dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
1084 &ppsz_meta_items[entry] ); \
1085 dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
1086 type##_AS_STRING, &variant ); \
1087 dbus_message_iter_append_basic( &variant, \
1090 dbus_message_iter_close_container( &dict_entry, &variant ); \
1091 dbus_message_iter_close_container( &dict, &dict_entry ); }
1093 #define ADD_VLC_META_STRING( entry, item ) \
1095 char * psz = input_item_Get##item( p_input );\
1096 ADD_META( entry, DBUS_TYPE_STRING, \
1101 int GetInputMeta( input_item_t* p_input,
1102 DBusMessageIter *args )
1104 DBusMessageIter dict, dict_entry, variant;
1105 /** The duration of the track can be expressed in second, milli-seconds and
1107 dbus_int64_t i_mtime = input_item_GetDuration( p_input );
1108 dbus_uint32_t i_time = i_mtime / 1000000;
1109 dbus_int64_t i_length = i_mtime / 1000;
1111 const char* ppsz_meta_items[] =
1113 /* Official MPRIS metas */
1114 "location", "title", "artist", "album", "tracknumber", "time", "mtime",
1115 "genre", "rating", "date", "arturl",
1116 "audio-bitrate", "audio-samplerate", "video-bitrate",
1117 /* VLC specifics metas */
1118 "audio-codec", "copyright", "description", "encodedby", "language", "length",
1119 "nowplaying", "publisher", "setting", "status", "trackid", "url",
1123 dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
1125 ADD_VLC_META_STRING( 0, URI );
1126 ADD_VLC_META_STRING( 1, Title );
1127 ADD_VLC_META_STRING( 2, Artist );
1128 ADD_VLC_META_STRING( 3, Album );
1129 ADD_VLC_META_STRING( 4, TrackNum );
1130 ADD_META( 5, DBUS_TYPE_UINT32, i_time );
1131 ADD_META( 6, DBUS_TYPE_UINT32, i_mtime );
1132 ADD_VLC_META_STRING( 7, Genre );
1133 ADD_VLC_META_STRING( 8, Rating );
1134 ADD_VLC_META_STRING( 9, Date );
1135 ADD_VLC_META_STRING( 10, ArtURL );
1137 ADD_VLC_META_STRING( 15, Copyright );
1138 ADD_VLC_META_STRING( 16, Description );
1139 ADD_VLC_META_STRING( 17, EncodedBy );
1140 ADD_VLC_META_STRING( 18, Language );
1141 ADD_META( 19, DBUS_TYPE_INT64, i_length );
1142 ADD_VLC_META_STRING( 20, NowPlaying );
1143 ADD_VLC_META_STRING( 21, Publisher );
1144 ADD_VLC_META_STRING( 22, Setting );
1145 ADD_VLC_META_STRING( 24, TrackID );
1146 ADD_VLC_META_STRING( 25, URL );
1148 vlc_mutex_lock( &p_input->lock );
1149 if( p_input->p_meta )
1151 int i_status = vlc_meta_GetStatus( p_input->p_meta );
1152 ADD_META( 23, DBUS_TYPE_INT32, i_status );
1154 vlc_mutex_unlock( &p_input->lock );
1156 dbus_message_iter_close_container( args, &dict );
1161 #undef ADD_VLC_META_STRING