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
)
284 DEF(MediaPlayerESAdded
)
285 DEF(MediaPlayerESDeleted
)
286 DEF(MediaPlayerESSelected
)
288 DEF(MediaListItemAdded
)
289 DEF(MediaListWillAddItem
)
290 DEF(MediaListItemDeleted
)
291 DEF(MediaListWillDeleteItem
)
293 DEF(MediaListViewItemAdded
)
294 DEF(MediaListViewWillAddItem
)
295 DEF(MediaListViewItemDeleted
)
296 DEF(MediaListViewWillDeleteItem
)
298 DEF(MediaListPlayerPlayed
)
299 DEF(MediaListPlayerNextItemSet
)
300 DEF(MediaListPlayerStopped
)
302 DEF(MediaDiscovererStarted
)
303 DEF(MediaDiscovererEnded
)
308 DEF(VlmMediaInstanceStarted
)
309 DEF(VlmMediaInstanceStopped
)
310 DEF(VlmMediaInstanceStatusInit
)
311 DEF(VlmMediaInstanceStatusOpening
)
312 DEF(VlmMediaInstanceStatusPlaying
)
313 DEF(VlmMediaInstanceStatusPause
)
314 DEF(VlmMediaInstanceStatusEnd
)
315 DEF(VlmMediaInstanceStatusError
)
319 static const char unknown_event_name
[] = "Unknown Event";
321 static int evcmp( const void *a
, const void *b
)
323 return (*(const int *)a
) - ((event_name_t
*)b
)->type
;
326 const char * libvlc_event_type_name( int event_type
)
328 const event_name_t
*p
;
330 p
= bsearch( &event_type
, event_list
,
331 sizeof(event_list
)/sizeof(event_list
[0]), sizeof(*p
),
333 return p
? p
->name
: unknown_event_name
;
336 /**************************************************************************
337 * event_attach (internal) :
339 * Add a callback for an event.
340 **************************************************************************/
342 int event_attach( libvlc_event_manager_t
* p_event_manager
,
343 libvlc_event_type_t event_type
,
344 libvlc_callback_t pf_callback
, void *p_user_data
,
345 bool is_asynchronous
)
347 libvlc_event_listeners_group_t
* listeners_group
;
348 libvlc_event_listener_t
* listener
;
351 listener
= malloc(sizeof(libvlc_event_listener_t
));
352 if( unlikely(listener
== NULL
) )
355 listener
->event_type
= event_type
;
356 listener
->p_user_data
= p_user_data
;
357 listener
->pf_callback
= pf_callback
;
358 listener
->is_asynchronous
= is_asynchronous
;
360 vlc_mutex_lock( &p_event_manager
->object_lock
);
361 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++ )
363 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
364 if( listeners_group
->event_type
== listener
->event_type
)
366 vlc_array_append( &listeners_group
->listeners
, listener
);
367 vlc_mutex_unlock( &p_event_manager
->object_lock
);
371 vlc_mutex_unlock( &p_event_manager
->object_lock
);
374 fprintf( stderr
, "This object event manager doesn't know about '%s' events",
375 libvlc_event_type_name(event_type
) );
380 /**************************************************************************
381 * libvlc_event_attach (public) :
383 * Add a callback for an event.
384 **************************************************************************/
385 int libvlc_event_attach( libvlc_event_manager_t
* p_event_manager
,
386 libvlc_event_type_t event_type
,
387 libvlc_callback_t pf_callback
,
390 return event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
391 false /* synchronous */);
394 /**************************************************************************
395 * libvlc_event_attach (public) :
397 * Add a callback for an event.
398 **************************************************************************/
399 void libvlc_event_attach_async( libvlc_event_manager_t
* p_event_manager
,
400 libvlc_event_type_t event_type
,
401 libvlc_callback_t pf_callback
,
404 event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
405 true /* asynchronous */);
408 /**************************************************************************
409 * libvlc_event_detach (public) :
411 * Remove a callback for an event.
412 **************************************************************************/
413 void libvlc_event_detach( libvlc_event_manager_t
*p_event_manager
,
414 libvlc_event_type_t event_type
,
415 libvlc_callback_t pf_callback
,
418 libvlc_event_listeners_group_t
* listeners_group
;
419 libvlc_event_listener_t
* listener
;
423 vlc_mutex_lock( &p_event_manager
->event_sending_lock
);
424 vlc_mutex_lock( &p_event_manager
->object_lock
);
425 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++)
427 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
428 if( listeners_group
->event_type
== event_type
)
430 for( j
= 0; j
< vlc_array_count(&listeners_group
->listeners
); j
++)
432 listener
= vlc_array_item_at_index(&listeners_group
->listeners
, j
);
433 if( listener
->event_type
== event_type
&&
434 listener
->pf_callback
== pf_callback
&&
435 listener
->p_user_data
== p_user_data
)
437 /* that's our listener */
439 /* Mark this group as edited so that libvlc_event_send
440 * will recheck what listener to call */
441 listeners_group
->b_sublistener_removed
= true;
444 vlc_array_remove( &listeners_group
->listeners
, j
);
451 vlc_mutex_unlock( &p_event_manager
->object_lock
);
452 vlc_mutex_unlock( &p_event_manager
->event_sending_lock
);
454 /* Now make sure any pending async event won't get fired after that point */
455 libvlc_event_listener_t listener_to_remove
;
456 listener_to_remove
.event_type
= event_type
;
457 listener_to_remove
.pf_callback
= pf_callback
;
458 listener_to_remove
.p_user_data
= p_user_data
;
459 listener_to_remove
.is_asynchronous
= true;
461 libvlc_event_async_ensure_listener_removal(p_event_manager
, &listener_to_remove
);