Merge ../mlt
[melted] / src / framework / mlt_events.c
1 /**
2 * \file mlt_events.c
3 * \brief event handling
4 * \see mlt_events_struct
5 *
6 * Copyright (C) 2004-2009 Ushodaya Enterprises Limited
7 * \author Charles Yates <charles.yates@pandora.be>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library 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 GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <pthread.h>
28
29 #include "mlt_properties.h"
30 #include "mlt_events.h"
31
32 /* Memory leak checks. */
33
34 #undef _MLT_EVENT_CHECKS_
35
36 #ifdef _MLT_EVENT_CHECKS_
37 static int events_created = 0;
38 static int events_destroyed = 0;
39 #endif
40
41 /** \brief Events class
42 *
43 * Events provide messages and notifications between services and the application.
44 * A service can register an event and fire/send it upon certain conditions or times.
45 * Likewise, a service or an application can listen/receive specific events on specific
46 * services.
47 */
48
49 struct mlt_events_struct
50 {
51 mlt_properties owner;
52 mlt_properties list;
53 };
54
55 typedef struct mlt_events_struct *mlt_events;
56
57 /** \brief Event class
58 *
59 */
60
61 struct mlt_event_struct
62 {
63 mlt_events owner;
64 int ref_count;
65 int block_count;
66 mlt_listener listener;
67 void *service;
68 };
69
70 /** Increment the reference count on this event.
71 *
72 * \public \memberof mlt_event_struct
73 * \param this an event
74 */
75
76 void mlt_event_inc_ref( mlt_event this )
77 {
78 if ( this != NULL )
79 this->ref_count ++;
80 }
81
82 /** Increment the block count on this event.
83 *
84 * \public \memberof mlt_event_struct
85 * \param this an event
86 */
87
88 void mlt_event_block( mlt_event this )
89 {
90 if ( this != NULL && this->owner != NULL )
91 this->block_count ++;
92 }
93
94 /** Decrement the block count on this event.
95 *
96 * \public \memberof mlt_event_struct
97 * \param this an event
98 */
99
100 void mlt_event_unblock( mlt_event this )
101 {
102 if ( this != NULL && this->owner != NULL )
103 this->block_count --;
104 }
105
106 /** Close this event.
107 *
108 * \public \memberof mlt_event_struct
109 * \param this an event
110 */
111
112 void mlt_event_close( mlt_event this )
113 {
114 if ( this != NULL )
115 {
116 if ( -- this->ref_count == 1 )
117 this->owner = NULL;
118 if ( this->ref_count <= 0 )
119 {
120 #ifdef _MLT_EVENT_CHECKS_
121 mlt_log( NULL, MLT_LOG_DEBUG, "Events created %d, destroyed %d\n", events_created, ++events_destroyed );
122 #endif
123 free( this );
124 }
125 }
126 }
127
128 /* Forward declaration to private functions.
129 */
130
131 static mlt_events mlt_events_fetch( mlt_properties );
132 static void mlt_events_store( mlt_properties, mlt_events );
133 static void mlt_events_close( mlt_events );
134
135 /** Initialise the events structure.
136 *
137 * \public \memberof mlt_events_struct
138 * \param this a properties list
139 */
140
141 void mlt_events_init( mlt_properties this )
142 {
143 mlt_events events = mlt_events_fetch( this );
144 if ( events == NULL )
145 {
146 events = malloc( sizeof( struct mlt_events_struct ) );
147 events->list = mlt_properties_new( );
148 mlt_events_store( this, events );
149 }
150 }
151
152 /** Register an event and transmitter.
153 *
154 * \public \memberof mlt_events_struct
155 * \param this a properties list
156 * \param id the name of an event
157 * \param transmitter the callback function to send an event message
158 * \return true if there was an error
159 */
160
161 int mlt_events_register( mlt_properties this, const char *id, mlt_transmitter transmitter )
162 {
163 int error = 1;
164 mlt_events events = mlt_events_fetch( this );
165 if ( events != NULL )
166 {
167 mlt_properties list = events->list;
168 char temp[ 128 ];
169 error = mlt_properties_set_data( list, id, transmitter, 0, NULL, NULL );
170 sprintf( temp, "list:%s", id );
171 if ( mlt_properties_get_data( list, temp, NULL ) == NULL )
172 mlt_properties_set_data( list, temp, mlt_properties_new( ), 0, ( mlt_destructor )mlt_properties_close, NULL );
173 }
174 return error;
175 }
176
177 /** Fire an event.
178 *
179 * This takes a variable number of arguments to supply to the listener.
180
181 * \public \memberof mlt_events_struct
182 * \param this a properties list
183 * \param id the name of an event
184 */
185
186 void mlt_events_fire( mlt_properties this, const char *id, ... )
187 {
188 mlt_events events = mlt_events_fetch( this );
189 if ( events != NULL )
190 {
191 int i = 0;
192 va_list alist;
193 void *args[ 10 ];
194 mlt_properties list = events->list;
195 mlt_properties listeners = NULL;
196 char temp[ 128 ];
197 mlt_transmitter transmitter = mlt_properties_get_data( list, id, NULL );
198 sprintf( temp, "list:%s", id );
199 listeners = mlt_properties_get_data( list, temp, NULL );
200
201 va_start( alist, id );
202 do
203 args[ i ] = va_arg( alist, void * );
204 while( args[ i ++ ] != NULL );
205 va_end( alist );
206
207 if ( listeners != NULL )
208 {
209 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
210 {
211 mlt_event event = mlt_properties_get_data_at( listeners, i, NULL );
212 if ( event != NULL && event->owner != NULL && event->block_count == 0 )
213 {
214 if ( transmitter != NULL )
215 transmitter( event->listener, event->owner, event->service, args );
216 else
217 event->listener( event->owner, event->service );
218 }
219 }
220 }
221 }
222 }
223
224 /** Register a listener.
225 *
226 * \public \memberof mlt_events_struct
227 * \param this a properties list
228 * \param service an opaque pointer
229 * \param id the name of the event to listen for
230 * \param listener the callback to receive an event message
231 * \return
232 */
233
234 mlt_event mlt_events_listen( mlt_properties this, void *service, const char *id, mlt_listener listener )
235 {
236 mlt_event event = NULL;
237 mlt_events events = mlt_events_fetch( this );
238 if ( events != NULL )
239 {
240 mlt_properties list = events->list;
241 mlt_properties listeners = NULL;
242 char temp[ 128 ];
243 sprintf( temp, "list:%s", id );
244 listeners = mlt_properties_get_data( list, temp, NULL );
245 if ( listeners != NULL )
246 {
247 int first_null = -1;
248 int i = 0;
249 for ( i = 0; event == NULL && i < mlt_properties_count( listeners ); i ++ )
250 {
251 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
252 if ( entry != NULL && entry->owner != NULL )
253 {
254 if ( entry->service == service && entry->listener == listener )
255 event = entry;
256 }
257 else if ( ( entry == NULL || entry->owner == NULL ) && first_null == -1 )
258 {
259 first_null = i;
260 }
261 }
262
263 if ( event == NULL )
264 {
265 event = malloc( sizeof( struct mlt_event_struct ) );
266 if ( event != NULL )
267 {
268 #ifdef _MLT_EVENT_CHECKS_
269 events_created ++;
270 #endif
271 sprintf( temp, "%d", first_null == -1 ? mlt_properties_count( listeners ) : first_null );
272 event->owner = events;
273 event->ref_count = 0;
274 event->block_count = 0;
275 event->listener = listener;
276 event->service = service;
277 mlt_properties_set_data( listeners, temp, event, 0, ( mlt_destructor )mlt_event_close, NULL );
278 mlt_event_inc_ref( event );
279 }
280 }
281
282 }
283 }
284 return event;
285 }
286
287 /** Block all events for a given service.
288 *
289 * \public \memberof mlt_events_struct
290 * \param this a properties list
291 * \param service an opaque pointer
292 */
293
294 void mlt_events_block( mlt_properties this, void *service )
295 {
296 mlt_events events = mlt_events_fetch( this );
297 if ( events != NULL )
298 {
299 int i = 0, j = 0;
300 mlt_properties list = events->list;
301 for ( j = 0; j < mlt_properties_count( list ); j ++ )
302 {
303 char *temp = mlt_properties_get_name( list, j );
304 if ( !strncmp( temp, "list:", 5 ) )
305 {
306 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
307 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
308 {
309 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
310 if ( entry != NULL && entry->service == service )
311 mlt_event_block( entry );
312 }
313 }
314 }
315 }
316 }
317
318 /** Unblock all events for a given service.
319 *
320 * \public \memberof mlt_events_struct
321 * \param this a properties list
322 * \param service an opaque pointer
323 */
324
325 void mlt_events_unblock( mlt_properties this, void *service )
326 {
327 mlt_events events = mlt_events_fetch( this );
328 if ( events != NULL )
329 {
330 int i = 0, j = 0;
331 mlt_properties list = events->list;
332 for ( j = 0; j < mlt_properties_count( list ); j ++ )
333 {
334 char *temp = mlt_properties_get_name( list, j );
335 if ( !strncmp( temp, "list:", 5 ) )
336 {
337 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
338 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
339 {
340 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
341 if ( entry != NULL && entry->service == service )
342 mlt_event_unblock( entry );
343 }
344 }
345 }
346 }
347 }
348
349 /** Disconnect all events for a given service.
350 *
351 * \public \memberof mlt_events_struct
352 * \param this a properties list
353 * \param service an opaque pointer
354 */
355
356 void mlt_events_disconnect( mlt_properties this, void *service )
357 {
358 mlt_events events = mlt_events_fetch( this );
359 if ( events != NULL )
360 {
361 int i = 0, j = 0;
362 mlt_properties list = events->list;
363 for ( j = 0; j < mlt_properties_count( list ); j ++ )
364 {
365 char *temp = mlt_properties_get_name( list, j );
366 if ( !strncmp( temp, "list:", 5 ) )
367 {
368 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
369 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
370 {
371 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
372 char *name = mlt_properties_get_name( listeners, i );
373 if ( entry != NULL && entry->service == service )
374 mlt_properties_set_data( listeners, name, NULL, 0, NULL, NULL );
375 }
376 }
377 }
378 }
379 }
380
381 /** \brief private to mlt_events_struct, used by mlt_events_wait_for() */
382
383 typedef struct
384 {
385 int done;
386 pthread_cond_t cond;
387 pthread_mutex_t mutex;
388 }
389 condition_pair;
390
391 /** The event listener callback for the wait functions.
392 *
393 * \private \memberof mlt_events_struct
394 * \param this a properties list
395 * \param pair a condition pair
396 */
397
398 static void mlt_events_listen_for( mlt_properties this, condition_pair *pair )
399 {
400 pthread_mutex_lock( &pair->mutex );
401 if ( pair->done == 0 )
402 {
403 pthread_cond_signal( &pair->cond );
404 pthread_mutex_unlock( &pair->mutex );
405 }
406 }
407
408 /** Prepare to wait for an event.
409 *
410 * \public \memberof mlt_events_struct
411 * \param this a properties list
412 * \param id the name of the event to wait for
413 * \return an event
414 */
415
416 mlt_event mlt_events_setup_wait_for( mlt_properties this, const char *id )
417 {
418 condition_pair *pair = malloc( sizeof( condition_pair ) );
419 pair->done = 0;
420 pthread_cond_init( &pair->cond, NULL );
421 pthread_mutex_init( &pair->mutex, NULL );
422 pthread_mutex_lock( &pair->mutex );
423 return mlt_events_listen( this, pair, id, ( mlt_listener )mlt_events_listen_for );
424 }
425
426 /** Wait for an event.
427 *
428 * \public \memberof mlt_events_struct
429 * \param this a properties list
430 * \param event an event
431 */
432
433 void mlt_events_wait_for( mlt_properties this, mlt_event event )
434 {
435 if ( event != NULL )
436 {
437 condition_pair *pair = event->service;
438 pthread_cond_wait( &pair->cond, &pair->mutex );
439 }
440 }
441
442 /** Cleanup after waiting for an event.
443 *
444 * \public \memberof mlt_events_struct
445 * \param this a properties list
446 * \param event an event
447 */
448
449 void mlt_events_close_wait_for( mlt_properties this, mlt_event event )
450 {
451 if ( event != NULL )
452 {
453 condition_pair *pair = event->service;
454 event->owner = NULL;
455 pair->done = 0;
456 pthread_mutex_unlock( &pair->mutex );
457 pthread_mutex_destroy( &pair->mutex );
458 pthread_cond_destroy( &pair->cond );
459 }
460 }
461
462 /** Fetch the events object.
463 *
464 * \private \memberof mlt_events_struct
465 * \param this a properties list
466 * \return an events object
467 */
468
469 static mlt_events mlt_events_fetch( mlt_properties this )
470 {
471 mlt_events events = NULL;
472 if ( this != NULL )
473 events = mlt_properties_get_data( this, "_events", NULL );
474 return events;
475 }
476
477 /** Store the events object.
478 *
479 * \private \memberof mlt_events_struct
480 * \param this a properties list
481 * \param events an events object
482 */
483
484 static void mlt_events_store( mlt_properties this, mlt_events events )
485 {
486 if ( this != NULL && events != NULL )
487 mlt_properties_set_data( this, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL );
488 }
489
490 /** Close the events object.
491 *
492 * \private \memberof mlt_events_struct
493 * \param events an events object
494 */
495
496 static void mlt_events_close( mlt_events events )
497 {
498 if ( events != NULL )
499 {
500 mlt_properties_close( events->list );
501 free( events );
502 }
503 }