]> git.sesse.net Git - mlt/blob - src/framework/mlt_events.c
A little debugging.
[mlt] / 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 self event.
71  *
72  * \public \memberof mlt_event_struct
73  * \param self an event
74  */
75
76 void mlt_event_inc_ref( mlt_event self )
77 {
78         if ( self != NULL )
79                 self->ref_count ++;
80 }
81
82 /** Increment the block count on self event.
83  *
84  * \public \memberof mlt_event_struct
85  * \param self an event
86  */
87
88 void mlt_event_block( mlt_event self )
89 {
90         if ( self != NULL && self->owner != NULL )
91                 self->block_count ++;
92 }
93
94 /** Decrement the block count on self event.
95  *
96  * \public \memberof mlt_event_struct
97  * \param self an event
98  */
99
100 void mlt_event_unblock( mlt_event self )
101 {
102         if ( self != NULL && self->owner != NULL )
103                 self->block_count --;
104 }
105
106 /** Close self event.
107  *
108  * \public \memberof mlt_event_struct
109  * \param self an event
110  */
111
112 void mlt_event_close( mlt_event self )
113 {
114         if ( self != NULL )
115         {
116                 if ( -- self->ref_count == 1 )
117                         self->owner = NULL;
118                 if ( self->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( self );
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 self a properties list
139  */
140
141 void mlt_events_init( mlt_properties self )
142 {
143         mlt_events events = mlt_events_fetch( self );
144         if ( events == NULL )
145         {
146                 events = calloc( 1, sizeof( struct mlt_events_struct ) );
147                 events->list = mlt_properties_new( );
148                 mlt_events_store( self, events );
149         }
150 }
151
152 /** Register an event and transmitter.
153  *
154  * \public \memberof mlt_events_struct
155  * \param self 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 self, const char *id, mlt_transmitter transmitter )
162 {
163         int error = 1;
164         mlt_events events = mlt_events_fetch( self );
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 self a properties list
183  * \param id the name of an event
184  * \return the number of listeners
185  */
186
187 int mlt_events_fire( mlt_properties self, const char *id, ... )
188 {
189         int result = 0;
190         mlt_events events = mlt_events_fetch( self );
191         if ( events != NULL )
192         {
193                 int i = 0;
194                 va_list alist;
195                 void *args[ 10 ];
196                 mlt_properties list = events->list;
197                 mlt_properties listeners = NULL;
198                 char temp[ 128 ];
199                 mlt_transmitter transmitter = mlt_properties_get_data( list, id, NULL );
200                 sprintf( temp, "list:%s", id );
201                 listeners = mlt_properties_get_data( list, temp, NULL );
202
203                 va_start( alist, id );
204                 do
205                         args[ i ] = va_arg( alist, void * );
206                 while( args[ i ++ ] != NULL );
207                 va_end( alist );
208
209                 if ( listeners != NULL )
210                 {
211                         for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
212                         {
213                                 mlt_event event = mlt_properties_get_data_at( listeners, i, NULL );
214                                 if ( event != NULL && event->owner != NULL && event->block_count == 0 )
215                                 {
216                                         if ( transmitter != NULL )
217                                                 transmitter( event->listener, event->owner, event->service, args );
218                                         else
219                                                 event->listener( event->owner, event->service );
220                                         ++result;
221                                 }
222                         }
223                 }
224         }
225         return result;
226 }
227
228 /** Register a listener.
229  *
230  * \public \memberof mlt_events_struct
231  * \param self a properties list
232  * \param service an opaque pointer
233  * \param id the name of the event to listen for
234  * \param listener the callback to receive an event message
235  * \return
236  */
237
238 mlt_event mlt_events_listen( mlt_properties self, void *service, const char *id, mlt_listener listener )
239 {
240         mlt_event event = NULL;
241         mlt_events events = mlt_events_fetch( self );
242         if ( events != NULL )
243         {
244                 mlt_properties list = events->list;
245                 mlt_properties listeners = NULL;
246                 char temp[ 128 ];
247                 sprintf( temp, "list:%s", id );
248                 listeners = mlt_properties_get_data( list, temp, NULL );
249                 if ( listeners != NULL )
250                 {
251                         int first_null = -1;
252                         int i = 0;
253                         for ( i = 0; event == NULL && i < mlt_properties_count( listeners ); i ++ )
254                         {
255                                 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
256                                 if ( entry != NULL && entry->owner != NULL )
257                                 {
258                                         if ( entry->service == service && entry->listener == listener )
259                                                 event = entry;
260                                 }
261                                 else if ( ( entry == NULL || entry->owner == NULL ) && first_null == -1 )
262                                 {
263                                         first_null = i;
264                                 }
265                         }
266
267                         if ( event == NULL )
268                         {
269                                 event = malloc( sizeof( struct mlt_event_struct ) );
270                                 if ( event != NULL )
271                                 {
272 #ifdef _MLT_EVENT_CHECKS_
273                                         events_created ++;
274 #endif
275                                         sprintf( temp, "%d", first_null == -1 ? mlt_properties_count( listeners ) : first_null );
276                                         event->owner = events;
277                                         event->ref_count = 0;
278                                         event->block_count = 0;
279                                         event->listener = listener;
280                                         event->service = service;
281                                         mlt_properties_set_data( listeners, temp, event, 0, ( mlt_destructor )mlt_event_close, NULL );
282                                         mlt_event_inc_ref( event );
283                                 }
284                         }
285
286                 }
287         }
288         return event;
289 }
290
291 /** Block all events for a given service.
292  *
293  * \public \memberof mlt_events_struct
294  * \param self a properties list
295  * \param service an opaque pointer
296  */
297
298 void mlt_events_block( mlt_properties self, void *service )
299 {
300         mlt_events events = mlt_events_fetch( self );
301         if ( events != NULL )
302         {
303                 int i = 0, j = 0;
304                 mlt_properties list = events->list;
305                 for ( j = 0; j < mlt_properties_count( list ); j ++ )
306                 {
307                         char *temp = mlt_properties_get_name( list, j );
308                         if ( !strncmp( temp, "list:", 5 ) )
309                         {
310                                 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
311                                 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
312                                 {
313                                         mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
314                                         if ( entry != NULL && entry->service == service )
315                                                 mlt_event_block( entry );
316                                 }
317                         }
318                 }
319         }
320 }
321
322 /** Unblock all events for a given service.
323  *
324  * \public \memberof mlt_events_struct
325  * \param self a properties list
326  * \param service an opaque pointer
327  */
328
329 void mlt_events_unblock( mlt_properties self, void *service )
330 {
331         mlt_events events = mlt_events_fetch( self );
332         if ( events != NULL )
333         {
334                 int i = 0, j = 0;
335                 mlt_properties list = events->list;
336                 for ( j = 0; j < mlt_properties_count( list ); j ++ )
337                 {
338                         char *temp = mlt_properties_get_name( list, j );
339                         if ( !strncmp( temp, "list:", 5 ) )
340                         {
341                                 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
342                                 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
343                                 {
344                                         mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
345                                         if ( entry != NULL && entry->service == service )
346                                                 mlt_event_unblock( entry );
347                                 }
348                         }
349                 }
350         }
351 }
352
353 /** Disconnect all events for a given service.
354  *
355  * \public \memberof mlt_events_struct
356  * \param self a properties list
357  * \param service an opaque pointer
358  */
359
360 void mlt_events_disconnect( mlt_properties self, void *service )
361 {
362         mlt_events events = mlt_events_fetch( self );
363         if ( events != NULL )
364         {
365                 int i = 0, j = 0;
366                 mlt_properties list = events->list;
367                 for ( j = 0; j < mlt_properties_count( list ); j ++ )
368                 {
369                         char *temp = mlt_properties_get_name( list, j );
370                         if ( !strncmp( temp, "list:", 5 ) )
371                         {
372                                 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
373                                 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
374                                 {
375                                         mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
376                                         char *name = mlt_properties_get_name( listeners, i );
377                                         if ( entry != NULL && entry->service == service )
378                                                 mlt_properties_set_data( listeners, name, NULL, 0, NULL, NULL );
379                                 }
380                         }
381                 }
382         }
383 }
384
385 /** \brief private to mlt_events_struct, used by mlt_events_wait_for() */
386
387 typedef struct
388 {
389         pthread_cond_t cond;
390         pthread_mutex_t mutex;
391 }
392 condition_pair;
393
394 /** The event listener callback for the wait functions.
395  *
396  * \private \memberof mlt_events_struct
397  * \param self a properties list
398  * \param pair a condition pair
399  */
400
401 static void mlt_events_listen_for( mlt_properties self, condition_pair *pair )
402 {
403         pthread_mutex_lock( &pair->mutex );
404         pthread_cond_signal( &pair->cond );
405         pthread_mutex_unlock( &pair->mutex );
406 }
407
408 /** Prepare to wait for an event.
409  *
410  * \public \memberof mlt_events_struct
411  * \param self 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 self, const char *id )
417 {
418         condition_pair *pair = malloc( sizeof( condition_pair ) );
419         pthread_cond_init( &pair->cond, NULL );
420         pthread_mutex_init( &pair->mutex, NULL );
421         pthread_mutex_lock( &pair->mutex );
422         return mlt_events_listen( self, pair, id, ( mlt_listener )mlt_events_listen_for );
423 }
424
425 /** Wait for an event.
426  *
427  * \public \memberof mlt_events_struct
428  * \param self a properties list
429  * \param event an event
430  */
431
432 void mlt_events_wait_for( mlt_properties self, mlt_event event )
433 {
434         if ( event != NULL )
435         {
436                 condition_pair *pair = event->service;
437                 pthread_cond_wait( &pair->cond, &pair->mutex );
438         }
439 }
440
441 /** Cleanup after waiting for an event.
442  *
443  * \public \memberof mlt_events_struct
444  * \param self a properties list
445  * \param event an event
446  */
447
448 void mlt_events_close_wait_for( mlt_properties self, mlt_event event )
449 {
450         if ( event != NULL )
451         {
452                 condition_pair *pair = event->service;
453                 event->owner = NULL;
454                 pthread_mutex_unlock( &pair->mutex );
455                 pthread_mutex_destroy( &pair->mutex );
456                 pthread_cond_destroy( &pair->cond );
457                 free( pair );
458         }
459 }
460
461 /** Fetch the events object.
462  *
463  * \private \memberof mlt_events_struct
464  * \param self a properties list
465  * \return an events object
466  */
467
468 static mlt_events mlt_events_fetch( mlt_properties self )
469 {
470         mlt_events events = NULL;
471         if ( self != NULL )
472                 events = mlt_properties_get_data( self, "_events", NULL );
473         return events;
474 }
475
476 /** Store the events object.
477  *
478  * \private \memberof mlt_events_struct
479  * \param self a properties list
480  * \param events an events object
481  */
482
483 static void mlt_events_store( mlt_properties self, mlt_events events )
484 {
485         if ( self != NULL && events != NULL )
486                 mlt_properties_set_data( self, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL );
487 }
488
489 /** Close the events object.
490  *
491  * \private \memberof mlt_events_struct
492  * \param events an events object
493  */
494
495 static void mlt_events_close( mlt_events events )
496 {
497         if ( events != NULL )
498         {
499                 mlt_properties_close( events->list );
500                 free( events );
501         }
502 }