Update NEWS with new supported codecs.
[vlc/solaris.git] / lib / event.c
blobf72276bee8506b25597d852cc44a3ee2743cc117
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)
282 DEF(MediaListItemAdded)
283 DEF(MediaListWillAddItem)
284 DEF(MediaListItemDeleted)
285 DEF(MediaListWillDeleteItem)
287 DEF(MediaListViewItemAdded)
288 DEF(MediaListViewWillAddItem)
289 DEF(MediaListViewItemDeleted)
290 DEF(MediaListViewWillDeleteItem)
292 DEF(MediaListPlayerPlayed)
293 DEF(MediaListPlayerNextItemSet)
294 DEF(MediaListPlayerStopped)
296 DEF(MediaDiscovererStarted)
297 DEF(MediaDiscovererEnded)
299 DEF(VlmMediaAdded)
300 DEF(VlmMediaRemoved)
301 DEF(VlmMediaChanged)
302 DEF(VlmMediaInstanceStarted)
303 DEF(VlmMediaInstanceStopped)
304 DEF(VlmMediaInstanceStatusInit)
305 DEF(VlmMediaInstanceStatusOpening)
307 #undef DEF
309 static const char unknown_event_name[] = "Unknown Event";
311 static int evcmp( const void *a, const void *b )
313 return (*(const int *)a) - ((event_name_t *)b)->type;
316 const char * libvlc_event_type_name( int event_type )
318 const event_name_t *p;
320 p = bsearch( &event_type, event_list,
321 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
322 evcmp );
323 return p ? p->name : unknown_event_name;
326 /**************************************************************************
327 * event_attach (internal) :
329 * Add a callback for an event.
330 **************************************************************************/
331 static
332 int event_attach( libvlc_event_manager_t * p_event_manager,
333 libvlc_event_type_t event_type,
334 libvlc_callback_t pf_callback, void *p_user_data,
335 bool is_asynchronous )
337 libvlc_event_listeners_group_t * listeners_group;
338 libvlc_event_listener_t * listener;
339 int i;
341 listener = malloc(sizeof(libvlc_event_listener_t));
342 if( unlikely(listener == NULL) )
343 return ENOMEM;
345 listener->event_type = event_type;
346 listener->p_user_data = p_user_data;
347 listener->pf_callback = pf_callback;
348 listener->is_asynchronous = is_asynchronous;
350 vlc_mutex_lock( &p_event_manager->object_lock );
351 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
353 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
354 if( listeners_group->event_type == listener->event_type )
356 vlc_array_append( &listeners_group->listeners, listener );
357 vlc_mutex_unlock( &p_event_manager->object_lock );
358 return 0;
361 vlc_mutex_unlock( &p_event_manager->object_lock );
363 free(listener);
364 fprintf( stderr, "This object event manager doesn't know about '%s' events",
365 libvlc_event_type_name(event_type) );
366 assert(0);
367 return -1;
370 /**************************************************************************
371 * libvlc_event_attach (public) :
373 * Add a callback for an event.
374 **************************************************************************/
375 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
376 libvlc_event_type_t event_type,
377 libvlc_callback_t pf_callback,
378 void *p_user_data )
380 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
381 false /* synchronous */);
384 /**************************************************************************
385 * libvlc_event_attach (public) :
387 * Add a callback for an event.
388 **************************************************************************/
389 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
390 libvlc_event_type_t event_type,
391 libvlc_callback_t pf_callback,
392 void *p_user_data )
394 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
395 true /* asynchronous */);
398 /**************************************************************************
399 * libvlc_event_detach (public) :
401 * Remove a callback for an event.
402 **************************************************************************/
403 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
404 libvlc_event_type_t event_type,
405 libvlc_callback_t pf_callback,
406 void *p_user_data )
408 libvlc_event_listeners_group_t * listeners_group;
409 libvlc_event_listener_t * listener;
410 int i, j;
411 bool found = false;
413 vlc_mutex_lock( &p_event_manager->event_sending_lock );
414 vlc_mutex_lock( &p_event_manager->object_lock );
415 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
417 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
418 if( listeners_group->event_type == event_type )
420 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
422 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
423 if( listener->event_type == event_type &&
424 listener->pf_callback == pf_callback &&
425 listener->p_user_data == p_user_data )
427 /* that's our listener */
429 /* Mark this group as edited so that libvlc_event_send
430 * will recheck what listener to call */
431 listeners_group->b_sublistener_removed = true;
433 free( listener );
434 vlc_array_remove( &listeners_group->listeners, j );
435 found = true;
436 break;
441 vlc_mutex_unlock( &p_event_manager->object_lock );
442 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
444 /* Now make sure any pending async event won't get fired after that point */
445 libvlc_event_listener_t listener_to_remove;
446 listener_to_remove.event_type = event_type;
447 listener_to_remove.pf_callback = pf_callback;
448 listener_to_remove.p_user_data = p_user_data;
449 listener_to_remove.is_asynchronous = true;
451 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
453 assert(found);