1 /*****************************************************************************
2 * event.c: New libvlc event control API
3 *****************************************************************************
4 * Copyright (C) 2007-2010 VLC authors and VideoLAN
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 *****************************************************************************/
29 #define LIBVLC_EVENT_TYPES_KEEP_DEFINE
30 #include <vlc/libvlc.h>
32 #include "libvlc_internal.h"
33 #include "event_internal.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
;
49 group_contains_listener( libvlc_event_listeners_group_t
* group
,
50 libvlc_event_listener_t
* searched_listener
)
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
)) )
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
));
78 libvlc_printerr( "Not enough memory" );
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
);
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
;
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
);
118 vlc_array_clear( &p_em
->listeners_groups
);
119 libvlc_release( p_em
->p_libvlc_instance
);
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) :
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 )
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" );
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
) );
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
);
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
);
217 /* The listener wants to block the emitter during event callback */
219 listener_cached
->pf_callback( p_event
, listener_cached
->p_user_data
);
222 if( listeners_group
->b_sublistener_removed
)
224 /* If a callback was removed, this gets called */
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
)
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, },
254 static const event_name_t event_list
[] = {
255 DEF(MediaMetaChanged
)
256 DEF(MediaSubItemAdded
)
257 DEF(MediaDurationChanged
)
258 DEF(MediaParsedChanged
)
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
)
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
)
307 DEF(VlmMediaInstanceStarted
)
308 DEF(VlmMediaInstanceStopped
)
309 DEF(VlmMediaInstanceStatusInit
)
310 DEF(VlmMediaInstanceStatusOpening
)
311 DEF(VlmMediaInstanceStatusPlaying
)
312 DEF(VlmMediaInstanceStatusPause
)
313 DEF(VlmMediaInstanceStatusEnd
)
314 DEF(VlmMediaInstanceStatusError
)
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
),
332 return p
? p
->name
: unknown_event_name
;
335 /**************************************************************************
336 * event_attach (internal) :
338 * Add a callback for an event.
339 **************************************************************************/
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
;
350 listener
= malloc(sizeof(libvlc_event_listener_t
));
351 if( unlikely(listener
== NULL
) )
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
);
370 vlc_mutex_unlock( &p_event_manager
->object_lock
);
373 fprintf( stderr
, "This object event manager doesn't know about '%s' events",
374 libvlc_event_type_name(event_type
) );
375 vlc_assert_unreachable();
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
,
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
,
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
,
417 libvlc_event_listeners_group_t
* listeners_group
;
418 libvlc_event_listener_t
* listener
;
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;
443 vlc_array_remove( &listeners_group
->listeners
, j
);
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
);