access: bd: add assertion
[vlc.git] / lib / event.c
blob7cc17bfa5fded4d33ed725e6797d97e2182f5db0
1 /*****************************************************************************
2 * event.c: New libvlc event control API
3 *****************************************************************************
4 * Copyright (C) 2007-2010 VLC authors and VideoLAN
5 * $Id $
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 it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #define LIBVLC_EVENT_TYPES_KEEP_DEFINE
30 #include <vlc/libvlc.h>
32 #include "libvlc_internal.h"
33 #include "event_internal.h"
34 #include <assert.h>
35 #include <errno.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;
45 * Private functions
48 static bool
49 group_contains_listener( libvlc_event_listeners_group_t * group,
50 libvlc_event_listener_t * searched_listener )
52 int i;
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)) )
56 return true;
58 return false;
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 ));
76 if( !p_em )
78 libvlc_printerr( "Not enough memory" );
79 return NULL;
82 p_em->p_obj = p_obj;
83 p_em->p_obj = p_obj;
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 );
91 return p_em;
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;
102 int i,j ;
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 );
117 free( p_lg );
119 vlc_array_clear( &p_em->listeners_groups );
120 libvlc_release( p_em->p_libvlc_instance );
121 free( p_em );
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) :
146 * Send a callback.
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 vlc_mutex_lock( &p_em->event_sending_lock );
161 vlc_mutex_lock( &p_em->object_lock );
162 for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
164 listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
165 if( listeners_group->event_type == p_event->type )
167 if( vlc_array_count( &listeners_group->listeners ) <= 0 )
168 break;
170 /* Cache a copy of the listener to avoid locking issues,
171 * and allow that edition of listeners during callbacks will garantee immediate effect. */
172 i_cached_listeners = vlc_array_count(&listeners_group->listeners);
173 array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
174 if( !array_listeners_cached )
176 vlc_mutex_unlock( &p_em->object_lock );
177 vlc_mutex_unlock( &p_em->event_sending_lock );
178 fprintf(stderr, "Can't alloc memory in libvlc_event_send" );
179 return;
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) );
187 listener_cached++;
189 break;
193 if( !listeners_group )
195 free( array_listeners_cached );
196 vlc_mutex_unlock( &p_em->object_lock );
197 vlc_mutex_unlock( &p_em->event_sending_lock );
198 return;
201 /* Track item removed from *this* thread, with a simple flag. Indeed
202 * event_sending_lock is a recursive lock. This has the advantage of
203 * allowing to remove an event listener from within a callback */
204 listeners_group->b_sublistener_removed = false;
206 vlc_mutex_unlock( &p_em->object_lock );
208 listener_cached = array_listeners_cached;
209 for( i = 0; i < i_cached_listeners; i++ )
211 if(listener_cached->is_asynchronous)
213 /* The listener wants not to block the emitter during event callback */
214 libvlc_event_async_dispatch(p_em, listener_cached, p_event);
216 else
218 /* The listener wants to block the emitter during event callback */
220 listener_cached->pf_callback( p_event, listener_cached->p_user_data );
221 listener_cached++;
223 if( listeners_group->b_sublistener_removed )
225 /* If a callback was removed, this gets called */
226 bool valid_listener;
227 vlc_mutex_lock( &p_em->object_lock );
228 valid_listener = group_contains_listener( listeners_group, listener_cached );
229 vlc_mutex_unlock( &p_em->object_lock );
230 if( !valid_listener )
232 listener_cached++;
233 continue;
238 vlc_mutex_unlock( &p_em->event_sending_lock );
240 free( array_listeners_cached );
244 * Public libvlc functions
247 #define DEF( a ) { libvlc_##a, #a, },
249 typedef struct
251 int type;
252 const char name[40];
253 } event_name_t;
255 static const event_name_t event_list[] = {
256 DEF(MediaMetaChanged)
257 DEF(MediaSubItemAdded)
258 DEF(MediaDurationChanged)
259 DEF(MediaParsedChanged)
260 DEF(MediaFreed)
261 DEF(MediaStateChanged)
262 DEF(MediaSubItemTreeAdded)
264 DEF(MediaPlayerMediaChanged)
265 DEF(MediaPlayerNothingSpecial)
266 DEF(MediaPlayerOpening)
267 DEF(MediaPlayerBuffering)
268 DEF(MediaPlayerPlaying)
269 DEF(MediaPlayerPaused)
270 DEF(MediaPlayerStopped)
271 DEF(MediaPlayerForward)
272 DEF(MediaPlayerBackward)
273 DEF(MediaPlayerEndReached)
274 DEF(MediaPlayerEncounteredError)
275 DEF(MediaPlayerTimeChanged)
276 DEF(MediaPlayerPositionChanged)
277 DEF(MediaPlayerSeekableChanged)
278 DEF(MediaPlayerPausableChanged)
279 DEF(MediaPlayerTitleChanged)
280 DEF(MediaPlayerSnapshotTaken)
281 DEF(MediaPlayerLengthChanged)
282 DEF(MediaPlayerVout)
283 DEF(MediaPlayerScrambledChanged)
284 DEF(MediaPlayerESAdded)
285 DEF(MediaPlayerESDeleted)
286 DEF(MediaPlayerESSelected)
288 DEF(MediaListItemAdded)
289 DEF(MediaListWillAddItem)
290 DEF(MediaListItemDeleted)
291 DEF(MediaListWillDeleteItem)
293 DEF(MediaListViewItemAdded)
294 DEF(MediaListViewWillAddItem)
295 DEF(MediaListViewItemDeleted)
296 DEF(MediaListViewWillDeleteItem)
298 DEF(MediaListPlayerPlayed)
299 DEF(MediaListPlayerNextItemSet)
300 DEF(MediaListPlayerStopped)
302 DEF(MediaDiscovererStarted)
303 DEF(MediaDiscovererEnded)
305 DEF(VlmMediaAdded)
306 DEF(VlmMediaRemoved)
307 DEF(VlmMediaChanged)
308 DEF(VlmMediaInstanceStarted)
309 DEF(VlmMediaInstanceStopped)
310 DEF(VlmMediaInstanceStatusInit)
311 DEF(VlmMediaInstanceStatusOpening)
312 DEF(VlmMediaInstanceStatusPlaying)
313 DEF(VlmMediaInstanceStatusPause)
314 DEF(VlmMediaInstanceStatusEnd)
315 DEF(VlmMediaInstanceStatusError)
317 #undef DEF
319 static const char unknown_event_name[] = "Unknown Event";
321 static int evcmp( const void *a, const void *b )
323 return (*(const int *)a) - ((event_name_t *)b)->type;
326 const char * libvlc_event_type_name( int event_type )
328 const event_name_t *p;
330 p = bsearch( &event_type, event_list,
331 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
332 evcmp );
333 return p ? p->name : unknown_event_name;
336 /**************************************************************************
337 * event_attach (internal) :
339 * Add a callback for an event.
340 **************************************************************************/
341 static
342 int event_attach( libvlc_event_manager_t * p_event_manager,
343 libvlc_event_type_t event_type,
344 libvlc_callback_t pf_callback, void *p_user_data,
345 bool is_asynchronous )
347 libvlc_event_listeners_group_t * listeners_group;
348 libvlc_event_listener_t * listener;
349 int i;
351 listener = malloc(sizeof(libvlc_event_listener_t));
352 if( unlikely(listener == NULL) )
353 return ENOMEM;
355 listener->event_type = event_type;
356 listener->p_user_data = p_user_data;
357 listener->pf_callback = pf_callback;
358 listener->is_asynchronous = is_asynchronous;
360 vlc_mutex_lock( &p_event_manager->object_lock );
361 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
363 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
364 if( listeners_group->event_type == listener->event_type )
366 vlc_array_append( &listeners_group->listeners, listener );
367 vlc_mutex_unlock( &p_event_manager->object_lock );
368 return 0;
371 vlc_mutex_unlock( &p_event_manager->object_lock );
373 free(listener);
374 fprintf( stderr, "This object event manager doesn't know about '%s' events",
375 libvlc_event_type_name(event_type) );
376 assert(0);
377 return -1;
380 /**************************************************************************
381 * libvlc_event_attach (public) :
383 * Add a callback for an event.
384 **************************************************************************/
385 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
386 libvlc_event_type_t event_type,
387 libvlc_callback_t pf_callback,
388 void *p_user_data )
390 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
391 false /* synchronous */);
394 /**************************************************************************
395 * libvlc_event_attach (public) :
397 * Add a callback for an event.
398 **************************************************************************/
399 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
400 libvlc_event_type_t event_type,
401 libvlc_callback_t pf_callback,
402 void *p_user_data )
404 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
405 true /* asynchronous */);
408 /**************************************************************************
409 * libvlc_event_detach (public) :
411 * Remove a callback for an event.
412 **************************************************************************/
413 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
414 libvlc_event_type_t event_type,
415 libvlc_callback_t pf_callback,
416 void *p_user_data )
418 libvlc_event_listeners_group_t * listeners_group;
419 libvlc_event_listener_t * listener;
420 int i, j;
421 bool found = false;
423 vlc_mutex_lock( &p_event_manager->event_sending_lock );
424 vlc_mutex_lock( &p_event_manager->object_lock );
425 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
427 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
428 if( listeners_group->event_type == event_type )
430 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
432 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
433 if( listener->event_type == event_type &&
434 listener->pf_callback == pf_callback &&
435 listener->p_user_data == p_user_data )
437 /* that's our listener */
439 /* Mark this group as edited so that libvlc_event_send
440 * will recheck what listener to call */
441 listeners_group->b_sublistener_removed = true;
443 free( listener );
444 vlc_array_remove( &listeners_group->listeners, j );
445 found = true;
446 break;
451 vlc_mutex_unlock( &p_event_manager->object_lock );
452 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
454 /* Now make sure any pending async event won't get fired after that point */
455 libvlc_event_listener_t listener_to_remove;
456 listener_to_remove.event_type = event_type;
457 listener_to_remove.pf_callback = pf_callback;
458 listener_to_remove.p_user_data = p_user_data;
459 listener_to_remove.is_asynchronous = true;
461 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
463 assert(found);