]> git.sesse.net Git - vlc/blob - src/control/event.c
control/event.c: Make sure the callback list is initially set to NULL. (Likely fix...
[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  *       handle_event (private)
36  *
37  * Callback from the vlc variables
38  **************************************************************************/
39 static int handle_event( vlc_object_t *p_this, char const *psz_cmd,
40                          vlc_value_t oldval, vlc_value_t newval,
41                          void *p_data )
42 {
43     /* This is thread safe, as the var_*Callback already provide the locking
44      * facility for p_data */
45     struct libvlc_callback_entry_t *entry = p_data;
46     libvlc_event_t event;
47
48     event.type = entry->i_event_type;    
49
50     if (event.type == libvlc_VolumeChanged)
51     {
52         if(!strcmp(psz_cmd, "intf-change"))
53         {
54             input_thread_t * p_input = (input_thread_t *)p_this;
55             vlc_value_t val;
56             var_Get( p_input, "time", &val );
57
58             /* Only send event at a reasonable time precision (500ms) */
59             /* (FIXME: this should be configurable) */
60             if ((val.i_time % I64C(500000)) != 0)
61             {
62                 /* Don't send this event */
63                 return VLC_SUCCESS;
64             }
65
66             event.u.input_position_changed.new_position = val.i_time;
67         }
68         else
69             event.u.input_position_changed.new_position = newval.i_time;
70
71         event.u.input_position_changed.new_position *= 1000LL;
72     }
73     else if (event.type == libvlc_InputPositionChanged)
74     {
75         event.u.volume_changed.new_volume = newval.i_int;
76     }
77
78     /* Call the client entry */
79     entry->f_callback( entry->p_instance, &event, entry->p_user_data );
80
81     return VLC_SUCCESS;
82 }
83
84 /**************************************************************************
85  *       get_input (private) :
86  *
87  * Utility function, Object should be released by vlc_object_release
88  * afterwards
89  **************************************************************************/
90 static input_thread_t * get_input(libvlc_instance_t * p_instance)
91 {
92     libvlc_media_instance_t * p_mi;
93     input_thread_t * p_input;
94
95     p_mi = libvlc_playlist_get_media_instance( p_instance, NULL );
96
97     if( !p_mi )
98         return NULL;
99
100     p_input = libvlc_get_input_thread( p_mi, NULL );
101
102     libvlc_media_instance_release( p_mi );
103
104     return p_input;
105 }
106
107 /**************************************************************************
108  *       install_input_event (private) :
109  *
110  * vlc variables callback, used to install input event.
111  * Can be called manually though.
112  **************************************************************************/
113 static int install_input_event( vlc_object_t *p_this, char const *psz_cmd,
114                                 vlc_value_t oldval, vlc_value_t newval,
115                                 void *p_data )
116 {
117     libvlc_instance_t * p_instance = p_data;
118     struct libvlc_callback_entry_list_t *p_listitem;
119     input_thread_t * p_input = get_input( p_instance );
120
121     if( !p_input )
122         return;
123
124     vlc_mutex_lock( &p_instance->instance_lock );
125
126     p_listitem = p_instance->p_callback_list;
127
128     for( ; p_listitem ; p_listitem = p_listitem->next )
129     {
130         if (p_listitem->elmt->i_event_type == libvlc_InputPositionChanged)
131         {
132             /* FIXME: here we shouldn't listen on intf-change, we have to provide
133              * in vlc core a more accurate callback */
134             var_AddCallback( p_input, "intf-change", handle_event, p_listitem->elmt );
135             var_AddCallback( p_input, "time", handle_event, p_listitem->elmt );
136         }
137     }
138
139     vlc_mutex_unlock( &p_instance->instance_lock );
140     vlc_object_release( p_input );
141     return VLC_SUCCESS;
142 }
143
144 /**************************************************************************
145  *       add_callback_to_list (private) :
146  *
147  * callback list utility function.
148  **************************************************************************/
149 static inline void add_callback_to_list( struct libvlc_callback_entry_t *entry,
150                                          struct libvlc_callback_entry_list_t **list )
151 {
152     struct libvlc_callback_entry_list_t *new_listitem;
153
154     /* malloc/free strategy:
155      *  - alloc-ded in add_callback_entry
156      *  - free-ed by libvlc_event_remove_callback
157      *  - free-ed in libvlc_destroy threw libvlc_event_remove_callback
158      *    when entry is destroyed
159      */
160     new_listitem = malloc( sizeof( struct libvlc_callback_entry_list_t ) );
161     new_listitem->elmt = entry;
162     new_listitem->next = *list;
163     new_listitem->prev = NULL;
164
165     if(*list)
166         (*list)->prev = new_listitem;
167
168     *list = new_listitem;
169 }
170
171 /**************************************************************************
172  *       remove_variable_callback (private) :
173  *
174  * Delete the appropriate vlc variables callback for an event.
175  **************************************************************************/
176 static int remove_variable_callback( libvlc_instance_t *p_instance, 
177                                      struct libvlc_callback_entry_t * p_entry )
178 {
179     input_thread_t * p_input = get_input( p_instance );
180     int res = VLC_SUCCESS;
181
182     /* Note: Appropriate lock should be held by the caller */
183
184     switch ( p_entry->i_event_type )
185     {
186         case libvlc_VolumeChanged:
187             res = var_DelCallback( p_instance->p_libvlc_int, "volume-change",
188                              handle_event, p_entry );
189             break;
190         case libvlc_InputPositionChanged:
191             /* We may not be deleting the right p_input callback, in this case this
192              * will be a no-op */
193             var_DelCallback( p_input, "intf-change", handle_event, p_entry );
194             var_DelCallback( p_input, "position", handle_event, p_entry );
195             break;
196     }
197     
198     if (p_input)
199         vlc_object_release( p_input );
200
201     return res;
202 }
203
204 /*
205  * Internal libvlc functions
206  */
207
208 /**************************************************************************
209  *       libvlc_event_init (internal) :
210  *
211  * initialization function.
212  **************************************************************************/
213 void libvlc_event_init( libvlc_instance_t *p_instance, libvlc_exception_t *p_e )
214 {
215     playlist_t *p_playlist = p_instance->p_libvlc_int->p_playlist;
216
217     p_instance->p_callback_list = NULL;
218
219     if( !p_playlist )
220         RAISEVOID ("Can't listen to input event");
221
222     /* Install a Callback for input changes, so
223      * so we can track input event */
224      var_AddCallback( p_playlist, "playlist-current",
225                       install_input_event, p_instance );
226 }
227
228 /**************************************************************************
229  *       libvlc_event_fini (internal) :
230  *
231  * finalization function.
232  **************************************************************************/
233 void libvlc_event_fini( libvlc_instance_t *p_instance, libvlc_exception_t *p_e )
234 {
235     playlist_t *p_playlist = p_instance->p_libvlc_int->p_playlist;
236     libvlc_exception_t p_e_unused;
237
238     libvlc_event_remove_all_callbacks( p_instance, &p_e_unused );
239
240     if( !p_playlist )
241         RAISEVOID ("Can't unregister input events");
242
243     var_DelCallback( p_playlist, "playlist-current",
244                      install_input_event, p_instance );
245 }
246
247 /*
248  * Public libvlc functions
249  */
250
251 /**************************************************************************
252  *       libvlc_event_add_callback (public) :
253  *
254  * Add a callback for an event.
255  **************************************************************************/
256 void libvlc_event_add_callback( libvlc_instance_t *p_instance,
257                                 libvlc_event_type_t i_event_type,
258                                 libvlc_callback_t f_callback,
259                                 void *user_data,
260                                 libvlc_exception_t *p_e )
261 {
262     struct libvlc_callback_entry_t *entry;
263     vlc_value_t unused1, unused2;
264     int res = VLC_SUCCESS;
265
266     if ( !f_callback )
267         RAISEVOID (" Callback function is null ");
268
269     /* malloc/free strategy:
270      *  - alloc-ded in libvlc_event_add_callback
271      *  - free-ed by libvlc_event_add_callback on error
272      *  - free-ed by libvlc_event_remove_callback
273      *  - free-ed in libvlc_destroy threw libvlc_event_remove_callback
274      *    when entry is destroyed
275      */
276     entry = malloc( sizeof( struct libvlc_callback_entry_t ) );
277     entry->f_callback = f_callback;
278     entry->i_event_type = i_event_type;
279     entry->p_user_data = user_data;
280     
281     switch ( i_event_type )
282     {
283         case libvlc_VolumeChanged:
284             res = var_AddCallback( p_instance->p_libvlc_int, "volume-change",
285                            handle_event, entry );
286             break;
287         case libvlc_InputPositionChanged:
288             install_input_event( NULL, NULL, unused1, unused2, p_instance);
289             break;
290         default:
291             free( entry );
292             RAISEVOID( "Unsupported event." );
293     }
294
295     if (res != VLC_SUCCESS)
296     {
297         free ( entry );
298         RAISEVOID("Internal callback registration was not successful. Callback not registered.");
299     }
300     
301     vlc_mutex_lock( &p_instance->instance_lock );
302     add_callback_to_list( entry, &p_instance->p_callback_list );
303     vlc_mutex_unlock( &p_instance->instance_lock );
304
305     return;
306 }
307
308 /**************************************************************************
309  *       libvlc_event_remove_callback (public) :
310  *
311  * Remove a callback for an event.
312  **************************************************************************/
313 void libvlc_event_remove_callback( libvlc_instance_t *p_instance,
314                                    libvlc_event_type_t i_event_type,
315                                    libvlc_callback_t f_callback,
316                                    void *p_user_data,
317                                    libvlc_exception_t *p_e )
318 {
319     struct libvlc_callback_entry_list_t *p_listitem;
320
321     vlc_mutex_lock( &p_instance->instance_lock );
322
323     p_listitem = p_instance->p_callback_list;
324
325     while( p_listitem )
326     {
327         if( p_listitem->elmt->f_callback == f_callback
328             && ( p_listitem->elmt->i_event_type == i_event_type )
329             && ( p_listitem->elmt->p_user_data == p_user_data )
330         
331         )
332         {
333             remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We should warn on error */
334
335             if( p_listitem->prev )
336                 p_listitem->prev->next = p_listitem->next;
337             else
338                 p_instance->p_callback_list = p_listitem->next;
339
340
341             p_listitem->next->prev = p_listitem->prev;
342
343             free( p_listitem->elmt );
344
345             free( p_listitem );
346             break;
347         }
348         
349         p_listitem = p_listitem->next;
350     }
351     vlc_mutex_unlock( &p_instance->instance_lock );
352 }
353
354 /**************************************************************************
355  *       libvlc_event_remove_all_callbacks (public) :
356  *
357  * Remove all callbacks for all events.
358  **************************************************************************/
359 void libvlc_event_remove_all_callbacks( libvlc_instance_t *p_instance,
360                                        libvlc_exception_t *p_e )
361 {
362     struct libvlc_callback_entry_list_t *p_listitem;
363
364     vlc_mutex_lock( &p_instance->instance_lock );
365
366     p_listitem = p_instance->p_callback_list;
367
368     while( p_listitem )
369     {
370         remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We could warn on error */
371         p_listitem = p_listitem->next;
372
373     }
374     p_instance->p_callback_list = NULL;
375
376     vlc_mutex_unlock( &p_instance->instance_lock );
377 }