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