1 /*****************************************************************************
2 * event.c: New libvlc event control API
3 *****************************************************************************
4 * Copyright (C) 2007-2010 the VideoLAN team
7 * Authors: Filippo Carone <filippo@carone.org>
8 * Pierre d'Herbemont <pdherbemont # videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
29 #define LIBVLC_EVENT_TYPES_KEEP_DEFINE
30 #include <vlc/libvlc.h>
32 #include "libvlc_internal.h"
33 #include "event_internal.h"
37 typedef struct libvlc_event_listeners_group_t
39 libvlc_event_type_t event_type;
40 vlc_array_t listeners;
41 bool b_sublistener_removed;
42 } libvlc_event_listeners_group_t;
49 group_contains_listener( libvlc_event_listeners_group_t * group,
50 libvlc_event_listener_t * searched_listener )
53 for( i = 0; i < vlc_array_count(&group->listeners); i++ )
55 if( listeners_are_equal(searched_listener, vlc_array_item_at_index(&group->listeners, i)) )
62 * Internal libvlc functions
65 /**************************************************************************
66 * libvlc_event_manager_new (internal) :
68 * Init an object's event manager.
69 **************************************************************************/
70 libvlc_event_manager_t *
71 libvlc_event_manager_new( void * p_obj, libvlc_instance_t * p_libvlc_inst )
73 libvlc_event_manager_t * p_em;
75 p_em = malloc(sizeof( libvlc_event_manager_t ));
78 libvlc_printerr( "Not enough memory" );
84 p_em->async_event_queue = NULL;
85 p_em->p_libvlc_instance = p_libvlc_inst;
87 libvlc_retain( p_libvlc_inst );
88 vlc_array_init( &p_em->listeners_groups );
89 vlc_mutex_init( &p_em->object_lock );
90 vlc_mutex_init_recursive( &p_em->event_sending_lock );
94 /**************************************************************************
95 * libvlc_event_manager_release (internal) :
97 * Release an object's event manager.
98 **************************************************************************/
99 void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
101 libvlc_event_listeners_group_t * p_lg;
104 libvlc_event_async_fini(p_em);
106 vlc_mutex_destroy( &p_em->event_sending_lock );
107 vlc_mutex_destroy( &p_em->object_lock );
109 for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
111 p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );
113 for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
114 free( vlc_array_item_at_index( &p_lg->listeners, j ) );
116 vlc_array_clear( &p_lg->listeners );
119 vlc_array_clear( &p_em->listeners_groups );
120 libvlc_release( p_em->p_libvlc_instance );
124 /**************************************************************************
125 * libvlc_event_manager_register_event_type (internal) :
127 * Init an object's event manager.
128 **************************************************************************/
129 void libvlc_event_manager_register_event_type(
130 libvlc_event_manager_t * p_em,
131 libvlc_event_type_t event_type )
133 libvlc_event_listeners_group_t * listeners_group;
134 listeners_group = xmalloc(sizeof(libvlc_event_listeners_group_t));
135 listeners_group->event_type = event_type;
136 vlc_array_init( &listeners_group->listeners );
138 vlc_mutex_lock( &p_em->object_lock );
139 vlc_array_append( &p_em->listeners_groups, listeners_group );
140 vlc_mutex_unlock( &p_em->object_lock );
143 /**************************************************************************
144 * libvlc_event_send (internal) :
147 **************************************************************************/
148 void libvlc_event_send( libvlc_event_manager_t * p_em,
149 libvlc_event_t * p_event )
151 libvlc_event_listeners_group_t * listeners_group = NULL;
152 libvlc_event_listener_t * listener_cached;
153 libvlc_event_listener_t * listener;
154 libvlc_event_listener_t * array_listeners_cached = NULL;
155 int i, i_cached_listeners = 0;
157 /* Fill event with the sending object now */
158 p_event->p_obj = p_em->p_obj;
160 /* Here a read/write lock would be nice */
162 vlc_mutex_lock( &p_em->object_lock );
163 for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
165 listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
166 if( listeners_group->event_type == p_event->type )
168 if( vlc_array_count( &listeners_group->listeners ) <= 0 )
171 /* Cache a copy of the listener to avoid locking issues,
172 * and allow that edition of listeners during callbacks will garantee immediate effect. */
173 i_cached_listeners = vlc_array_count(&listeners_group->listeners);
174 array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
175 if( !array_listeners_cached )
177 vlc_mutex_unlock( &p_em->object_lock );
178 fprintf(stderr, "Can't alloc memory in libvlc_event_send" );
182 listener_cached = array_listeners_cached;
183 for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
185 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
186 memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
193 if( !listeners_group )
195 free( array_listeners_cached );
196 vlc_mutex_unlock( &p_em->object_lock );
200 vlc_mutex_unlock( &p_em->object_lock );
202 vlc_mutex_lock( &p_em->event_sending_lock );
203 listener_cached = array_listeners_cached;
204 listeners_group->b_sublistener_removed = false;
205 for( i = 0; i < i_cached_listeners; i++ )
207 if(listener_cached->is_asynchronous)
209 /* The listener wants not to block the emitter during event callback */
210 libvlc_event_async_dispatch(p_em, listener_cached, p_event);
214 /* The listener wants to block the emitter during event callback */
216 listener_cached->pf_callback( p_event, listener_cached->p_user_data );
219 if( listeners_group->b_sublistener_removed )
221 /* If a callback was removed, this gets called */
223 vlc_mutex_lock( &p_em->object_lock );
224 valid_listener = group_contains_listener( listeners_group, listener_cached );
225 vlc_mutex_unlock( &p_em->object_lock );
226 if( !valid_listener )
234 vlc_mutex_unlock( &p_em->event_sending_lock );
236 free( array_listeners_cached );
240 * Public libvlc functions
243 #define DEF( a ) { libvlc_##a, #a, },
251 static const event_name_t event_list[] = {
252 DEF(MediaMetaChanged)
253 DEF(MediaSubItemAdded)
254 DEF(MediaDurationChanged)
255 DEF(MediaParsedChanged)
257 DEF(MediaStateChanged)
259 DEF(MediaPlayerMediaChanged)
260 DEF(MediaPlayerNothingSpecial)
261 DEF(MediaPlayerOpening)
262 DEF(MediaPlayerBuffering)
263 DEF(MediaPlayerPlaying)
264 DEF(MediaPlayerPaused)
265 DEF(MediaPlayerStopped)
266 DEF(MediaPlayerForward)
267 DEF(MediaPlayerBackward)
268 DEF(MediaPlayerEndReached)
269 DEF(MediaPlayerEncounteredError)
270 DEF(MediaPlayerTimeChanged)
271 DEF(MediaPlayerPositionChanged)
272 DEF(MediaPlayerSeekableChanged)
273 DEF(MediaPlayerPausableChanged)
274 DEF(MediaPlayerTitleChanged)
275 DEF(MediaPlayerSnapshotTaken)
276 DEF(MediaPlayerLengthChanged)
278 DEF(MediaListItemAdded)
279 DEF(MediaListWillAddItem)
280 DEF(MediaListItemDeleted)
281 DEF(MediaListWillDeleteItem)
283 DEF(MediaListViewItemAdded)
284 DEF(MediaListViewWillAddItem)
285 DEF(MediaListViewItemDeleted)
286 DEF(MediaListViewWillDeleteItem)
288 DEF(MediaListPlayerPlayed)
289 DEF(MediaListPlayerNextItemSet)
290 DEF(MediaListPlayerStopped)
292 DEF(MediaDiscovererStarted)
293 DEF(MediaDiscovererEnded)
298 DEF(VlmMediaInstanceStarted)
299 DEF(VlmMediaInstanceStopped)
300 DEF(VlmMediaInstanceStatusInit)
301 DEF(VlmMediaInstanceStatusOpening)
305 static const char unknown_event_name[] = "Unknown Event";
307 static int evcmp( const void *a, const void *b )
309 return (*(const int *)a) - ((event_name_t *)b)->type;
312 const char * libvlc_event_type_name( int event_type )
314 const event_name_t *p;
316 p = bsearch( &event_type, event_list,
317 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
319 return p ? p->name : unknown_event_name;
322 /**************************************************************************
323 * event_attach (internal) :
325 * Add a callback for an event.
326 **************************************************************************/
328 int event_attach( libvlc_event_manager_t * p_event_manager,
329 libvlc_event_type_t event_type,
330 libvlc_callback_t pf_callback, void *p_user_data,
331 bool is_asynchronous )
333 libvlc_event_listeners_group_t * listeners_group;
334 libvlc_event_listener_t * listener;
337 listener = malloc(sizeof(libvlc_event_listener_t));
338 if( unlikely(listener == NULL) )
341 listener->event_type = event_type;
342 listener->p_user_data = p_user_data;
343 listener->pf_callback = pf_callback;
344 listener->is_asynchronous = is_asynchronous;
346 vlc_mutex_lock( &p_event_manager->object_lock );
347 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
349 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
350 if( listeners_group->event_type == listener->event_type )
352 vlc_array_append( &listeners_group->listeners, listener );
353 vlc_mutex_unlock( &p_event_manager->object_lock );
357 vlc_mutex_unlock( &p_event_manager->object_lock );
360 fprintf( stderr, "This object event manager doesn't know about '%s' events",
361 libvlc_event_type_name(event_type) );
366 /**************************************************************************
367 * libvlc_event_attach (public) :
369 * Add a callback for an event.
370 **************************************************************************/
371 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
372 libvlc_event_type_t event_type,
373 libvlc_callback_t pf_callback,
376 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
377 false /* synchronous */);
380 /**************************************************************************
381 * libvlc_event_attach (public) :
383 * Add a callback for an event.
384 **************************************************************************/
385 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
386 libvlc_event_type_t event_type,
387 libvlc_callback_t pf_callback,
390 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
391 true /* asynchronous */);
394 /**************************************************************************
395 * libvlc_event_detach (public) :
397 * Remove a callback for an event.
398 **************************************************************************/
399 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
400 libvlc_event_type_t event_type,
401 libvlc_callback_t pf_callback,
404 libvlc_event_listeners_group_t * listeners_group;
405 libvlc_event_listener_t * listener;
409 vlc_mutex_lock( &p_event_manager->event_sending_lock );
410 vlc_mutex_lock( &p_event_manager->object_lock );
411 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
413 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
414 if( listeners_group->event_type == event_type )
416 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
418 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
419 if( listener->event_type == event_type &&
420 listener->pf_callback == pf_callback &&
421 listener->p_user_data == p_user_data )
423 /* that's our listener */
425 /* Mark this group as edited so that libvlc_event_send
426 * will recheck what listener to call */
427 listeners_group->b_sublistener_removed = false;
430 vlc_array_remove( &listeners_group->listeners, j );
437 vlc_mutex_unlock( &p_event_manager->object_lock );
438 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
440 /* Now make sure any pending async event won't get fired after that point */
441 libvlc_event_listener_t listener_to_remove;
442 listener_to_remove.event_type = event_type;
443 listener_to_remove.pf_callback = pf_callback;
444 listener_to_remove.p_user_data = p_user_data;
445 listener_to_remove.is_asynchronous = true;
447 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);