1 /*****************************************************************************
2 * events.c: events interface
3 * This library provides an interface to the send and receive events.
4 * It is more lightweight than variable based callback.
5 *****************************************************************************
6 * Copyright (C) 1998-2005 the VideoLAN team
9 * Authors: Pierre d'Herbemont <pdherbemont # videolan.org >
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include <vlc_common.h>
38 #include <vlc_events.h>
39 #include <vlc_arrays.h>
41 /*****************************************************************************
42 * Documentation : Read vlc_events.h
43 *****************************************************************************/
47 /*****************************************************************************
49 *****************************************************************************/
51 typedef struct vlc_event_listener_t
54 vlc_event_callback_t pf_callback
;
56 char * psz_debug_name
;
58 } vlc_event_listener_t
;
60 typedef struct vlc_event_listeners_group_t
62 vlc_event_type_t event_type
;
63 DECL_ARRAY(struct vlc_event_listener_t
*) listeners
;
65 /* Used in vlc_event_send() to make sure to behave
66 Correctly when vlc_event_detach was called during
68 bool b_sublistener_removed
;
70 } vlc_event_listeners_group_t
;
73 static const char ppsz_event_type_to_name
[][40] =
75 [vlc_InputStateChanged
] = "vlc_InputStateChanged",
76 [vlc_InputSelectedStreamChanged
] = "vlc_InputSelectedStreamChanged",
78 [vlc_InputItemMetaChanged
] = "vlc_InputItemMetaChanged",
79 [vlc_InputItemSubItemAdded
] = "vlc_InputItemSubItemAdded",
80 [vlc_InputItemDurationChanged
] = "vlc_InputItemDurationChanged",
81 [vlc_InputItemPreparsedChanged
] = "vlc_InputItemPreparsedChanged",
82 [vlc_InputItemNameChanged
] = "vlc_InputItemNameChanged",
83 [vlc_InputItemInfoChanged
] = "vlc_InputItemInfoChanged",
84 [vlc_InputItemErrorWhenReadingChanged
] = "vlc_InputItemErrorWhenReadingChanged",
86 [vlc_ServicesDiscoveryItemAdded
] = "vlc_ServicesDiscoveryItemAdded",
87 [vlc_ServicesDiscoveryItemRemoved
] = "vlc_ServicesDiscoveryItemRemoved",
88 [vlc_ServicesDiscoveryStarted
] = "vlc_ServicesDiscoveryStarted",
89 [vlc_ServicesDiscoveryEnded
] = "vlc_ServicesDiscoveryEnded"
94 listeners_are_equal( vlc_event_listener_t
* listener1
,
95 vlc_event_listener_t
* listener2
)
97 return listener1
->pf_callback
== listener2
->pf_callback
&&
98 listener1
->p_user_data
== listener2
->p_user_data
;
102 group_contains_listener( vlc_event_listeners_group_t
* group
,
103 vlc_event_listener_t
* searched_listener
)
105 vlc_event_listener_t
* listener
;
106 FOREACH_ARRAY( listener
, group
->listeners
)
107 if( listeners_are_equal(searched_listener
, listener
) )
113 /*****************************************************************************
115 *****************************************************************************/
117 #undef vlc_event_manager_init
119 * Initialize event manager object
120 * p_obj is the object that contains the event manager. But not
121 * necessarily a vlc_object_t (an input_item_t is not a vlc_object_t
123 * p_parent_obj gives a libvlc instance
125 int vlc_event_manager_init( vlc_event_manager_t
* p_em
, void * p_obj
,
126 vlc_object_t
* p_parent_obj
)
129 p_em
->p_parent_object
= p_parent_obj
;
130 vlc_mutex_init( &p_em
->object_lock
);
132 /* We need a recursive lock here, because we need to be able
133 * to call libvlc_event_detach even if vlc_event_send is in
135 * This ensures that after libvlc_event_detach, the callback
136 * will never gets triggered.
138 vlc_mutex_init_recursive( &p_em
->event_sending_lock
);
139 ARRAY_INIT( p_em
->listeners_groups
);
144 * Destroy the event manager
146 void vlc_event_manager_fini( vlc_event_manager_t
* p_em
)
148 struct vlc_event_listeners_group_t
* listeners_group
;
149 struct vlc_event_listener_t
* listener
;
151 vlc_mutex_destroy( &p_em
->object_lock
);
152 vlc_mutex_destroy( &p_em
->event_sending_lock
);
154 FOREACH_ARRAY( listeners_group
, p_em
->listeners_groups
)
155 FOREACH_ARRAY( listener
, listeners_group
->listeners
)
158 ARRAY_RESET( listeners_group
->listeners
);
159 free( listeners_group
);
161 ARRAY_RESET( p_em
->listeners_groups
);
165 * Register the event manager
167 int vlc_event_manager_register_event_type(
168 vlc_event_manager_t
* p_em
,
169 vlc_event_type_t event_type
)
171 vlc_event_listeners_group_t
* listeners_group
;
172 listeners_group
= malloc(sizeof(vlc_event_listeners_group_t
));
174 if( !listeners_group
)
177 listeners_group
->event_type
= event_type
;
178 ARRAY_INIT( listeners_group
->listeners
);
180 vlc_mutex_lock( &p_em
->object_lock
);
181 ARRAY_APPEND( p_em
->listeners_groups
, listeners_group
);
182 vlc_mutex_unlock( &p_em
->object_lock
);
188 * Send an event to the listener attached to this p_em.
190 void vlc_event_send( vlc_event_manager_t
* p_em
,
191 vlc_event_t
* p_event
)
193 vlc_event_listeners_group_t
* listeners_group
= NULL
;
194 vlc_event_listener_t
* listener
;
195 vlc_event_listener_t
* array_of_cached_listeners
= NULL
;
196 vlc_event_listener_t
* cached_listener
;
197 int i
, i_cached_listeners
= 0;
199 /* Fill event with the sending object now */
200 p_event
->p_obj
= p_em
->p_obj
;
202 vlc_mutex_lock( &p_em
->object_lock
);
203 FOREACH_ARRAY( listeners_group
, p_em
->listeners_groups
)
204 if( listeners_group
->event_type
== p_event
->type
)
206 if( listeners_group
->listeners
.i_size
<= 0 )
209 /* Save the function to call */
210 i_cached_listeners
= listeners_group
->listeners
.i_size
;
211 array_of_cached_listeners
= malloc(
212 sizeof(vlc_event_listener_t
)*i_cached_listeners
);
213 if( !array_of_cached_listeners
)
215 vlc_mutex_unlock( &p_em
->object_lock
);
219 cached_listener
= array_of_cached_listeners
;
220 FOREACH_ARRAY( listener
, listeners_group
->listeners
)
221 memcpy( cached_listener
, listener
, sizeof(vlc_event_listener_t
));
223 cached_listener
->psz_debug_name
= strdup(cached_listener
->psz_debug_name
);
231 vlc_mutex_unlock( &p_em
->object_lock
);
233 /* Call the function attached */
234 cached_listener
= array_of_cached_listeners
;
236 if( !listeners_group
|| !array_of_cached_listeners
)
238 free( array_of_cached_listeners
);
242 vlc_mutex_lock( &p_em
->event_sending_lock
) ;
244 /* Track item removed from *this* thread, with a simple flag */
245 listeners_group
->b_sublistener_removed
= false;
247 for( i
= 0; i
< i_cached_listeners
; i
++ )
250 msg_Dbg( p_em
->p_parent_object
,
251 "Calling '%s' with a '%s' event (data %p)",
252 cached_listener
->psz_debug_name
,
253 ppsz_event_type_to_name
[p_event
->type
],
254 cached_listener
->p_user_data
);
255 free(cached_listener
->psz_debug_name
);
257 /* No need to lock on listeners_group, a listener group can't be removed */
258 if( listeners_group
->b_sublistener_removed
)
260 /* If a callback was removed, this gets called */
262 vlc_mutex_lock( &p_em
->object_lock
);
263 valid_listener
= group_contains_listener( listeners_group
, cached_listener
);
264 vlc_mutex_unlock( &p_em
->object_lock
);
265 if( !valid_listener
)
268 msg_Dbg( p_em
->p_parent_object
, "Callback was removed during execution" );
274 cached_listener
->pf_callback( p_event
, cached_listener
->p_user_data
);
277 vlc_mutex_unlock( &p_em
->event_sending_lock
);
279 free( array_of_cached_listeners
);
282 #undef vlc_event_attach
284 * Add a callback for an event.
286 int vlc_event_attach( vlc_event_manager_t
* p_em
,
287 vlc_event_type_t event_type
,
288 vlc_event_callback_t pf_callback
,
290 const char * psz_debug_name
)
292 vlc_event_listeners_group_t
* listeners_group
;
293 vlc_event_listener_t
* listener
;
294 listener
= malloc(sizeof(vlc_event_listener_t
));
298 listener
->p_user_data
= p_user_data
;
299 listener
->pf_callback
= pf_callback
;
301 listener
->psz_debug_name
= strdup( psz_debug_name
);
303 (void)psz_debug_name
;
306 vlc_mutex_lock( &p_em
->object_lock
);
307 FOREACH_ARRAY( listeners_group
, p_em
->listeners_groups
)
308 if( listeners_group
->event_type
== event_type
)
310 ARRAY_APPEND( listeners_group
->listeners
, listener
);
312 msg_Dbg( p_em
->p_parent_object
,
313 "Listening to '%s' event with '%s' (data %p)",
314 ppsz_event_type_to_name
[event_type
],
315 listener
->psz_debug_name
,
316 listener
->p_user_data
);
318 vlc_mutex_unlock( &p_em
->object_lock
);
322 vlc_mutex_unlock( &p_em
->object_lock
);
324 msg_Err( p_em
->p_parent_object
, "cannot attach to an object event" );
330 * Remove a callback for an event.
333 int vlc_event_detach( vlc_event_manager_t
*p_em
,
334 vlc_event_type_t event_type
,
335 vlc_event_callback_t pf_callback
,
338 vlc_event_listeners_group_t
* listeners_group
;
339 struct vlc_event_listener_t
* listener
;
341 vlc_mutex_lock( &p_em
->object_lock
);
342 vlc_mutex_lock( &p_em
->event_sending_lock
);
343 FOREACH_ARRAY( listeners_group
, p_em
->listeners_groups
)
344 if( listeners_group
->event_type
== event_type
)
346 FOREACH_ARRAY( listener
, listeners_group
->listeners
)
347 if( listener
->pf_callback
== pf_callback
&&
348 listener
->p_user_data
== p_user_data
)
350 /* Tell vlc_event_send, we did remove an item from that group,
351 in case vlc_event_send is in our caller stack */
352 listeners_group
->b_sublistener_removed
= true;
354 /* that's our listener */
355 ARRAY_REMOVE( listeners_group
->listeners
,
356 fe_idx
/* This comes from the macro (and that's why
359 msg_Dbg( p_em
->p_parent_object
,
360 "Detaching '%s' from '%s' event (data %p)",
361 listener
->psz_debug_name
,
362 ppsz_event_type_to_name
[event_type
],
363 listener
->p_user_data
);
365 free( listener
->psz_debug_name
);
368 vlc_mutex_unlock( &p_em
->event_sending_lock
);
369 vlc_mutex_unlock( &p_em
->object_lock
);
375 vlc_mutex_unlock( &p_em
->event_sending_lock
);
376 vlc_mutex_unlock( &p_em
->object_lock
);
378 msg_Warn( p_em
->p_parent_object
, "cannot detach from an object event" );