MusicBrainz: use the MB release ID if exists
[vlc.git] / lib / event.c
blob87f7d125a294837505881f14b85b9410e7789fd1
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)
284 DEF(MediaListItemAdded)
285 DEF(MediaListWillAddItem)
286 DEF(MediaListItemDeleted)
287 DEF(MediaListWillDeleteItem)
289 DEF(MediaListViewItemAdded)
290 DEF(MediaListViewWillAddItem)
291 DEF(MediaListViewItemDeleted)
292 DEF(MediaListViewWillDeleteItem)
294 DEF(MediaListPlayerPlayed)
295 DEF(MediaListPlayerNextItemSet)
296 DEF(MediaListPlayerStopped)
298 DEF(MediaDiscovererStarted)
299 DEF(MediaDiscovererEnded)
301 DEF(VlmMediaAdded)
302 DEF(VlmMediaRemoved)
303 DEF(VlmMediaChanged)
304 DEF(VlmMediaInstanceStarted)
305 DEF(VlmMediaInstanceStopped)
306 DEF(VlmMediaInstanceStatusInit)
307 DEF(VlmMediaInstanceStatusOpening)
308 DEF(VlmMediaInstanceStatusPlaying)
309 DEF(VlmMediaInstanceStatusPause)
310 DEF(VlmMediaInstanceStatusEnd)
311 DEF(VlmMediaInstanceStatusError)
313 #undef DEF
315 static const char unknown_event_name[] = "Unknown Event";
317 static int evcmp( const void *a, const void *b )
319 return (*(const int *)a) - ((event_name_t *)b)->type;
322 const char * libvlc_event_type_name( int event_type )
324 const event_name_t *p;
326 p = bsearch( &event_type, event_list,
327 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
328 evcmp );
329 return p ? p->name : unknown_event_name;
332 /**************************************************************************
333 * event_attach (internal) :
335 * Add a callback for an event.
336 **************************************************************************/
337 static
338 int event_attach( libvlc_event_manager_t * p_event_manager,
339 libvlc_event_type_t event_type,
340 libvlc_callback_t pf_callback, void *p_user_data,
341 bool is_asynchronous )
343 libvlc_event_listeners_group_t * listeners_group;
344 libvlc_event_listener_t * listener;
345 int i;
347 listener = malloc(sizeof(libvlc_event_listener_t));
348 if( unlikely(listener == NULL) )
349 return ENOMEM;
351 listener->event_type = event_type;
352 listener->p_user_data = p_user_data;
353 listener->pf_callback = pf_callback;
354 listener->is_asynchronous = is_asynchronous;
356 vlc_mutex_lock( &p_event_manager->object_lock );
357 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
359 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
360 if( listeners_group->event_type == listener->event_type )
362 vlc_array_append( &listeners_group->listeners, listener );
363 vlc_mutex_unlock( &p_event_manager->object_lock );
364 return 0;
367 vlc_mutex_unlock( &p_event_manager->object_lock );
369 free(listener);
370 fprintf( stderr, "This object event manager doesn't know about '%s' events",
371 libvlc_event_type_name(event_type) );
372 assert(0);
373 return -1;
376 /**************************************************************************
377 * libvlc_event_attach (public) :
379 * Add a callback for an event.
380 **************************************************************************/
381 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
382 libvlc_event_type_t event_type,
383 libvlc_callback_t pf_callback,
384 void *p_user_data )
386 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
387 false /* synchronous */);
390 /**************************************************************************
391 * libvlc_event_attach (public) :
393 * Add a callback for an event.
394 **************************************************************************/
395 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
396 libvlc_event_type_t event_type,
397 libvlc_callback_t pf_callback,
398 void *p_user_data )
400 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
401 true /* asynchronous */);
404 /**************************************************************************
405 * libvlc_event_detach (public) :
407 * Remove a callback for an event.
408 **************************************************************************/
409 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
410 libvlc_event_type_t event_type,
411 libvlc_callback_t pf_callback,
412 void *p_user_data )
414 libvlc_event_listeners_group_t * listeners_group;
415 libvlc_event_listener_t * listener;
416 int i, j;
417 bool found = false;
419 vlc_mutex_lock( &p_event_manager->event_sending_lock );
420 vlc_mutex_lock( &p_event_manager->object_lock );
421 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
423 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
424 if( listeners_group->event_type == event_type )
426 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
428 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
429 if( listener->event_type == event_type &&
430 listener->pf_callback == pf_callback &&
431 listener->p_user_data == p_user_data )
433 /* that's our listener */
435 /* Mark this group as edited so that libvlc_event_send
436 * will recheck what listener to call */
437 listeners_group->b_sublistener_removed = true;
439 free( listener );
440 vlc_array_remove( &listeners_group->listeners, j );
441 found = true;
442 break;
447 vlc_mutex_unlock( &p_event_manager->object_lock );
448 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
450 /* Now make sure any pending async event won't get fired after that point */
451 libvlc_event_listener_t listener_to_remove;
452 listener_to_remove.event_type = event_type;
453 listener_to_remove.pf_callback = pf_callback;
454 listener_to_remove.p_user_data = p_user_data;
455 listener_to_remove.is_asynchronous = true;
457 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
459 assert(found);