]> git.sesse.net Git - vlc/blob - src/control/event.c
- libvlc APIs: bug fixing, and please note that exception argument is OPTIONAL (can...
[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     if( !p_playlist )
218         RAISEVOID ("Can't listen to input event");
219
220     /* Install a Callback for input changes, so
221      * so we can track input event */
222      var_AddCallback( p_playlist, "playlist-current",
223                       install_input_event, p_instance );
224 }
225
226 /**************************************************************************
227  *       libvlc_event_fini (internal) :
228  *
229  * finalization function.
230  **************************************************************************/
231 void libvlc_event_fini( libvlc_instance_t *p_instance, libvlc_exception_t *p_e )
232 {
233     playlist_t *p_playlist = p_instance->p_libvlc_int->p_playlist;
234     libvlc_exception_t p_e_unused;
235
236     libvlc_event_remove_all_callbacks( p_instance, &p_e_unused );
237
238     if( !p_playlist )
239         RAISEVOID ("Can't unregister input events");
240
241     var_DelCallback( p_playlist, "playlist-current",
242                      install_input_event, p_instance );
243 }
244
245 /*
246  * Public libvlc functions
247  */
248
249 /**************************************************************************
250  *       libvlc_event_add_callback (public) :
251  *
252  * Add a callback for an event.
253  **************************************************************************/
254 void libvlc_event_add_callback( libvlc_instance_t *p_instance,
255                                 libvlc_event_type_t i_event_type,
256                                 libvlc_callback_t f_callback,
257                                 void *user_data,
258                                 libvlc_exception_t *p_e )
259 {
260     struct libvlc_callback_entry_t *entry;
261     vlc_value_t unused1, unused2;
262     int res = VLC_SUCCESS;
263
264     if ( !f_callback )
265         RAISEVOID (" Callback function is null ");
266
267     /* malloc/free strategy:
268      *  - alloc-ded in libvlc_event_add_callback
269      *  - free-ed by libvlc_event_add_callback on error
270      *  - free-ed by libvlc_event_remove_callback
271      *  - free-ed in libvlc_destroy threw libvlc_event_remove_callback
272      *    when entry is destroyed
273      */
274     entry = malloc( sizeof( struct libvlc_callback_entry_t ) );
275     entry->f_callback = f_callback;
276     entry->i_event_type = i_event_type;
277     entry->p_user_data = user_data;
278     
279     switch ( i_event_type )
280     {
281         case libvlc_VolumeChanged:
282             res = var_AddCallback( p_instance->p_libvlc_int, "volume-change",
283                            handle_event, entry );
284             break;
285         case libvlc_InputPositionChanged:
286             install_input_event( NULL, NULL, unused1, unused2, p_instance);
287             break;
288         default:
289             free( entry );
290             RAISEVOID( "Unsupported event." );
291     }
292
293     if (res != VLC_SUCCESS)
294     {
295         free ( entry );
296         RAISEVOID("Internal callback registration was not successful. Callback not registered.");
297     }
298     
299     vlc_mutex_lock( &p_instance->instance_lock );
300     add_callback_to_list( entry, &p_instance->p_callback_list );
301     vlc_mutex_unlock( &p_instance->instance_lock );
302
303     return;
304 }
305
306 /**************************************************************************
307  *       libvlc_event_remove_callback (public) :
308  *
309  * Remove a callback for an event.
310  **************************************************************************/
311 void libvlc_event_remove_callback( libvlc_instance_t *p_instance,
312                                    libvlc_event_type_t i_event_type,
313                                    libvlc_callback_t f_callback,
314                                    void *p_user_data,
315                                    libvlc_exception_t *p_e )
316 {
317     struct libvlc_callback_entry_list_t *p_listitem;
318
319     vlc_mutex_lock( &p_instance->instance_lock );
320
321     p_listitem = p_instance->p_callback_list;
322
323     while( p_listitem )
324     {
325         if( p_listitem->elmt->f_callback == f_callback
326             && ( p_listitem->elmt->i_event_type == i_event_type )
327             && ( p_listitem->elmt->p_user_data == p_user_data )
328         
329         )
330         {
331             remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We should warn on error */
332
333             if( p_listitem->prev )
334                 p_listitem->prev->next = p_listitem->next;
335             else
336                 p_instance->p_callback_list = p_listitem->next;
337
338
339             p_listitem->next->prev = p_listitem->prev;
340
341             free( p_listitem->elmt );
342
343             free( p_listitem );
344             break;
345         }
346         
347         p_listitem = p_listitem->next;
348     }
349     vlc_mutex_unlock( &p_instance->instance_lock );
350 }
351
352 /**************************************************************************
353  *       libvlc_event_remove_all_callbacks (public) :
354  *
355  * Remove all callbacks for all events.
356  **************************************************************************/
357 void libvlc_event_remove_all_callbacks( libvlc_instance_t *p_instance,
358                                        libvlc_exception_t *p_e )
359 {
360     struct libvlc_callback_entry_list_t *p_listitem;
361
362     vlc_mutex_lock( &p_instance->instance_lock );
363
364     p_listitem = p_instance->p_callback_list;
365
366     while( p_listitem )
367     {
368         remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We could warn on error */
369         p_listitem = p_listitem->next;
370
371     }
372     p_instance->p_callback_list = NULL;
373
374     vlc_mutex_unlock( &p_instance->instance_lock );
375 }