]> git.sesse.net Git - vlc/blob - src/control/event.c
control/event.c: Use vlc_array_t instead of the macros.
[vlc] / src / control / event.c
1 /*****************************************************************************
2  * event.c: New libvlc event control API
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
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
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.
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 General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "libvlc_internal.h"
26 #include <vlc/libvlc.h>
27 #include <vlc_playlist.h>
28
29
30 /*
31  * Private functions
32  */
33
34 /*
35  * Internal libvlc functions
36  */
37
38 /**************************************************************************
39  *       libvlc_event_init (internal) :
40  *
41  * initialization function.
42  **************************************************************************/
43 void libvlc_event_init( libvlc_instance_t *p_instance, libvlc_exception_t *p_e )
44 {
45     (void)p_instance;(void)p_e;
46     /* Will certainly be used to install libvlc_instance event */
47 }
48
49 /**************************************************************************
50  *       libvlc_event_fini (internal) :
51  *
52  * finalization function.
53  **************************************************************************/
54 void libvlc_event_fini( libvlc_instance_t *p_instance )
55 {
56     (void)p_instance;
57 }
58
59 /**************************************************************************
60  *       libvlc_event_manager_init (internal) :
61  *
62  * Init an object's event manager.
63  **************************************************************************/
64 libvlc_event_manager_t *
65 libvlc_event_manager_new( void * p_obj, libvlc_instance_t * p_libvlc_inst,
66                            libvlc_exception_t *p_e )
67 {
68     libvlc_event_manager_t * p_em;
69
70     p_em = malloc(sizeof( libvlc_event_manager_t ));
71     if( !p_em )
72     {
73         libvlc_exception_raise( p_e, "No Memory left" );
74         return NULL;
75     }
76
77     p_em->p_obj = p_obj;
78     p_em->p_libvlc_instance = p_libvlc_inst;
79     libvlc_retain( p_libvlc_inst );
80     vlc_array_init( &p_em->listeners_groups );
81     vlc_mutex_init( p_libvlc_inst->p_libvlc_int, &p_em->object_lock );
82     vlc_mutex_init( p_libvlc_inst->p_libvlc_int, &p_em->event_sending_lock );
83     return p_em;
84 }
85
86 /**************************************************************************
87  *       libvlc_event_manager_release (internal) :
88  *
89  * Init an object's event manager.
90  **************************************************************************/
91 void libvlc_event_manager_release( libvlc_event_manager_t * p_em )
92 {
93     libvlc_event_listeners_group_t * p_lg;
94     int i,j ;
95
96     vlc_mutex_destroy( &p_em->event_sending_lock );
97     vlc_mutex_destroy( &p_em->object_lock );
98
99     for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
100     {
101         p_lg = vlc_array_item_at_index( &p_em->listeners_groups, i );
102
103         for( j = 0; j < vlc_array_count(&p_lg->listeners); j++)
104             free( vlc_array_item_at_index( &p_em->listeners_groups, i ) );
105
106         vlc_array_clear( &p_lg->listeners );
107         free( p_lg );
108     }
109     vlc_array_clear( &p_em->listeners_groups );
110     libvlc_release( p_em->p_libvlc_instance );
111     free( p_em );
112 }
113
114 /**************************************************************************
115  *       libvlc_event_manager_register_event_type (internal) :
116  *
117  * Init an object's event manager.
118  **************************************************************************/
119 void libvlc_event_manager_register_event_type(
120         libvlc_event_manager_t * p_em,
121         libvlc_event_type_t event_type,
122         libvlc_exception_t * p_e )
123 {
124     libvlc_event_listeners_group_t * listeners_group;
125     listeners_group = malloc(sizeof(libvlc_event_listeners_group_t));
126     if( !listeners_group )
127     {
128         libvlc_exception_raise( p_e, "No Memory left" );
129         return;
130     }
131
132     listeners_group->event_type = event_type;
133     vlc_array_init( &listeners_group->listeners );
134
135     vlc_mutex_lock( &p_em->object_lock );
136     vlc_array_append( &p_em->listeners_groups, listeners_group );
137     vlc_mutex_unlock( &p_em->object_lock );
138 }
139
140 /**************************************************************************
141  *       libvlc_event_send (internal) :
142  *
143  * Send a callback.
144  **************************************************************************/
145 void libvlc_event_send( libvlc_event_manager_t * p_em,
146                         libvlc_event_t * p_event )
147 {
148     libvlc_event_listeners_group_t * listeners_group;
149     libvlc_event_listener_t * listener_cached;
150     libvlc_event_listener_t * listener;
151     libvlc_event_listener_t * array_listeners_cached = NULL;
152     int i, i_cached_listeners = 0;
153
154     /* Fill event with the sending object now */
155     p_event->p_obj = p_em->p_obj;
156
157     /* Here a read/write lock would be nice */
158
159     vlc_mutex_lock( &p_em->object_lock );
160     for( i = 0; i < vlc_array_count(&p_em->listeners_groups); i++)
161     {
162         listeners_group = vlc_array_item_at_index(&p_em->listeners_groups, i);
163         if( listeners_group->event_type == p_event->type )
164         {
165             if( vlc_array_count( &listeners_group->listeners ) <= 0 )
166                 break;
167
168             /* Cache a copy of the listener to avoid locking issues */
169             i_cached_listeners = vlc_array_count(&listeners_group->listeners);
170             array_listeners_cached = malloc(sizeof(libvlc_event_listener_t)*(i_cached_listeners));
171             if( !array_listeners_cached )
172             {
173                 printf( "Can't alloc memory in libvlc_event_send" );
174                 break;
175             }
176
177             listener_cached = array_listeners_cached;
178             for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
179             {
180                 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
181                 memcpy( listener_cached, listener, sizeof(libvlc_event_listener_t) );
182                 listener_cached++;
183             }
184             break;
185         }
186     }
187
188     vlc_mutex_unlock( &p_em->object_lock );
189
190     /* Here a read/write lock would be *especially* nice,
191      * We would be able to send event without blocking */
192     /* The only reason for this lock is to be able to wait the
193      * End of events sending with libvlc_event_manager_lock_event_sending.
194      * This can be useful to make sure a callback is completely detached. */
195     vlc_mutex_lock( &p_em->event_sending_lock );
196     listener_cached = array_listeners_cached;
197     for( i = 0; i < i_cached_listeners; i++ )
198     {
199         listener_cached->pf_callback( p_event, listener_cached->p_user_data );
200         listener_cached++;
201     }
202     vlc_mutex_unlock( &p_em->event_sending_lock );
203
204     free( array_listeners_cached );
205 }
206
207 /*
208  * Public libvlc functions
209  */
210
211 /**************************************************************************
212  *       libvlc_event_type_name (public) :
213  *
214  * Get the char * name of an event type.
215  **************************************************************************/
216 static const char * event_type_to_name[] =
217 {
218 #define EVENT(a) [a]=#a
219     EVENT(libvlc_MediaDescriptorMetaChanged),
220     EVENT(libvlc_MediaDescriptorSubItemAdded),
221     EVENT(libvlc_MediaDescriptorDurationChanged),
222     EVENT(libvlc_MediaDescriptorPreparsedChanged),
223     EVENT(libvlc_MediaDescriptorFreed),
224     EVENT(libvlc_MediaDescriptorStateChanged),
225
226     EVENT(libvlc_MediaInstancePlayed),
227     EVENT(libvlc_MediaInstancePaused),
228     EVENT(libvlc_MediaInstanceReachedEnd),
229     EVENT(libvlc_MediaInstanceTimeChanged),
230     EVENT(libvlc_MediaInstancePositionChanged),
231
232     EVENT(libvlc_MediaListItemAdded),
233     EVENT(libvlc_MediaListWillAddItem),
234     EVENT(libvlc_MediaListItemDeleted),
235     EVENT(libvlc_MediaListWillDeleteItem),
236
237     EVENT(libvlc_MediaListViewItemAdded),
238     EVENT(libvlc_MediaListViewWillAddItem),
239     EVENT(libvlc_MediaListViewItemDeleted),
240     EVENT(libvlc_MediaListViewWillDeleteItem),
241
242     EVENT(libvlc_MediaListPlayerPlayed),
243     EVENT(libvlc_MediaListPlayerNextItemSet),
244     EVENT(libvlc_MediaListPlayerStopped),
245
246     EVENT(libvlc_MediaDiscovererStarted),
247     EVENT(libvlc_MediaDiscovererEnded)
248 #undef EVENT
249 };
250 static const char * unkwown_event_name = "Unknown Event";
251
252 const char * libvlc_event_type_name( libvlc_event_type_t event_type )
253 {
254     if( event_type >= sizeof(event_type_to_name)/sizeof(event_type_to_name[0]))
255         return unkwown_event_name;
256     return event_type_to_name[event_type];
257 }
258
259 /**************************************************************************
260  *       libvlc_event_attach (public) :
261  *
262  * Add a callback for an event.
263  **************************************************************************/
264 void libvlc_event_attach( libvlc_event_manager_t * p_event_manager,
265                           libvlc_event_type_t event_type,
266                           libvlc_callback_t pf_callback,
267                           void *p_user_data,
268                           libvlc_exception_t *p_e )
269 {
270     libvlc_event_listeners_group_t * listeners_group;
271     libvlc_event_listener_t * listener;
272     int i;
273
274     listener = malloc(sizeof(libvlc_event_listener_t));
275     if( !listener )
276     {
277         libvlc_exception_raise( p_e, "No Memory left" );
278         return;
279     }
280  
281     listener->event_type = event_type;
282     listener->p_user_data = p_user_data;
283     listener->pf_callback = pf_callback;
284
285     vlc_mutex_lock( &p_event_manager->object_lock );
286     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++ )
287     {
288         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
289         if( listeners_group->event_type == listener->event_type )
290         {
291             vlc_array_append( &listeners_group->listeners, listener );
292             vlc_mutex_unlock( &p_event_manager->object_lock );
293             return;
294         }
295     }
296     vlc_mutex_unlock( &p_event_manager->object_lock );
297
298     free(listener);
299     libvlc_exception_raise( p_e,
300             "This object event manager doesn't know about '%s' events",
301             libvlc_event_type_name(event_type));
302 }
303
304 /**************************************************************************
305  *       libvlc_event_detach (public) :
306  *
307  * Remove a callback for an event.
308  **************************************************************************/
309 void libvlc_event_detach( libvlc_event_manager_t *p_event_manager,
310                           libvlc_event_type_t event_type,
311                           libvlc_callback_t pf_callback,
312                           void *p_user_data,
313                           libvlc_exception_t *p_e )
314 {
315     libvlc_event_detach_lock_state( p_event_manager, event_type, pf_callback,
316                                     p_user_data, libvlc_UnLocked, p_e );
317 }
318
319 /**************************************************************************
320  *       libvlc_event_detach_no_lock (internal) :
321  *
322  * Remove a callback for an event.
323  **************************************************************************/
324 void libvlc_event_detach_lock_state( libvlc_event_manager_t *p_event_manager,
325                                      libvlc_event_type_t event_type,
326                                      libvlc_callback_t pf_callback,
327                                      void *p_user_data,
328                                      libvlc_lock_state_t lockstate,
329                                      libvlc_exception_t *p_e )
330 {
331     libvlc_event_listeners_group_t * listeners_group;
332     libvlc_event_listener_t * listener;
333     int i;
334
335     if( lockstate == libvlc_UnLocked )
336         vlc_mutex_lock( &p_event_manager->event_sending_lock );
337     vlc_mutex_lock( &p_event_manager->object_lock );
338     for( i = 0; i < vlc_array_count(&p_event_manager->listeners_groups); i++)
339     {
340         listeners_group = vlc_array_item_at_index(&p_event_manager->listeners_groups, i);
341         if( listeners_group->event_type == event_type )
342         {
343             for( i = 0; i < vlc_array_count(&listeners_group->listeners); i++)
344             {
345                 listener = vlc_array_item_at_index(&listeners_group->listeners, i);
346                 if( listener->event_type == event_type &&
347                     listener->pf_callback == pf_callback &&
348                     listener->p_user_data == p_user_data )
349                 {
350                     /* that's our listener */
351                     free( listener );
352                     vlc_array_remove( &listeners_group->listeners, i );
353                     vlc_mutex_unlock( &p_event_manager->object_lock );
354                     if( lockstate == libvlc_UnLocked )
355                         vlc_mutex_unlock( &p_event_manager->event_sending_lock );
356                     return;
357                 }
358             }
359         }
360     }
361     vlc_mutex_unlock( &p_event_manager->object_lock );
362     if( lockstate == libvlc_UnLocked )
363         vlc_mutex_unlock( &p_event_manager->event_sending_lock );
364
365     libvlc_exception_raise( p_e,
366             "This object event manager doesn't know about '%s,%p,%p' event observer",
367             libvlc_event_type_name(event_type), pf_callback, p_user_data );
368 }