Change the gmp download URL to https://gmplib.org/download
[vlc/gmpfix.git] / lib / event.c
blobaa285f93faf45cae99ab8cd416fbd6ed2b196d8f
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)
285 DEF(MediaListItemAdded)
286 DEF(MediaListWillAddItem)
287 DEF(MediaListItemDeleted)
288 DEF(MediaListWillDeleteItem)
290 DEF(MediaListViewItemAdded)
291 DEF(MediaListViewWillAddItem)
292 DEF(MediaListViewItemDeleted)
293 DEF(MediaListViewWillDeleteItem)
295 DEF(MediaListPlayerPlayed)
296 DEF(MediaListPlayerNextItemSet)
297 DEF(MediaListPlayerStopped)
299 DEF(MediaDiscovererStarted)
300 DEF(MediaDiscovererEnded)
302 DEF(VlmMediaAdded)
303 DEF(VlmMediaRemoved)
304 DEF(VlmMediaChanged)
305 DEF(VlmMediaInstanceStarted)
306 DEF(VlmMediaInstanceStopped)
307 DEF(VlmMediaInstanceStatusInit)
308 DEF(VlmMediaInstanceStatusOpening)
309 DEF(VlmMediaInstanceStatusPlaying)
310 DEF(VlmMediaInstanceStatusPause)
311 DEF(VlmMediaInstanceStatusEnd)
312 DEF(VlmMediaInstanceStatusError)
314 #undef DEF
316 static const char unknown_event_name[] = "Unknown Event";
318 static int evcmp( const void *a, const void *b )
320 return (*(const int *)a) - ((event_name_t *)b)->type;
323 const char * libvlc_event_type_name( int event_type )
325 const event_name_t *p;
327 p = bsearch( &event_type, event_list,
328 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
329 evcmp );
330 return p ? p->name : unknown_event_name;
333 /**************************************************************************
334 * event_attach (internal) :
336 * Add a callback for an event.
337 **************************************************************************/
338 static
339 int event_attach( libvlc_event_manager_t * p_event_manager,
340 libvlc_event_type_t event_type,
341 libvlc_callback_t pf_callback, void *p_user_data,
342 bool is_asynchronous )
344 libvlc_event_listeners_group_t * listeners_group;
345 libvlc_event_listener_t * listener;
346 int i;
348 listener = malloc(sizeof(libvlc_event_listener_t));
349 if( unlikely(listener == NULL) )
350 return ENOMEM;
352 listener->event_type = event_type;
353 listener->p_user_data = p_user_data;
354 listener->pf_callback = pf_callback;
355 listener->is_asynchronous = is_asynchronous;
357 vlc_mutex_lock( &p_event_manager->object_lock );
358 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
360 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
361 if( listeners_group->event_type == listener->event_type )
363 vlc_array_append( &listeners_group->listeners, listener );
364 vlc_mutex_unlock( &p_event_manager->object_lock );
365 return 0;
368 vlc_mutex_unlock( &p_event_manager->object_lock );
370 free(listener);
371 fprintf( stderr, "This object event manager doesn't know about '%s' events",
372 libvlc_event_type_name(event_type) );
373 assert(0);
374 return -1;
377 /**************************************************************************
378 * libvlc_event_attach (public) :
380 * Add a callback for an event.
381 **************************************************************************/
382 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
383 libvlc_event_type_t event_type,
384 libvlc_callback_t pf_callback,
385 void *p_user_data )
387 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
388 false /* synchronous */);
391 /**************************************************************************
392 * libvlc_event_attach (public) :
394 * Add a callback for an event.
395 **************************************************************************/
396 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
397 libvlc_event_type_t event_type,
398 libvlc_callback_t pf_callback,
399 void *p_user_data )
401 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
402 true /* asynchronous */);
405 /**************************************************************************
406 * libvlc_event_detach (public) :
408 * Remove a callback for an event.
409 **************************************************************************/
410 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
411 libvlc_event_type_t event_type,
412 libvlc_callback_t pf_callback,
413 void *p_user_data )
415 libvlc_event_listeners_group_t * listeners_group;
416 libvlc_event_listener_t * listener;
417 int i, j;
418 bool found = false;
420 vlc_mutex_lock( &p_event_manager->event_sending_lock );
421 vlc_mutex_lock( &p_event_manager->object_lock );
422 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
424 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
425 if( listeners_group->event_type == event_type )
427 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
429 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
430 if( listener->event_type == event_type &&
431 listener->pf_callback == pf_callback &&
432 listener->p_user_data == p_user_data )
434 /* that's our listener */
436 /* Mark this group as edited so that libvlc_event_send
437 * will recheck what listener to call */
438 listeners_group->b_sublistener_removed = true;
440 free( listener );
441 vlc_array_remove( &listeners_group->listeners, j );
442 found = true;
443 break;
448 vlc_mutex_unlock( &p_event_manager->object_lock );
449 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
451 /* Now make sure any pending async event won't get fired after that point */
452 libvlc_event_listener_t listener_to_remove;
453 listener_to_remove.event_type = event_type;
454 listener_to_remove.pf_callback = pf_callback;
455 listener_to_remove.p_user_data = p_user_data;
456 listener_to_remove.is_asynchronous = true;
458 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
460 assert(found);