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