1 /*****************************************************************************
2 * event.c: New libvlc event control API
3 *****************************************************************************
4 * Copyright (C) 2007-2010 the VideoLAN team
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
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 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 /* Here a read/write lock would be nice */
162 vlc_mutex_lock( &p_em
->object_lock
);
163 for( i
= 0; i
< vlc_array_count(&p_em
->listeners_groups
); i
++)
165 listeners_group
= vlc_array_item_at_index(&p_em
->listeners_groups
, i
);
166 if( listeners_group
->event_type
== p_event
->type
)
168 if( vlc_array_count( &listeners_group
->listeners
) <= 0 )
171 /* Cache a copy of the listener to avoid locking issues,
172 * and allow that edition of listeners during callbacks will garantee immediate effect. */
173 i_cached_listeners
= vlc_array_count(&listeners_group
->listeners
);
174 array_listeners_cached
= malloc(sizeof(libvlc_event_listener_t
)*(i_cached_listeners
));
175 if( !array_listeners_cached
)
177 vlc_mutex_unlock( &p_em
->object_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
);
200 vlc_mutex_unlock( &p_em
->object_lock
);
202 vlc_mutex_lock( &p_em
->event_sending_lock
);
203 listener_cached
= array_listeners_cached
;
204 listeners_group
->b_sublistener_removed
= false;
205 for( i
= 0; i
< i_cached_listeners
; i
++ )
207 if(listener_cached
->is_asynchronous
)
209 /* The listener wants not to block the emitter during event callback */
210 libvlc_event_async_dispatch(p_em
, listener_cached
, p_event
);
214 /* The listener wants to block the emitter during event callback */
216 listener_cached
->pf_callback( p_event
, listener_cached
->p_user_data
);
219 if( listeners_group
->b_sublistener_removed
)
221 /* If a callback was removed, this gets called */
223 vlc_mutex_lock( &p_em
->object_lock
);
224 valid_listener
= group_contains_listener( listeners_group
, listener_cached
);
225 vlc_mutex_unlock( &p_em
->object_lock
);
226 if( !valid_listener
)
234 vlc_mutex_unlock( &p_em
->event_sending_lock
);
236 free( array_listeners_cached
);
240 * Public libvlc functions
243 #define DEF( a ) { libvlc_##a, #a, },
251 static const event_name_t event_list
[] = {
252 DEF(MediaMetaChanged
)
253 DEF(MediaSubItemAdded
)
254 DEF(MediaDurationChanged
)
255 DEF(MediaParsedChanged
)
257 DEF(MediaStateChanged
)
259 DEF(MediaPlayerMediaChanged
)
260 DEF(MediaPlayerNothingSpecial
)
261 DEF(MediaPlayerOpening
)
262 DEF(MediaPlayerBuffering
)
263 DEF(MediaPlayerPlaying
)
264 DEF(MediaPlayerPaused
)
265 DEF(MediaPlayerStopped
)
266 DEF(MediaPlayerForward
)
267 DEF(MediaPlayerBackward
)
268 DEF(MediaPlayerEndReached
)
269 DEF(MediaPlayerEncounteredError
)
270 DEF(MediaPlayerTimeChanged
)
271 DEF(MediaPlayerPositionChanged
)
272 DEF(MediaPlayerSeekableChanged
)
273 DEF(MediaPlayerPausableChanged
)
274 DEF(MediaPlayerTitleChanged
)
275 DEF(MediaPlayerSnapshotTaken
)
276 DEF(MediaPlayerLengthChanged
)
278 DEF(MediaListItemAdded
)
279 DEF(MediaListWillAddItem
)
280 DEF(MediaListItemDeleted
)
281 DEF(MediaListWillDeleteItem
)
283 DEF(MediaListViewItemAdded
)
284 DEF(MediaListViewWillAddItem
)
285 DEF(MediaListViewItemDeleted
)
286 DEF(MediaListViewWillDeleteItem
)
288 DEF(MediaListPlayerPlayed
)
289 DEF(MediaListPlayerNextItemSet
)
290 DEF(MediaListPlayerStopped
)
292 DEF(MediaDiscovererStarted
)
293 DEF(MediaDiscovererEnded
)
298 DEF(VlmMediaInstanceStarted
)
299 DEF(VlmMediaInstanceStopped
)
300 DEF(VlmMediaInstanceStatusInit
)
301 DEF(VlmMediaInstanceStatusOpening
)
305 static const char unknown_event_name
[] = "Unknown Event";
307 static int evcmp( const void *a
, const void *b
)
309 return (*(const int *)a
) - ((event_name_t
*)b
)->type
;
312 const char * libvlc_event_type_name( int event_type
)
314 const event_name_t
*p
;
316 p
= bsearch( &event_type
, event_list
,
317 sizeof(event_list
)/sizeof(event_list
[0]), sizeof(*p
),
319 return p
? p
->name
: unknown_event_name
;
322 /**************************************************************************
323 * event_attach (internal) :
325 * Add a callback for an event.
326 **************************************************************************/
328 int event_attach( libvlc_event_manager_t
* p_event_manager
,
329 libvlc_event_type_t event_type
,
330 libvlc_callback_t pf_callback
, void *p_user_data
,
331 bool is_asynchronous
)
333 libvlc_event_listeners_group_t
* listeners_group
;
334 libvlc_event_listener_t
* listener
;
337 listener
= malloc(sizeof(libvlc_event_listener_t
));
338 if( unlikely(listener
== NULL
) )
341 listener
->event_type
= event_type
;
342 listener
->p_user_data
= p_user_data
;
343 listener
->pf_callback
= pf_callback
;
344 listener
->is_asynchronous
= is_asynchronous
;
346 vlc_mutex_lock( &p_event_manager
->object_lock
);
347 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++ )
349 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
350 if( listeners_group
->event_type
== listener
->event_type
)
352 vlc_array_append( &listeners_group
->listeners
, listener
);
353 vlc_mutex_unlock( &p_event_manager
->object_lock
);
357 vlc_mutex_unlock( &p_event_manager
->object_lock
);
360 fprintf( stderr
, "This object event manager doesn't know about '%s' events",
361 libvlc_event_type_name(event_type
) );
366 /**************************************************************************
367 * libvlc_event_attach (public) :
369 * Add a callback for an event.
370 **************************************************************************/
371 int libvlc_event_attach( libvlc_event_manager_t
* p_event_manager
,
372 libvlc_event_type_t event_type
,
373 libvlc_callback_t pf_callback
,
376 return event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
377 false /* synchronous */);
380 /**************************************************************************
381 * libvlc_event_attach (public) :
383 * Add a callback for an event.
384 **************************************************************************/
385 void libvlc_event_attach_async( libvlc_event_manager_t
* p_event_manager
,
386 libvlc_event_type_t event_type
,
387 libvlc_callback_t pf_callback
,
390 event_attach(p_event_manager
, event_type
, pf_callback
, p_user_data
,
391 true /* asynchronous */);
394 /**************************************************************************
395 * libvlc_event_detach (public) :
397 * Remove a callback for an event.
398 **************************************************************************/
399 void libvlc_event_detach( libvlc_event_manager_t
*p_event_manager
,
400 libvlc_event_type_t event_type
,
401 libvlc_callback_t pf_callback
,
404 libvlc_event_listeners_group_t
* listeners_group
;
405 libvlc_event_listener_t
* listener
;
409 vlc_mutex_lock( &p_event_manager
->event_sending_lock
);
410 vlc_mutex_lock( &p_event_manager
->object_lock
);
411 for( i
= 0; i
< vlc_array_count(&p_event_manager
->listeners_groups
); i
++)
413 listeners_group
= vlc_array_item_at_index(&p_event_manager
->listeners_groups
, i
);
414 if( listeners_group
->event_type
== event_type
)
416 for( j
= 0; j
< vlc_array_count(&listeners_group
->listeners
); j
++)
418 listener
= vlc_array_item_at_index(&listeners_group
->listeners
, j
);
419 if( listener
->event_type
== event_type
&&
420 listener
->pf_callback
== pf_callback
&&
421 listener
->p_user_data
== p_user_data
)
423 /* that's our listener */
425 /* Mark this group as edited so that libvlc_event_send
426 * will recheck what listener to call */
427 listeners_group
->b_sublistener_removed
= false;
430 vlc_array_remove( &listeners_group
->listeners
, j
);
437 vlc_mutex_unlock( &p_event_manager
->object_lock
);
438 vlc_mutex_unlock( &p_event_manager
->event_sending_lock
);
440 /* Now make sure any pending async event won't get fired after that point */
441 libvlc_event_listener_t listener_to_remove
;
442 listener_to_remove
.event_type
= event_type
;
443 listener_to_remove
.pf_callback
= pf_callback
;
444 listener_to_remove
.p_user_data
= p_user_data
;
445 listener_to_remove
.is_asynchronous
= true;
447 libvlc_event_async_ensure_listener_removal(p_event_manager
, &listener_to_remove
);