]> git.sesse.net Git - vlc/blob - lib/event.c
Qt: fallback on the high-resolution icons instead of the xpm
[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
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)
281     DEF(MediaPlayerVout)
282
283     DEF(MediaListItemAdded)
284     DEF(MediaListWillAddItem)
285     DEF(MediaListItemDeleted)
286     DEF(MediaListWillDeleteItem)
287
288     DEF(MediaListViewItemAdded)
289     DEF(MediaListViewWillAddItem)
290     DEF(MediaListViewItemDeleted)
291     DEF(MediaListViewWillDeleteItem)
292
293     DEF(MediaListPlayerPlayed)
294     DEF(MediaListPlayerNextItemSet)
295     DEF(MediaListPlayerStopped)
296
297     DEF(MediaDiscovererStarted)
298     DEF(MediaDiscovererEnded)
299
300     DEF(VlmMediaAdded)
301     DEF(VlmMediaRemoved)
302     DEF(VlmMediaChanged)
303     DEF(VlmMediaInstanceStarted)
304     DEF(VlmMediaInstanceStopped)
305     DEF(VlmMediaInstanceStatusInit)
306     DEF(VlmMediaInstanceStatusOpening)
307     DEF(VlmMediaInstanceStatusPlaying)
308     DEF(VlmMediaInstanceStatusPause)
309     DEF(VlmMediaInstanceStatusEnd)
310     DEF(VlmMediaInstanceStatusError)
311 };
312 #undef DEF
313
314 static const char unknown_event_name[] = "Unknown Event";
315
316 static int evcmp( const void *a, const void *b )
317 {
318     return (*(const int *)a) - ((event_name_t *)b)->type;
319 }
320
321 const char * libvlc_event_type_name( int event_type )
322 {
323     const event_name_t *p;
324
325     p = bsearch( &event_type, event_list,
326                  sizeof(event_list)/sizeof(event_list[0]), sizeof(*p),
327                  evcmp );
328     return p ? p->name : unknown_event_name;
329 }
330
331 /**************************************************************************
332  *       event_attach (internal) :
333  *
334  * Add a callback for an event.
335  **************************************************************************/
336 static
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 )
341 {
342     libvlc_event_listeners_group_t * listeners_group;
343     libvlc_event_listener_t * listener;
344     int i;
345
346     listener = malloc(sizeof(libvlc_event_listener_t));
347     if( unlikely(listener == NULL) )
348         return ENOMEM;
349
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;
354
355     vlc_mutex_lock( &p_event_manager->object_lock );
356     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
357     {
358         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
359         if( listeners_group->event_type == listener->event_type )
360         {
361             vlc_array_append( &listeners_group->listeners, listener );
362             vlc_mutex_unlock( &p_event_manager->object_lock );
363             return 0;
364         }
365     }
366     vlc_mutex_unlock( &p_event_manager->object_lock );
367
368     free(listener);
369     fprintf( stderr, "This object event manager doesn't know about '%s' events",
370              libvlc_event_type_name(event_type) );
371     assert(0);
372     return -1;
373 }
374
375 /**************************************************************************
376  *       libvlc_event_attach (public) :
377  *
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,
383                          void *p_user_data )
384 {
385     return event_attach(p_event_manager, event_type, pf_callback, p_user_data,
386                         false /* synchronous */);
387 }
388
389 /**************************************************************************
390  *       libvlc_event_attach (public) :
391  *
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,
397                          void *p_user_data )
398 {
399     event_attach(p_event_manager, event_type, pf_callback, p_user_data,
400                  true /* asynchronous */);
401 }
402
403 /**************************************************************************
404  *       libvlc_event_detach (public) :
405  *
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,
411                                      void *p_user_data )
412 {
413     libvlc_event_listeners_group_t * listeners_group;
414     libvlc_event_listener_t * listener;
415     int i, j;
416     bool found = false;
417
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++)
421     {
422         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
423         if( listeners_group->event_type == event_type )
424         {
425             for( j = 0; j < vlc_array_count(&listeners_group->listeners); j++)
426             {
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 )
431                 {
432                     /* that's our listener */
433
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;
437
438                     free( listener );
439                     vlc_array_remove( &listeners_group->listeners, j );
440                     found = true;
441                     break;
442                 }
443             }
444         }
445     }
446     vlc_mutex_unlock( &p_event_manager->object_lock );
447     vlc_mutex_unlock( &p_event_manager->event_sending_lock );
448
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;
455
456     libvlc_event_async_ensure_listener_removal(p_event_manager, &listener_to_remove);
457
458     assert(found);
459 }