os2: filesystem: implement vlc_write() and vlc_writev()
[vlc.git] / lib / event.c
blob4a95ceee6c2673fb910db13aa33d6b35b609f663
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->async_event_queue = NULL;
84 p_em->p_libvlc_instance = p_libvlc_inst;
86 libvlc_retain( p_libvlc_inst );
87 vlc_array_init( &p_em->listeners_groups );
88 vlc_mutex_init( &p_em->object_lock );
89 vlc_mutex_init_recursive( &p_em->event_sending_lock );
90 return p_em;
93 /**************************************************************************
94 * libvlc_event_manager_release (internal) :
96 * Release an object's event manager.
97 **************************************************************************/
98 void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
100 libvlc_event_listeners_group_t * p_lg;
101 int i,j ;
103 libvlc_event_async_fini(p_em);
105 vlc_mutex_destroy( &p_em->event_sending_lock );
106 vlc_mutex_destroy( &p_em->object_lock );
108 for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
110 p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );
112 for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
113 free( vlc_array_item_at_index( &p_lg->listeners, j ) );
115 vlc_array_clear( &p_lg->listeners );
116 free( p_lg );
118 vlc_array_clear( &p_em->listeners_groups );
119 libvlc_release( p_em->p_libvlc_instance );
120 free( p_em );
123 /**************************************************************************
124 * libvlc_event_manager_register_event_type (internal) :
126 * Init an object's event manager.
127 **************************************************************************/
128 void libvlc_event_manager_register_event_type(
129 libvlc_event_manager_t * p_em,
130 libvlc_event_type_t event_type )
132 libvlc_event_listeners_group_t * listeners_group;
133 listeners_group = xmalloc(sizeof(libvlc_event_listeners_group_t));
134 listeners_group->event_type = event_type;
135 vlc_array_init( &listeners_group->listeners );
137 vlc_mutex_lock( &p_em->object_lock );
138 vlc_array_append( &p_em->listeners_groups, listeners_group );
139 vlc_mutex_unlock( &p_em->object_lock );
142 /**************************************************************************
143 * libvlc_event_send (internal) :
145 * Send a callback.
146 **************************************************************************/
147 void libvlc_event_send( libvlc_event_manager_t * p_em,
148 libvlc_event_t * p_event )
150 libvlc_event_listeners_group_t * listeners_group = NULL;
151 libvlc_event_listener_t * listener_cached;
152 libvlc_event_listener_t * listener;
153 libvlc_event_listener_t * array_listeners_cached = NULL;
154 int i, i_cached_listeners = 0;
156 /* Fill event with the sending object now */
157 p_event->p_obj = p_em->p_obj;
159 vlc_mutex_lock( &p_em->event_sending_lock );
160 vlc_mutex_lock( &p_em->object_lock );
161 for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
163 listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
164 if( listeners_group->event_type == p_event->type )
166 if( vlc_array_count( &listeners_group->listeners ) <= 0 )
167 break;
169 /* Cache a copy of the listener to avoid locking issues,
170 * and allow that edition of listeners during callbacks will garantee immediate effect. */
171 i_cached_listeners = vlc_array_count(&listeners_group->listeners);
172 array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
173 if( !array_listeners_cached )
175 vlc_mutex_unlock( &p_em->object_lock );
176 vlc_mutex_unlock( &p_em->event_sending_lock );
177 fprintf(stderr, "Can't alloc memory in libvlc_event_send" );
178 return;
181 listener_cached = array_listeners_cached;
182 for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
184 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
185 memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
186 listener_cached++;
188 break;
192 if( !listeners_group )
194 free( array_listeners_cached );
195 vlc_mutex_unlock( &p_em->object_lock );
196 vlc_mutex_unlock( &p_em->event_sending_lock );
197 return;
200 /* Track item removed from *this* thread, with a simple flag. Indeed
201 * event_sending_lock is a recursive lock. This has the advantage of
202 * allowing to remove an event listener from within a callback */
203 listeners_group->b_sublistener_removed = false;
205 vlc_mutex_unlock( &p_em->object_lock );
207 listener_cached = array_listeners_cached;
208 for( i = 0; i < i_cached_listeners; i++ )
210 if(listener_cached->is_asynchronous)
212 /* The listener wants not to block the emitter during event callback */
213 libvlc_event_async_dispatch(p_em, listener_cached, p_event);
215 else
217 /* The listener wants to block the emitter during event callback */
219 listener_cached->pf_callback( p_event, listener_cached->p_user_data );
220 listener_cached++;
222 if( listeners_group->b_sublistener_removed )
224 /* If a callback was removed, this gets called */
225 bool valid_listener;
226 vlc_mutex_lock( &p_em->object_lock );
227 valid_listener = group_contains_listener( listeners_group, listener_cached );
228 vlc_mutex_unlock( &p_em->object_lock );
229 if( !valid_listener )
231 listener_cached++;
232 continue;
237 vlc_mutex_unlock( &p_em->event_sending_lock );
239 free( array_listeners_cached );
243 * Public libvlc functions
246 #define DEF( a ) { libvlc_##a, #a, },
248 typedef struct
250 int type;
251 const char name[40];
252 } event_name_t;
254 static const event_name_t event_list[] = {
255 DEF(MediaMetaChanged)
256 DEF(MediaSubItemAdded)
257 DEF(MediaDurationChanged)
258 DEF(MediaParsedChanged)
259 DEF(MediaFreed)
260 DEF(MediaStateChanged)
261 DEF(MediaSubItemTreeAdded)
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)
282 DEF(MediaPlayerScrambledChanged)
283 DEF(MediaPlayerESAdded)
284 DEF(MediaPlayerESDeleted)
285 DEF(MediaPlayerESSelected)
287 DEF(MediaListItemAdded)
288 DEF(MediaListWillAddItem)
289 DEF(MediaListItemDeleted)
290 DEF(MediaListWillDeleteItem)
292 DEF(MediaListViewItemAdded)
293 DEF(MediaListViewWillAddItem)
294 DEF(MediaListViewItemDeleted)
295 DEF(MediaListViewWillDeleteItem)
297 DEF(MediaListPlayerPlayed)
298 DEF(MediaListPlayerNextItemSet)
299 DEF(MediaListPlayerStopped)
301 DEF(MediaDiscovererStarted)
302 DEF(MediaDiscovererEnded)
304 DEF(VlmMediaAdded)
305 DEF(VlmMediaRemoved)
306 DEF(VlmMediaChanged)
307 DEF(VlmMediaInstanceStarted)
308 DEF(VlmMediaInstanceStopped)
309 DEF(VlmMediaInstanceStatusInit)
310 DEF(VlmMediaInstanceStatusOpening)
311 DEF(VlmMediaInstanceStatusPlaying)
312 DEF(VlmMediaInstanceStatusPause)
313 DEF(VlmMediaInstanceStatusEnd)
314 DEF(VlmMediaInstanceStatusError)
316 #undef DEF
318 static const char unknown_event_name[] = "Unknown Event";
320 static int evcmp( const void *a, const void *b )
322 return (*(const int *)a) - ((event_name_t *)b)->type;
325 const char * libvlc_event_type_name( int event_type )
327 const event_name_t *p;
329 p = bsearch( &event_type, event_list,
330 sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
331 evcmp );
332 return p ? p->name : unknown_event_name;
335 /**************************************************************************
336 * event_attach (internal) :
338 * Add a callback for an event.
339 **************************************************************************/
340 static
341 int event_attach( libvlc_event_manager_t * p_event_manager,
342 libvlc_event_type_t event_type,
343 libvlc_callback_t pf_callback, void *p_user_data,
344 bool is_asynchronous )
346 libvlc_event_listeners_group_t * listeners_group;
347 libvlc_event_listener_t * listener;
348 int i;
350 listener = malloc(sizeof(libvlc_event_listener_t));
351 if( unlikely(listener == NULL) )
352 return ENOMEM;
354 listener->event_type = event_type;
355 listener->p_user_data = p_user_data;
356 listener->pf_callback = pf_callback;
357 listener->is_asynchronous = is_asynchronous;
359 vlc_mutex_lock( &p_event_manager->object_lock );
360 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
362 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
363 if( listeners_group->event_type == listener->event_type )
365 vlc_array_append( &listeners_group->listeners, listener );
366 vlc_mutex_unlock( &p_event_manager->object_lock );
367 return 0;
370 vlc_mutex_unlock( &p_event_manager->object_lock );
372 free(listener);
373 fprintf( stderr, "This object event manager doesn't know about '%s' events",
374 libvlc_event_type_name(event_type) );
375 vlc_assert_unreachable();
376 return -1;
379 /**************************************************************************
380 * libvlc_event_attach (public) :
382 * Add a callback for an event.
383 **************************************************************************/
384 int libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
385 libvlc_event_type_t event_type,
386 libvlc_callback_t pf_callback,
387 void *p_user_data )
389 return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
390 false /* synchronous */);
393 /**************************************************************************
394 * libvlc_event_attach (public) :
396 * Add a callback for an event.
397 **************************************************************************/
398 void libvlc_event_attach_async( libvlc_event_manager_t * p_event_manager,
399 libvlc_event_type_t event_type,
400 libvlc_callback_t pf_callback,
401 void *p_user_data )
403 event_attach(p_event_manager, event_type, pf_callback, p_user_data,
404 true /* asynchronous */);
407 /**************************************************************************
408 * libvlc_event_detach (public) :
410 * Remove a callback for an event.
411 **************************************************************************/
412 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
413 libvlc_event_type_t event_type,
414 libvlc_callback_t pf_callback,
415 void *p_user_data )
417 libvlc_event_listeners_group_t * listeners_group;
418 libvlc_event_listener_t * listener;
419 int i, j;
420 bool found = false;
422 vlc_mutex_lock( &p_event_manager->event_sending_lock );
423 vlc_mutex_lock( &p_event_manager->object_lock );
424 for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
426 listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
427 if( listeners_group->event_type == event_type )
429 for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
431 listener = vlc_array_item_at_index(&listeners_group->listeners, j);
432 if( listener->event_type == event_type &&
433 listener->pf_callback == pf_callback &&
434 listener->p_user_data == p_user_data )
436 /* that's our listener */
438 /* Mark this group as edited so that libvlc_event_send
439 * will recheck what listener to call */
440 listeners_group->b_sublistener_removed = true;
442 free( listener );
443 vlc_array_remove( &listeners_group->listeners, j );
444 found = true;
445 break;
450 vlc_mutex_unlock( &p_event_manager->object_lock );
451 vlc_mutex_unlock( &p_event_manager->event_sending_lock );
453 /* Now make sure any pending async event won't get fired after that point */
454 libvlc_event_listener_t listener_to_remove;
455 listener_to_remove.event_type = event_type;
456 listener_to_remove.pf_callback = pf_callback;
457 listener_to_remove.p_user_data = p_user_data;
458 listener_to_remove.is_asynchronous = true;
460 libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
462 assert(found);