Correct spelling: s/[sS]yncro/[sS]ynchro/
[vlc.git] / lib / event.c
blobc71a48ab98e3dbc0e69c14bbe19bc059d59c580f
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)
263 DEF(MediaPlayerMediaChanged)
264 DEF(MediaPlayerNothingSpecial)
265 DEF(MediaPlayerOpening)
266 DEF(MediaPlayerBuffering)
267 DEF(MediaPlayerPlaying)
268 DEF(MediaPlayerPaused)
269 DEF(MediaPlayerStopped)
270 DEF(MediaPlayerForward)
271 DEF(MediaPlayerBackward)
272 DEF(MediaPlayerEndReached)
273 DEF(MediaPlayerEncounteredError)
274 DEF(MediaPlayerTimeChanged)
275 DEF(MediaPlayerPositionChanged)
276 DEF(MediaPlayerSeekableChanged)
277 DEF(MediaPlayerPausableChanged)
278 DEF(MediaPlayerTitleChanged)
279 DEF(MediaPlayerSnapshotTaken)
280 DEF(MediaPlayerLengthChanged)
281 DEF(MediaPlayerVout)
283 DEF(MediaListItemAdded)
284 DEF(MediaListWillAddItem)
285 DEF(MediaListItemDeleted)
286 DEF(MediaListWillDeleteItem)
288 DEF(MediaListViewItemAdded)
289 DEF(MediaListViewWillAddItem)
290 DEF(MediaListViewItemDeleted)
291 DEF(MediaListViewWillDeleteItem)
293 DEF(MediaListPlayerPlayed)
294 DEF(MediaListPlayerNextItemSet)
295 DEF(MediaListPlayerStopped)
297 DEF(MediaDiscovererStarted)
298 DEF(MediaDiscovererEnded)
300 DEF(VlmMediaAdded)
301 DEF(VlmMediaRemoved)
302 DEF(VlmMediaChanged)
303 DEF(VlmMediaInstanceStarted)
304 DEF(VlmMediaInstanceStopped)
305 DEF(VlmMediaInstanceStatusInit)
306 DEF(VlmMediaInstanceStatusOpening)
307 DEF(VlmMediaInstanceStatusPlaying)
308 DEF(VlmMediaInstanceStatusPause)
309 DEF(VlmMediaInstanceStatusEnd)
310 DEF(VlmMediaInstanceStatusError)
312 #undef DEF
314 static const char unknown_event_name[] = "Unknown Event";
316 static int evcmp( const void *a, const void *b )
318 return (*(const int *)a) - ((event_name_t *)b)->type;
321 const char * libvlc_event_type_name( int event_type )
323 const event_name_t *p;
325 p = bsearch( &event_type, event_list,
326 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
327 evcmp );
328 return p ? p->name : unknown_event_name;
331 /**************************************************************************
332 * event_attach (internal) :
334 * Add a callback for an event.
335 **************************************************************************/
336 static
337 int event_attach( libvlc_event_manager_t * p_event_manager,
338 libvlc_event_type_t event_type,
339 libvlc_callback_t pf_callback, void *p_user_data,
340 bool is_asynchronous )
342 libvlc_event_listeners_group_t * listeners_group;
343 libvlc_event_listener_t * listener;
344 int i;
346 listener = malloc(sizeof(libvlc_event_listener_t));
347 if( unlikely(listener == NULL) )
348 return ENOMEM;
350 listener->event_type = event_type;
351 listener->p_user_data = p_user_data;
352 listener->pf_callback = pf_callback;
353 listener->is_asynchronous = is_asynchronous;
355 vlc_mutex_lock( &p_event_manager->object_lock );
356 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
358 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
359 if( listeners_group->event_type == listener->event_type )
361 vlc_array_append( &listeners_group->listeners, listener );
362 vlc_mutex_unlock( &p_event_manager->object_lock );
363 return 0;
366 vlc_mutex_unlock( &p_event_manager->object_lock );
368 free(listener);
369 fprintf( stderr, "This object event manager doesn't know about '%s' events",
370 libvlc_event_type_name(event_type) );
371 assert(0);
372 return -1;
375 /**************************************************************************
376 * libvlc_event_attach (public) :
378 * Add a callback for an event.
379 **************************************************************************/
380 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
381 libvlc_event_type_t event_type,
382 libvlc_callback_t pf_callback,
383 void *p_user_data )
385 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
386 false /* synchronous */);
389 /**************************************************************************
390 * libvlc_event_attach (public) :
392 * Add a callback for an event.
393 **************************************************************************/
394 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
395 libvlc_event_type_t event_type,
396 libvlc_callback_t pf_callback,
397 void *p_user_data )
399 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
400 true /* asynchronous */);
403 /**************************************************************************
404 * libvlc_event_detach (public) :
406 * Remove a callback for an event.
407 **************************************************************************/
408 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
409 libvlc_event_type_t event_type,
410 libvlc_callback_t pf_callback,
411 void *p_user_data )
413 libvlc_event_listeners_group_t * listeners_group;
414 libvlc_event_listener_t * listener;
415 int i, j;
416 bool found = false;
418 vlc_mutex_lock( &p_event_manager->event_sending_lock );
419 vlc_mutex_lock( &p_event_manager->object_lock );
420 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
422 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
423 if( listeners_group->event_type == event_type )
425 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
427 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
428 if( listener->event_type == event_type &&
429 listener->pf_callback == pf_callback &&
430 listener->p_user_data == p_user_data )
432 /* that's our listener */
434 /* Mark this group as edited so that libvlc_event_send
435 * will recheck what listener to call */
436 listeners_group->b_sublistener_removed = true;
438 free( listener );
439 vlc_array_remove( &listeners_group->listeners, j );
440 found = true;
441 break;
446 vlc_mutex_unlock( &p_event_manager->object_lock );
447 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
449 /* Now make sure any pending async event won't get fired after that point */
450 libvlc_event_listener_t listener_to_remove;
451 listener_to_remove.event_type = event_type;
452 listener_to_remove.pf_callback = pf_callback;
453 listener_to_remove.p_user_data = p_user_data;
454 listener_to_remove.is_asynchronous = true;
456 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
458 assert(found);