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
)
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
)
283 DEF(MediaListItemAdded
)
284 DEF(MediaListWillAddItem
)
285 DEF(MediaListItemDeleted
)
286 DEF(MediaListWillDeleteItem
)
288 DEF(MediaListViewItemAdded
)
289 DEF(MediaListViewWillAddItem
)
290 DEF(MediaListViewItemDeleted
)
291 DEF(MediaListViewWillDeleteItem
)
293 DEF(MediaListPlayerPlayed
)
294 DEF(MediaListPlayerNextItemSet
)
295 DEF(MediaListPlayerStopped
)
297 DEF(MediaDiscovererStarted
)
298 DEF(MediaDiscovererEnded
)
303 DEF(VlmMediaInstanceStarted
)
304 DEF(VlmMediaInstanceStopped
)
305 DEF(VlmMediaInstanceStatusInit
)
306 DEF(VlmMediaInstanceStatusOpening
)
307 DEF(VlmMediaInstanceStatusPlaying
)
308 DEF(VlmMediaInstanceStatusPause
)
309 DEF(VlmMediaInstanceStatusEnd
)
310 DEF(VlmMediaInstanceStatusError
)
314 static const char unknown_event_name
[] = "Unknown Event";
316 static int evcmp( const void *a
, const void *b
)
318 return (*(const int *)a
) - ((event_name_t
*)b
)->type
;
321 const char * libvlc_event_type_name( int event_type
)
323 const event_name_t
*p
;
325 p
= bsearch( &event_type
, event_list
,
326 sizeof(event_list
)/sizeof(event_list
[0]), sizeof(*p
),
328 return p
? p
->name
: unknown_event_name
;
331 /**************************************************************************
332 * event_attach (internal) :
334 * Add a callback for an event.
335 **************************************************************************/
337 int event_attach( libvlc_event_manager_t
* p_event_manager
,
338 libvlc_event_type_t event_type
,
339 libvlc_callback_t pf_callback
, void *p_user_data
,
340 bool is_asynchronous
)
342 libvlc_event_listeners_group_t
* listeners_group
;
343 libvlc_event_listener_t
* listener
;
346 listener
= malloc(sizeof(libvlc_event_listener_t
));
347 if( unlikely(listener
== NULL
) )
350 listener
->event_type
= event_type
;
351 listener
->p_user_data
= p_user_data
;
352 listener
->pf_callback
= pf_callback
;
353 listener
->is_asynchronous
= is_asynchronous
;
355 vlc_mutex_lock( &p_event_manager
->object_lock
);
356 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++ )
358 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
359 if( listeners_group
->event_type
== listener
->event_type
)
361 vlc_array_append( &listeners_group
->listeners
, listener
);
362 vlc_mutex_unlock( &p_event_manager
->object_lock
);
366 vlc_mutex_unlock( &p_event_manager
->object_lock
);
369 fprintf( stderr
, "This object event manager doesn't know about '%s' events",
370 libvlc_event_type_name(event_type
) );
375 /**************************************************************************
376 * libvlc_event_attach (public) :
378 * Add a callback for an event.
379 **************************************************************************/
380 int libvlc_event_attach( libvlc_event_manager_t
* p_event_manager
,
381 libvlc_event_type_t event_type
,
382 libvlc_callback_t pf_callback
,
385 return event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
386 false /* synchronous */);
389 /**************************************************************************
390 * libvlc_event_attach (public) :
392 * Add a callback for an event.
393 **************************************************************************/
394 void libvlc_event_attach_async( libvlc_event_manager_t
* p_event_manager
,
395 libvlc_event_type_t event_type
,
396 libvlc_callback_t pf_callback
,
399 event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
400 true /* asynchronous */);
403 /**************************************************************************
404 * libvlc_event_detach (public) :
406 * Remove a callback for an event.
407 **************************************************************************/
408 void libvlc_event_detach( libvlc_event_manager_t
*p_event_manager
,
409 libvlc_event_type_t event_type
,
410 libvlc_callback_t pf_callback
,
413 libvlc_event_listeners_group_t
* listeners_group
;
414 libvlc_event_listener_t
* listener
;
418 vlc_mutex_lock( &p_event_manager
->event_sending_lock
);
419 vlc_mutex_lock( &p_event_manager
->object_lock
);
420 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++)
422 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
423 if( listeners_group
->event_type
== event_type
)
425 for( j
= 0; j
< vlc_array_count(&listeners_group
->listeners
); j
++)
427 listener
= vlc_array_item_at_index(&listeners_group
->listeners
, j
);
428 if( listener
->event_type
== event_type
&&
429 listener
->pf_callback
== pf_callback
&&
430 listener
->p_user_data
== p_user_data
)
432 /* that's our listener */
434 /* Mark this group as edited so that libvlc_event_send
435 * will recheck what listener to call */
436 listeners_group
->b_sublistener_removed
= true;
439 vlc_array_remove( &listeners_group
->listeners
, j
);
446 vlc_mutex_unlock( &p_event_manager
->object_lock
);
447 vlc_mutex_unlock( &p_event_manager
->event_sending_lock
);
449 /* Now make sure any pending async event won't get fired after that point */
450 libvlc_event_listener_t listener_to_remove
;
451 listener_to_remove
.event_type
= event_type
;
452 listener_to_remove
.pf_callback
= pf_callback
;
453 listener_to_remove
.p_user_data
= p_user_data
;
454 listener_to_remove
.is_asynchronous
= true;
456 libvlc_event_async_ensure_listener_removal(p_event_manager
, &listener_to_remove
);