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