]> git.sesse.net Git - vlc/blob - src/control/event.c
libvlc event: Make event function thread safe. (And fix a mutex leak)
[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  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include "libvlc_internal.h"
25 #include <vlc/libvlc.h>
26
27
28 /*
29  * Private functions
30  */
31
32 static int handle_event( vlc_object_t *p_this, char const *psz_cmd,
33                          vlc_value_t oldval, vlc_value_t newval,
34                          void *p_data )
35 {
36     /* This is thread safe, as the var_*Callback already provide the locking
37      * facility for p_data */
38     struct libvlc_callback_entry_t *entry = p_data;
39     libvlc_event_t event;
40     event.type = entry->i_event_type;
41     switch ( event.type )
42     {
43         case VOLUME_CHANGED:
44             event.value_type = BOOLEAN_EVENT;
45             break;
46         case INPUT_POSITION_CHANGED:
47             break;
48         default:
49             break;
50     }
51     event.old_value = oldval;
52     event.new_value = newval;
53
54     /* Call the client entry */
55     entry->f_callback( entry->p_instance, &event, entry->p_user_data );
56
57     return VLC_SUCCESS;
58 }
59
60 static inline void add_callback_to_list( struct libvlc_callback_entry_t *entry,
61                                          struct libvlc_callback_entry_list_t **list )
62 {
63     struct libvlc_callback_entry_list_t *new_listitem;
64
65     /* malloc/free strategy:
66      *  - alloc-ded in add_callback_entry
67      *  - free-ed by libvlc_event_remove_callback
68      *  - free-ed in libvlc_destroy threw libvlc_event_remove_callback
69      *    when entry is destroyed
70      */
71     new_listitem = malloc( sizeof( struct libvlc_callback_entry_list_t ) );
72     new_listitem->elmt = entry;
73     new_listitem->next = *list;
74     new_listitem->prev = NULL;
75
76     if(*list)
77         (*list)->prev = new_listitem;
78
79     *list = new_listitem;
80 }
81
82 static int remove_variable_callback( libvlc_instance_t *p_instance, 
83                                      struct libvlc_callback_entry_t * p_entry )
84 {
85     const char * callback_name = NULL;
86     
87     /* Note: Appropriate lock should be held by the caller */
88
89     switch ( p_entry->i_event_type )
90     {
91         case VOLUME_CHANGED:
92             callback_name = "volume-change";
93             break;
94         case INPUT_POSITION_CHANGED:
95             break;
96     }
97
98     if (!callback_name)
99         return VLC_EGENERIC;
100
101     return var_DelCallback( p_instance->p_libvlc_int,
102                             callback_name, handle_event,
103                             p_entry );
104 }
105
106 /*
107  * Public libvlc functions
108  */
109
110 void libvlc_event_add_callback( libvlc_instance_t *p_instance,
111                                 libvlc_event_type_t i_event_type,
112                                 libvlc_callback_t f_callback,
113                                 void *user_data,
114                                 libvlc_exception_t *p_e )
115 {
116     struct libvlc_callback_entry_t *entry;
117     const char * callback_name = NULL;
118     int res;
119
120     if ( !f_callback )
121         RAISEVOID (" Callback function is null ");
122
123     /* malloc/free strategy:
124      *  - alloc-ded in libvlc_event_add_callback
125      *  - free-ed by libvlc_event_add_callback on error
126      *  - free-ed by libvlc_event_remove_callback
127      *  - free-ed in libvlc_destroy threw libvlc_event_remove_callback
128      *    when entry is destroyed
129      */
130     entry = malloc( sizeof( struct libvlc_callback_entry_t ) );
131     entry->f_callback = f_callback;
132     entry->i_event_type = i_event_type;
133     entry->p_user_data = user_data;
134     
135     switch ( i_event_type )
136     {
137         case VOLUME_CHANGED:
138             callback_name = "volume-change";
139             break;
140         case INPUT_POSITION_CHANGED:
141             break;
142         default:
143             free( entry );
144             RAISEVOID( "Unsupported event." );
145     }
146
147     res = var_AddCallback( p_instance->p_libvlc_int,
148                            callback_name,
149                            handle_event,
150                            entry );
151     
152     if (res != VLC_SUCCESS)
153     {
154         free ( entry );
155         RAISEVOID("Internal callback registration was not successful. Callback not registered.");
156     }
157     
158     vlc_mutex_lock( &p_instance->instance_lock );
159     add_callback_to_list( entry, &p_instance->p_callback_list );
160     vlc_mutex_unlock( &p_instance->instance_lock );
161
162     return;
163 }
164
165 void libvlc_event_remove_all_callbacks( libvlc_instance_t *p_instance,
166                                        libvlc_exception_t *p_e )
167 {
168     struct libvlc_callback_entry_list_t *p_listitem;
169
170     vlc_mutex_lock( &p_instance->instance_lock );
171
172     p_listitem = p_instance->p_callback_list;
173
174     while( p_listitem )
175     {
176         remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We could warn on error */
177         p_listitem = p_listitem->next;
178
179     }
180     p_instance->p_callback_list = NULL;
181
182     vlc_mutex_unlock( &p_instance->instance_lock );
183 }
184
185 void libvlc_event_remove_callback( libvlc_instance_t *p_instance,
186                                    libvlc_event_type_t i_event_type,
187                                    libvlc_callback_t f_callback,
188                                    void *p_user_data,
189                                    libvlc_exception_t *p_e )
190 {
191     struct libvlc_callback_entry_list_t *p_listitem;
192
193     vlc_mutex_lock( &p_instance->instance_lock );
194
195     p_listitem = p_instance->p_callback_list;
196
197     while( p_listitem )
198     {
199         if( p_listitem->elmt->f_callback == f_callback
200             && ( p_listitem->elmt->i_event_type == i_event_type )
201             && ( p_listitem->elmt->p_user_data == p_user_data )
202         
203         )
204         {
205             remove_variable_callback( p_instance, p_listitem->elmt ); /* FIXME: We should warn on error */
206
207             if( p_listitem->prev )
208                 p_listitem->prev->next = p_listitem->next;
209             else
210                 p_instance->p_callback_list = p_listitem->next;
211
212
213             p_listitem->next->prev = p_listitem->prev;
214
215             free( p_listitem->elmt );
216
217             free( p_listitem );
218             break;
219         }
220         
221         p_listitem = p_listitem->next;
222     }
223     vlc_mutex_unlock( &p_instance->instance_lock );
224 }