]> git.sesse.net Git - vlc/blob - lib/event.c
lib: add missing define for scrambled-changed event
[vlc] / lib / event.c
1 /*****************************************************************************
2  * event.c: New libvlc event control API
3  *****************************************************************************
4  * Copyright (C) 2007-2010 VLC authors and VideoLAN
5  * $Id $
6  *
7  * Authors: Filippo Carone <filippo@carone.org>
8  *          Pierre d'Herbemont <pdherbemont # videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #define  LIBVLC_EVENT_TYPES_KEEP_DEFINE
30 #include <vlc/libvlc.h>
31
32 #include "libvlc_internal.h"
33 #include "event_internal.h"
34 #include <assert.h>
35 #include <errno.h>
36
37 typedef struct libvlc_event_listeners_group_t
38 {
39     libvlc_event_type_t event_type;
40     vlc_array_t listeners;
41     bool b_sublistener_removed;
42 } libvlc_event_listeners_group_t;
43
44 /*
45  * Private functions
46  */
47
48 static bool
49 group_contains_listener( libvlc_event_listeners_group_t * group,
50                          libvlc_event_listener_t * searched_listener )
51 {
52     int i;
53     for( i = 0; i < vlc_array_count(&group->listeners); i++ )
54     {
55         if( listeners_are_equal(searched_listener, vlc_array_item_at_index(&group->listeners, i)) )
56             return true;
57     }
58     return false;
59 }
60
61 /*
62  * Internal libvlc functions
63  */
64
65 /**************************************************************************
66  *       libvlc_event_manager_new (internal) :
67  *
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 )
72 {
73     libvlc_event_manager_t * p_em;
74
75     p_em = malloc(sizeof( libvlc_event_manager_t ));
76     if( !p_em )
77     {
78         libvlc_printerr( "Not enough memory" );
79         return NULL;
80     }
81
82     p_em->p_obj = p_obj;
83     p_em->p_obj = p_obj;
84     p_em->async_event_queue = NULL;
85     p_em->p_libvlc_instance = p_libvlc_inst;
86
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 );
91     return p_em;
92 }
93
94 /**************************************************************************
95  *       libvlc_event_manager_release (internal) :
96  *
97  * Release an object's event manager.
98  **************************************************************************/
99 void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
100 {
101     libvlc_event_listeners_group_t * p_lg;
102     int i,j ;
103
104     libvlc_event_async_fini(p_em);
105
106     vlc_mutex_destroy( &p_em->event_sending_lock );
107     vlc_mutex_destroy( &p_em->object_lock );
108
109     for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
110     {
111         p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );
112
113         for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
114             free( vlc_array_item_at_index( &p_lg->listeners, j ) );
115
116         vlc_array_clear( &p_lg->listeners );
117         free( p_lg );
118     }
119     vlc_array_clear( &p_em->listeners_groups );
120     libvlc_release( p_em->p_libvlc_instance );
121     free( p_em );
122 }
123
124 /**************************************************************************
125  *       libvlc_event_manager_register_event_type (internal) :
126  *
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 )
132 {
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 );
137
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 );
141 }
142
143 /**************************************************************************
144  *       libvlc_event_send (internal) :
145  *
146  * Send a callback.
147  **************************************************************************/
148 void libvlc_event_send( libvlc_event_manager_t * p_em,
149                         libvlc_event_t * p_event )
150 {
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;
156
157     /* Fill event with the sending object now */
158     p_event->p_obj = p_em->p_obj;
159
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++)
163     {
164         listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
165         if( listeners_group->event_type == p_event->type )
166         {
167             if( vlc_array_count( &listeners_group->listeners ) <= 0 )
168                 break;
169
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 )
175             {
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" );
179                 return;
180             }
181
182             listener_cached = array_listeners_cached;
183             for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
184             {
185                 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
186                 memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
187                 listener_cached++;
188             }
189             break;
190         }
191     }
192
193     if( !listeners_group )
194     {
195         free( array_listeners_cached );
196         vlc_mutex_unlock( &p_em->object_lock );
197         vlc_mutex_unlock( &p_em->event_sending_lock );
198         return;
199     }
200
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;
205
206     vlc_mutex_unlock( &p_em->object_lock );
207
208     listener_cached = array_listeners_cached;
209     for( i = 0; i < i_cached_listeners; i++ )
210     {
211         if(listener_cached->is_asynchronous)
212         {
213             /* The listener wants not to block the emitter during event callback */
214             libvlc_event_async_dispatch(p_em, listener_cached, p_event);
215         }
216         else
217         {
218             /* The listener wants to block the emitter during event callback */
219
220             listener_cached->pf_callback( p_event, listener_cached->p_user_data );
221             listener_cached++;
222
223             if( listeners_group->b_sublistener_removed )
224             {
225                 /* If a callback was removed, this gets called */
226                 bool valid_listener;
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 )
231                 {
232                     listener_cached++;
233                     continue;
234                 }
235             }
236         }
237     }
238     vlc_mutex_unlock( &p_em->event_sending_lock );
239
240     free( array_listeners_cached );
241 }
242
243 /*
244  * Public libvlc functions
245  */
246
247 #define DEF( a ) { libvlc_##a, #a, },
248
249 typedef struct
250 {
251     int type;
252     const char name[40];
253 } event_name_t;
254
255 static const event_name_t event_list[] = {
256     DEF(MediaMetaChanged)
257     DEF(MediaSubItemAdded)
258     DEF(MediaDurationChanged)
259     DEF(MediaParsedChanged)
260     DEF(MediaFreed)
261     DEF(MediaStateChanged)
262     DEF(MediaSubItemTreeAdded)
263
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)
282     DEF(MediaPlayerVout)
283     DEF(MediaPlayerScrambledChanged)
284
285     DEF(MediaListItemAdded)
286     DEF(MediaListWillAddItem)
287     DEF(MediaListItemDeleted)
288     DEF(MediaListWillDeleteItem)
289
290     DEF(MediaListViewItemAdded)
291     DEF(MediaListViewWillAddItem)
292     DEF(MediaListViewItemDeleted)
293     DEF(MediaListViewWillDeleteItem)
294
295     DEF(MediaListPlayerPlayed)
296     DEF(MediaListPlayerNextItemSet)
297     DEF(MediaListPlayerStopped)
298
299     DEF(MediaDiscovererStarted)
300     DEF(MediaDiscovererEnded)
301
302     DEF(VlmMediaAdded)
303     DEF(VlmMediaRemoved)
304     DEF(VlmMediaChanged)
305     DEF(VlmMediaInstanceStarted)
306     DEF(VlmMediaInstanceStopped)
307     DEF(VlmMediaInstanceStatusInit)
308     DEF(VlmMediaInstanceStatusOpening)
309     DEF(VlmMediaInstanceStatusPlaying)
310     DEF(VlmMediaInstanceStatusPause)
311     DEF(VlmMediaInstanceStatusEnd)
312     DEF(VlmMediaInstanceStatusError)
313 };
314 #undef DEF
315
316 static const char unknown_event_name[] = "Unknown Event";
317
318 static int evcmp( const void *a, const void *b )
319 {
320     return (*(const int *)a) - ((event_name_t *)b)->type;
321 }
322
323 const char * libvlc_event_type_name( int event_type )
324 {
325     const event_name_t *p;
326
327     p = bsearch( &event_type, event_list,
328                  sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
329                  evcmp );
330     return p ? p->name : unknown_event_name;
331 }
332
333 /**************************************************************************
334  *       event_attach (internal) :
335  *
336  * Add a callback for an event.
337  **************************************************************************/
338 static
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 )
343 {
344     libvlc_event_listeners_group_t * listeners_group;
345     libvlc_event_listener_t * listener;
346     int i;
347
348     listener = malloc(sizeof(libvlc_event_listener_t));
349     if( unlikely(listener == NULL) )
350         return ENOMEM;
351
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;
356
357     vlc_mutex_lock( &p_event_manager->object_lock );
358     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
359     {
360         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
361         if( listeners_group->event_type == listener->event_type )
362         {
363             vlc_array_append( &listeners_group->listeners, listener );
364             vlc_mutex_unlock( &p_event_manager->object_lock );
365             return 0;
366         }
367     }
368     vlc_mutex_unlock( &p_event_manager->object_lock );
369
370     free(listener);
371     fprintf( stderr, "This object event manager doesn't know about '%s' events",
372              libvlc_event_type_name(event_type) );
373     assert(0);
374     return -1;
375 }
376
377 /**************************************************************************
378  *       libvlc_event_attach (public) :
379  *
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,
385                          void *p_user_data )
386 {
387     return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
388                         false /* synchronous */);
389 }
390
391 /**************************************************************************
392  *       libvlc_event_attach (public) :
393  *
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,
399                          void *p_user_data )
400 {
401     event_attach(p_event_manager, event_type, pf_callback, p_user_data,
402                  true /* asynchronous */);
403 }
404
405 /**************************************************************************
406  *       libvlc_event_detach (public) :
407  *
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,
413                                      void *p_user_data )
414 {
415     libvlc_event_listeners_group_t * listeners_group;
416     libvlc_event_listener_t * listener;
417     int i, j;
418     bool found = false;
419
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++)
423     {
424         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
425         if( listeners_group->event_type == event_type )
426         {
427             for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
428             {
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 )
433                 {
434                     /* that's our listener */
435
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;
439
440                     free( listener );
441                     vlc_array_remove( &listeners_group->listeners, j );
442                     found = true;
443                     break;
444                 }
445             }
446         }
447     }
448     vlc_mutex_unlock( &p_event_manager->object_lock );
449     vlc_mutex_unlock( &p_event_manager->event_sending_lock );
450
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;
457
458     libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
459
460     assert(found);
461 }