misc/events.c: (Thanks to ileoo) Fix a crash.
[vlc.git] / 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 #include <vlc/vlc.h>
32
33 #include <assert.h>
34
35 #include <vlc_events.h>
36 #include <vlc_arrays.h>
37
38 /*****************************************************************************
39  * Documentation : Read vlc_events.h
40  *****************************************************************************/
41
42 //#define DEBUG_EVENT
43
44 /*****************************************************************************
45  *  Private types.
46  *****************************************************************************/
47
48 typedef struct vlc_event_listener_t
49 {
50     void *               p_user_data;
51     vlc_event_callback_t pf_callback;
52 #ifdef DEBUG_EVENT
53     char *               psz_debug_name;
54 #endif
55 } vlc_event_listener_t;
56
57 typedef struct vlc_event_listeners_group_t
58 {
59     vlc_event_type_t    event_type;
60     DECL_ARRAY(struct vlc_event_listener_t *) listeners;
61 } vlc_event_listeners_group_t;
62
63 #ifdef DEBUG_EVENT
64 static const char * ppsz_event_type_to_name[] = 
65 {
66     [vlc_InputItemMetaChanged]          = "vlc_InputItemMetaChanged",
67     [vlc_InputItemSubItemAdded]         = "vlc_InputItemSubItemAdded",
68     [vlc_ServicesDiscoveryItemAdded]    = "vlc_ServicesDiscoveryItemAdded",
69     [vlc_ServicesDiscoveryItemRemoved]  = "vlc_ServicesDiscoveryItemRemoved"
70 };
71 #endif
72
73 /*****************************************************************************
74  * 
75  *****************************************************************************/
76
77 /**
78  * Initialize event manager object
79  * p_obj is the object that contains the event manager. But not
80  * necessarily a vlc_object_t (an input_item_t is not a vlc_object_t
81  * for instance).
82  * p_parent_obj gives a libvlc instance
83  */
84 int vlc_event_manager_init( vlc_event_manager_t * p_em, void * p_obj,
85                             vlc_object_t * p_parent_obj )
86 {
87     p_em->p_obj = p_obj;
88     p_em->p_parent_object = p_parent_obj;
89     vlc_mutex_init( p_parent_obj, &p_em->object_lock );
90     ARRAY_INIT( p_em->listeners_groups );
91     return VLC_SUCCESS;
92 }
93
94 /**
95  * Destroy the event manager
96  */
97 void vlc_event_manager_fini( vlc_event_manager_t * p_em )
98 {
99     struct vlc_event_listeners_group_t * listeners_group;
100     struct vlc_event_listener_t * listener;
101
102     vlc_mutex_destroy( &p_em->object_lock );
103
104     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
105         FOREACH_ARRAY( listener, listeners_group->listeners )
106             free( listener );
107         FOREACH_END()
108         free( listeners_group );
109     FOREACH_END()
110 }
111
112 /**
113  * Destroy the event manager
114  */
115 int vlc_event_manager_register_event_type(
116         vlc_event_manager_t * p_em,
117         vlc_event_type_t event_type )
118 {
119     vlc_event_listeners_group_t * listeners_group;
120     listeners_group = malloc(sizeof(vlc_event_listeners_group_t));
121
122     if( !listeners_group )
123         return VLC_ENOMEM;
124
125     listeners_group->event_type = event_type;
126     ARRAY_INIT( listeners_group->listeners );
127     
128     vlc_mutex_lock( &p_em->object_lock );
129     ARRAY_APPEND( p_em->listeners_groups, listeners_group );
130     vlc_mutex_unlock( &p_em->object_lock );
131
132     return VLC_SUCCESS;
133 }
134
135 /**
136  * Send an event to the listener attached to this p_em.
137  */
138 void vlc_event_send( vlc_event_manager_t * p_em,
139                      vlc_event_t * p_event )
140 {
141     vlc_event_listeners_group_t * listeners_group;
142     vlc_event_listener_t * listener;
143     vlc_event_listener_t * array_of_cached_listeners = NULL;
144     vlc_event_listener_t * cached_listener;
145     int i, i_cached_listeners = 0;
146
147     /* Fill event with the sending object now */
148     p_event->p_obj = p_em->p_obj;
149
150     vlc_mutex_lock( &p_em->object_lock );
151     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
152         if( listeners_group->event_type == p_event->type )
153         {
154             if( listeners_group->listeners.i_size <= 0 )
155                 break;
156
157             /* Save the function to call */
158             i_cached_listeners = listeners_group->listeners.i_size;
159             array_of_cached_listeners = malloc(
160                     sizeof(vlc_event_listener_t)*i_cached_listeners );
161             if( !array_of_cached_listeners )
162             {
163                 msg_Err( p_em->p_parent_object, "Not enough memory in vlc_event_send" );
164                 vlc_mutex_unlock( &p_em->object_lock );
165                 return;
166             }
167
168             cached_listener = array_of_cached_listeners;
169             FOREACH_ARRAY( listener, listeners_group->listeners )
170                 memcpy( cached_listener, listener, sizeof(vlc_event_listener_t));
171 #ifdef DEBUG_EVENT
172                 cached_listener->psz_debug_name = strdup(cached_listener->psz_debug_name);
173 #endif
174                 printf("%p\n", cached_listener);
175                 cached_listener++;
176                 printf("%p\n", cached_listener-1);
177             FOREACH_END()
178
179             break;
180         }
181     FOREACH_END()
182     vlc_mutex_unlock( &p_em->object_lock );
183     
184     /* Call the function attached */
185     cached_listener = array_of_cached_listeners;
186     for( i = 0; i < i_cached_listeners; i++ )
187     {
188 #ifdef DEBUG_EVENT
189         msg_Dbg( p_em->p_parent_object,
190                     "Calling '%s' with a '%s' event (data %p)",
191                     cached_listener->psz_debug_name,
192                     ppsz_event_type_to_name[p_event->type],
193                     cached_listener->p_user_data );
194         free(cached_listener->psz_debug_name);
195 #endif
196
197         cached_listener->pf_callback( p_event, cached_listener->p_user_data );
198         cached_listener++;
199     }
200     free( array_of_cached_listeners );
201
202 }
203
204 /**
205  * Add a callback for an event.
206  */
207 int __vlc_event_attach( vlc_event_manager_t * p_em,
208                         vlc_event_type_t event_type,
209                         vlc_event_callback_t pf_callback,
210                         void *p_user_data,
211                         const char * psz_debug_name )
212 {
213     vlc_event_listeners_group_t * listeners_group;
214     vlc_event_listener_t * listener;
215     listener = malloc(sizeof(vlc_event_listener_t));
216     if( !listener )
217         return VLC_ENOMEM;
218     
219     listener->p_user_data = p_user_data;
220     listener->pf_callback = pf_callback;
221 #ifdef DEBUG_EVENT
222     listener->psz_debug_name = strdup( psz_debug_name );
223 #else
224     (void)psz_debug_name;
225 #endif
226
227     vlc_mutex_lock( &p_em->object_lock );
228     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
229         if( listeners_group->event_type == event_type )
230         {
231             ARRAY_APPEND( listeners_group->listeners, listener );
232 #ifdef DEBUG_EVENT
233                 msg_Dbg( p_em->p_parent_object,
234                     "Listening to '%s' event with '%s' (data %p)",
235                     ppsz_event_type_to_name[event_type],
236                     listener->psz_debug_name,
237                     listener->p_user_data );
238 #endif
239             vlc_mutex_unlock( &p_em->object_lock );
240             return VLC_SUCCESS;
241         }
242     FOREACH_END()
243     vlc_mutex_unlock( &p_em->object_lock );
244
245     msg_Err( p_em->p_parent_object, "Can't attach to an object event manager event" );
246     free(listener);
247     return VLC_EGENERIC;
248 }
249
250 /**
251  * Remove a callback for an event.
252  */
253 int vlc_event_detach( vlc_event_manager_t *p_em,
254                       vlc_event_type_t event_type,
255                       vlc_event_callback_t pf_callback,
256                       void *p_user_data )
257 {
258     vlc_event_listeners_group_t * listeners_group;
259     struct vlc_event_listener_t * listener;
260
261     vlc_mutex_lock( &p_em->object_lock );
262     FOREACH_ARRAY( listeners_group, p_em->listeners_groups )
263         if( listeners_group->event_type == event_type )
264         {
265             FOREACH_ARRAY( listener, listeners_group->listeners )
266                 if( listener->pf_callback == pf_callback &&
267                     listener->p_user_data == p_user_data )
268                 {
269                     /* that's our listener */
270                     ARRAY_REMOVE( listeners_group->listeners,
271                         fe_idx /* This comes from the macro (and that's why
272                                   I hate macro) */ );
273 #ifdef DEBUG_EVENT
274                     msg_Dbg( p_em->p_parent_object,
275                         "Detaching '%s' from '%s' event (data %p)",
276                         listener->psz_debug_name,
277                         ppsz_event_type_to_name[event_type],
278                         listener->p_user_data );
279
280                     free( listener->psz_debug_name );
281 #endif
282                     free( listener );
283                     vlc_mutex_unlock( &p_em->object_lock );
284                     return VLC_SUCCESS;
285                 }
286             FOREACH_END()
287         }
288     FOREACH_END()
289     vlc_mutex_unlock( &p_em->object_lock );
290
291     msg_Warn( p_em->p_parent_object, "Can't detach to an object event manager event" );
292
293     return VLC_EGENERIC;
294 }