]> git.sesse.net Git - vlc/blob - src/misc/events.c
Don't include config.h from the headers - refs #297.
[vlc] / src / misc / events.c
1 /*****************************************************************************
2  * events.c: events interface
3  * This library provides an interface to the send and receive events.
4  * It is more lightweight than variable based callback.
5  * Methode
6  *****************************************************************************
7  * Copyright (C) 1998-2005 the VideoLAN team
8  * $Id$
9  *
10  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org >
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc/vlc.h>
36
37 #include <assert.h>
38
39 #include <vlc_events.h>
40 #include <vlc_arrays.h>
41
42 /*****************************************************************************
43  * Documentation : Read vlc_events.h
44  *****************************************************************************/
45
46 //#define DEBUG_EVENT
47
48 /*****************************************************************************
49  *  Private types.
50  *****************************************************************************/
51
52 typedef struct vlc_event_listener_t
53 {
54     void *               p_user_data;
55     vlc_event_callback_t pf_callback;
56 #ifdef DEBUG_EVENT
57     char *               psz_debug_name;
58 #endif
59 } vlc_event_listener_t;
60
61 typedef struct vlc_event_listeners_group_t
62 {
63     vlc_event_type_t    event_type;
64     DECL_ARRAY(struct vlc_event_listener_t *) listeners;
65
66    /* Used in vlc_event_send() to make sure to behave
67       Correctly when vlc_event_detach was called during
68       a callback */
69     vlc_bool_t          b_sublistener_removed;
70                                          
71 } vlc_event_listeners_group_t;
72
73 #ifdef DEBUG_EVENT
74 static const char * ppsz_event_type_to_name[] =
75 {
76     [vlc_InputItemMetaChanged]          = "vlc_InputItemMetaChanged",
77     [vlc_InputItemSubItemAdded]         = "vlc_InputItemSubItemAdded",
78     [vlc_ServicesDiscoveryItemAdded]    = "vlc_ServicesDiscoveryItemAdded",
79     [vlc_ServicesDiscoveryItemRemoved]  = "vlc_ServicesDiscoveryItemRemoved"
80 };
81 #endif
82
83 static vlc_bool_t
84 group_contains_listener( vlc_event_listeners_group_t * group,
85                          vlc_event_listener_t * searched_listener )
86 {
87     vlc_event_listener_t * listener;
88     FOREACH_ARRAY( listener, group->listeners )
89         if( searched_listener == listener )
90             return VLC_TRUE;
91     FOREACH_END()
92     return VLC_FALSE;
93 }
94
95 /*****************************************************************************
96  *
97  *****************************************************************************/
98
99 /**
100  * Initialize event manager object
101  * p_obj is the object that contains the event manager. But not
102  * necessarily a vlc_object_t (an input_item_t is not a vlc_object_t
103  * for instance).
104  * p_parent_obj gives a libvlc instance
105  */
106 int vlc_event_manager_init( vlc_event_manager_t * p_em, void * p_obj,
107                             vlc_object_t * p_parent_obj )
108 {
109     p_em->p_obj = p_obj;
110     p_em->p_parent_object = p_parent_obj;
111     vlc_mutex_init( p_parent_obj, &p_em->object_lock );
112
113     /* We need a recursive lock here, because we need to be able
114      * to call libvlc_event_detach even if vlc_event_send is in
115      * the call frame.
116      * This ensures that after libvlc_event_detach, the callback
117      * will never gets triggered.
118      * */
119     vlc_mutex_init_recursive( p_parent_obj, &p_em->event_sending_lock );
120     ARRAY_INIT( p_em->listeners_groups );
121     return VLC_SUCCESS;
122 }
123
124 /**
125  * Destroy the event manager
126  */
127 void vlc_event_manager_fini( vlc_event_manager_t * p_em )
128 {
129     struct vlc_event_listeners_group_t * listeners_group;
130     struct vlc_event_listener_t * listener;
131
132     vlc_mutex_destroy( &p_em->object_lock );
133     vlc_mutex_destroy( &p_em->event_sending_lock );
134
135     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
136         FOREACH_ARRAY( listener, listeners_group->listeners )
137             free( listener );
138         FOREACH_END()
139         ARRAY_RESET( listeners_group->listeners );
140         free( listeners_group );
141     FOREACH_END()
142     ARRAY_RESET( p_em->listeners_groups );
143 }
144
145 /**
146  * Destroy the event manager
147  */
148 int vlc_event_manager_register_event_type(
149         vlc_event_manager_t * p_em,
150         vlc_event_type_t event_type )
151 {
152     vlc_event_listeners_group_t * listeners_group;
153     listeners_group = malloc(sizeof(vlc_event_listeners_group_t));
154
155     if( !listeners_group )
156         return VLC_ENOMEM;
157
158     listeners_group->event_type = event_type;
159     ARRAY_INIT( listeners_group->listeners );
160  
161     vlc_mutex_lock( &p_em->object_lock );
162     ARRAY_APPEND( p_em->listeners_groups, listeners_group );
163     vlc_mutex_unlock( &p_em->object_lock );
164
165     return VLC_SUCCESS;
166 }
167
168 /**
169  * Send an event to the listener attached to this p_em.
170  */
171 void vlc_event_send( vlc_event_manager_t * p_em,
172                      vlc_event_t * p_event )
173 {
174     vlc_event_listeners_group_t * listeners_group = NULL;
175     vlc_event_listener_t * listener;
176     vlc_event_listener_t * array_of_cached_listeners = NULL;
177     vlc_event_listener_t * cached_listener;
178     int i, i_cached_listeners = 0;
179
180     /* Fill event with the sending object now */
181     p_event->p_obj = p_em->p_obj;
182
183     vlc_mutex_lock( &p_em->object_lock );
184     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
185         if( listeners_group->event_type == p_event->type )
186         {
187             if( listeners_group->listeners.i_size <= 0 )
188                 break;
189
190             /* Save the function to call */
191             i_cached_listeners = listeners_group->listeners.i_size;
192             array_of_cached_listeners = malloc(
193                     sizeof(vlc_event_listener_t)*i_cached_listeners );
194             if( !array_of_cached_listeners )
195             {
196                 msg_Err( p_em->p_parent_object, "Not enough memory in vlc_event_send" );
197                 vlc_mutex_unlock( &p_em->object_lock );
198                 return;
199             }
200
201             cached_listener = array_of_cached_listeners;
202             FOREACH_ARRAY( listener, listeners_group->listeners )
203                 memcpy( cached_listener, listener, sizeof(vlc_event_listener_t));
204 #ifdef DEBUG_EVENT
205                 cached_listener->psz_debug_name = strdup(cached_listener->psz_debug_name);
206 #endif
207                 cached_listener++;
208             FOREACH_END()
209
210             break;
211         }
212     FOREACH_END()
213     vlc_mutex_unlock( &p_em->object_lock );
214  
215     /* Call the function attached */
216     cached_listener = array_of_cached_listeners;
217
218     if( !listeners_group || !array_of_cached_listeners )
219     {
220         free( array_of_cached_listeners );
221         return;
222     }
223
224     vlc_mutex_lock( &p_em->event_sending_lock ) ;
225
226     /* Track item removed from *this* thread, with a simple flag */
227     listeners_group->b_sublistener_removed = VLC_FALSE;
228
229     for( i = 0; i < i_cached_listeners; i++ )
230     {
231 #ifdef DEBUG_EVENT
232         msg_Dbg( p_em->p_parent_object,
233                     "Calling '%s' with a '%s' event (data %p)",
234                     cached_listener->psz_debug_name,
235                     ppsz_event_type_to_name[p_event->type],
236                     cached_listener->p_user_data );
237         free(cached_listener->psz_debug_name);
238 #endif
239         /* No need to lock on listeners_group, a listener group can't be removed */
240         if( listeners_group->b_sublistener_removed )
241         {
242             /* If a callback was removed, this gets called */
243             vlc_bool_t valid_listener;
244             vlc_mutex_lock( &p_em->object_lock );
245             valid_listener = group_contains_listener( listeners_group, cached_listener );
246             vlc_mutex_unlock( &p_em->object_lock );
247             if( !valid_listener )
248             {
249 #ifdef DEBUG_EVENT
250                 msg_Dbg( p_em->p_parent_object, "Callback was removed during execution" );
251 #endif
252                 cached_listener++;
253                 continue;
254             }
255         }
256         cached_listener->pf_callback( p_event, cached_listener->p_user_data );
257         cached_listener++;
258     }
259     vlc_mutex_unlock( &p_em->event_sending_lock );
260
261     free( array_of_cached_listeners );
262 }
263
264 /**
265  * Add a callback for an event.
266  */
267 int __vlc_event_attach( vlc_event_manager_t * p_em,
268                         vlc_event_type_t event_type,
269                         vlc_event_callback_t pf_callback,
270                         void *p_user_data,
271                         const char * psz_debug_name )
272 {
273     vlc_event_listeners_group_t * listeners_group;
274     vlc_event_listener_t * listener;
275     listener = malloc(sizeof(vlc_event_listener_t));
276     if( !listener )
277         return VLC_ENOMEM;
278  
279     listener->p_user_data = p_user_data;
280     listener->pf_callback = pf_callback;
281 #ifdef DEBUG_EVENT
282     listener->psz_debug_name = strdup( psz_debug_name );
283 #else
284     (void)psz_debug_name;
285 #endif
286
287     vlc_mutex_lock( &p_em->object_lock );
288     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
289         if( listeners_group->event_type == event_type )
290         {
291             ARRAY_APPEND( listeners_group->listeners, listener );
292 #ifdef DEBUG_EVENT
293                 msg_Dbg( p_em->p_parent_object,
294                     "Listening to '%s' event with '%s' (data %p)",
295                     ppsz_event_type_to_name[event_type],
296                     listener->psz_debug_name,
297                     listener->p_user_data );
298 #endif
299             vlc_mutex_unlock( &p_em->object_lock );
300             return VLC_SUCCESS;
301         }
302     FOREACH_END()
303     vlc_mutex_unlock( &p_em->object_lock );
304
305     msg_Err( p_em->p_parent_object, "Can't attach to an object event manager event" );
306     free(listener);
307     return VLC_EGENERIC;
308 }
309
310 /**
311  * Remove a callback for an event.
312  */
313
314 int vlc_event_detach( vlc_event_manager_t *p_em,
315                       vlc_event_type_t event_type,
316                       vlc_event_callback_t pf_callback,
317                       void *p_user_data )
318 {
319     vlc_event_listeners_group_t * listeners_group;
320     struct vlc_event_listener_t * listener;
321
322     vlc_mutex_lock( &p_em->object_lock );
323     vlc_mutex_lock( &p_em->event_sending_lock );
324     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
325         if( listeners_group->event_type == event_type )
326         {
327             FOREACH_ARRAY( listener, listeners_group->listeners )
328                 if( listener->pf_callback == pf_callback &&
329                     listener->p_user_data == p_user_data )
330                 {
331                     /* Tell vlc_event_send, we did remove an item from that group,
332                        in case vlc_event_send is in our caller stack  */
333                     listeners_group->b_sublistener_removed = VLC_TRUE;
334
335                     /* that's our listener */
336                     ARRAY_REMOVE( listeners_group->listeners,
337                         fe_idx /* This comes from the macro (and that's why
338                                   I hate macro) */ );
339 #ifdef DEBUG_EVENT
340                     msg_Dbg( p_em->p_parent_object,
341                         "Detaching '%s' from '%s' event (data %p)",
342                         listener->psz_debug_name,
343                         ppsz_event_type_to_name[event_type],
344                         listener->p_user_data );
345
346                     free( listener->psz_debug_name );
347 #endif
348                     free( listener );
349                     vlc_mutex_unlock( &p_em->event_sending_lock );
350                     vlc_mutex_unlock( &p_em->object_lock );
351                     return VLC_SUCCESS;
352                 }
353             FOREACH_END()
354         }
355     FOREACH_END()
356     vlc_mutex_unlock( &p_em->event_sending_lock );
357     vlc_mutex_unlock( &p_em->object_lock );
358
359     msg_Warn( p_em->p_parent_object, "Can't detach to an object event manager event" );
360
361     return VLC_EGENERIC;
362 }
363