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" );
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
);
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
;
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
);
119 vlc_array_clear( &p_em
->listeners_groups
);
120 libvlc_release( p_em
->p_libvlc_instance
);
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) :
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 )
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" );
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
) );
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
);
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
);
218 /* The listener wants to block the emitter during event callback */
220 listener_cached
->pf_callback( p_event
, listener_cached
->p_user_data
);
223 if( listeners_group
->b_sublistener_removed
)
225 /* If a callback was removed, this gets called */
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
)
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, },
255 static const event_name_t event_list
[] = {
256 DEF(MediaMetaChanged
)
257 DEF(MediaSubItemAdded
)
258 DEF(MediaDurationChanged
)
259 DEF(MediaParsedChanged
)
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
)
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
)
305 DEF(VlmMediaInstanceStarted
)
306 DEF(VlmMediaInstanceStopped
)
307 DEF(VlmMediaInstanceStatusInit
)
308 DEF(VlmMediaInstanceStatusOpening
)
309 DEF(VlmMediaInstanceStatusPlaying
)
310 DEF(VlmMediaInstanceStatusPause
)
311 DEF(VlmMediaInstanceStatusEnd
)
312 DEF(VlmMediaInstanceStatusError
)
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
),
330 return p
? p
->name
: unknown_event_name
;
333 /**************************************************************************
334 * event_attach (internal) :
336 * Add a callback for an event.
337 **************************************************************************/
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
;
348 listener
= malloc(sizeof(libvlc_event_listener_t
));
349 if( unlikely(listener
== NULL
) )
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
);
368 vlc_mutex_unlock( &p_event_manager
->object_lock
);
371 fprintf( stderr
, "This object event manager doesn't know about '%s' events",
372 libvlc_event_type_name(event_type
) );
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
,
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
,
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
,
415 libvlc_event_listeners_group_t
* listeners_group
;
416 libvlc_event_listener_t
* listener
;
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;
441 vlc_array_remove( &listeners_group
->listeners
, j
);
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
);