src/framework/*: improve the doxygen documentation (work in progress). This also...
[melted] / 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 */
43
44 struct mlt_events_struct
45 {
46 mlt_properties owner;
47 mlt_properties list;
48 };
49
50 typedef struct mlt_events_struct *mlt_events;
51
52 /** \brief Event class
53 *
54 */
55
56 struct mlt_event_struct
57 {
58 mlt_events owner;
59 int ref_count;
60 int block_count;
61 mlt_listener listener;
62 void *service;
63 };
64
65 /** Increment the reference count on this event.
66 */
67
68 void mlt_event_inc_ref( mlt_event this )
69 {
70 if ( this != NULL )
71 this->ref_count ++;
72 }
73
74 /** Increment the block count on this event.
75 */
76
77 void mlt_event_block( mlt_event this )
78 {
79 if ( this != NULL && this->owner != NULL )
80 this->block_count ++;
81 }
82
83 /** Decrement the block count on this event.
84 */
85
86 void mlt_event_unblock( mlt_event this )
87 {
88 if ( this != NULL && this->owner != NULL )
89 this->block_count --;
90 }
91
92 /** Close this event.
93 */
94
95 void mlt_event_close( mlt_event this )
96 {
97 if ( this != NULL )
98 {
99 if ( -- this->ref_count == 1 )
100 this->owner = NULL;
101 if ( this->ref_count <= 0 )
102 {
103 #ifdef _MLT_EVENT_CHECKS_
104 events_destroyed ++;
105 fprintf( stderr, "Events created %d, destroyed %d\n", events_created, events_destroyed );
106 #endif
107 free( this );
108 }
109 }
110 }
111
112 /* Forward declaration to private functions.
113 */
114
115 static mlt_events mlt_events_fetch( mlt_properties );
116 static void mlt_events_store( mlt_properties, mlt_events );
117 static void mlt_events_close( mlt_events );
118
119 /** Initialise the events structure.
120 */
121
122 void mlt_events_init( mlt_properties this )
123 {
124 mlt_events events = mlt_events_fetch( this );
125 if ( events == NULL )
126 {
127 events = malloc( sizeof( struct mlt_events_struct ) );
128 events->list = mlt_properties_new( );
129 mlt_events_store( this, events );
130 }
131 }
132
133 /** Register an event and transmitter.
134 */
135
136 int mlt_events_register( mlt_properties this, char *id, mlt_transmitter transmitter )
137 {
138 int error = 1;
139 mlt_events events = mlt_events_fetch( this );
140 if ( events != NULL )
141 {
142 mlt_properties list = events->list;
143 char temp[ 128 ];
144 error = mlt_properties_set_data( list, id, transmitter, 0, NULL, NULL );
145 sprintf( temp, "list:%s", id );
146 if ( mlt_properties_get_data( list, temp, NULL ) == NULL )
147 mlt_properties_set_data( list, temp, mlt_properties_new( ), 0, ( mlt_destructor )mlt_properties_close, NULL );
148 }
149 return error;
150 }
151
152 /** Fire an event.
153 */
154
155 void mlt_events_fire( mlt_properties this, char *id, ... )
156 {
157 mlt_events events = mlt_events_fetch( this );
158 if ( events != NULL )
159 {
160 int i = 0;
161 va_list alist;
162 void *args[ 10 ];
163 mlt_properties list = events->list;
164 mlt_properties listeners = NULL;
165 char temp[ 128 ];
166 mlt_transmitter transmitter = mlt_properties_get_data( list, id, NULL );
167 sprintf( temp, "list:%s", id );
168 listeners = mlt_properties_get_data( list, temp, NULL );
169
170 va_start( alist, id );
171 do
172 args[ i ] = va_arg( alist, void * );
173 while( args[ i ++ ] != NULL );
174 va_end( alist );
175
176 if ( listeners != NULL )
177 {
178 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
179 {
180 mlt_event event = mlt_properties_get_data_at( listeners, i, NULL );
181 if ( event != NULL && event->owner != NULL && event->block_count == 0 )
182 {
183 if ( transmitter != NULL )
184 transmitter( event->listener, event->owner, event->service, args );
185 else
186 event->listener( event->owner, event->service );
187 }
188 }
189 }
190 }
191 }
192
193 /** Register a listener.
194 */
195
196 mlt_event mlt_events_listen( mlt_properties this, void *service, char *id, mlt_listener listener )
197 {
198 mlt_event event = NULL;
199 mlt_events events = mlt_events_fetch( this );
200 if ( events != NULL )
201 {
202 mlt_properties list = events->list;
203 mlt_properties listeners = NULL;
204 char temp[ 128 ];
205 sprintf( temp, "list:%s", id );
206 listeners = mlt_properties_get_data( list, temp, NULL );
207 if ( listeners != NULL )
208 {
209 int first_null = -1;
210 int i = 0;
211 for ( i = 0; event == NULL && i < mlt_properties_count( listeners ); i ++ )
212 {
213 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
214 if ( entry != NULL && entry->owner != NULL )
215 {
216 if ( entry->service == service && entry->listener == listener )
217 event = entry;
218 }
219 else if ( ( entry == NULL || entry->owner == NULL ) && first_null == -1 )
220 {
221 first_null = i;
222 }
223 }
224
225 if ( event == NULL )
226 {
227 event = malloc( sizeof( struct mlt_event_struct ) );
228 if ( event != NULL )
229 {
230 #ifdef _MLT_EVENT_CHECKS_
231 events_created ++;
232 #endif
233 sprintf( temp, "%d", first_null == -1 ? mlt_properties_count( listeners ) : first_null );
234 event->owner = events;
235 event->ref_count = 0;
236 event->block_count = 0;
237 event->listener = listener;
238 event->service = service;
239 mlt_properties_set_data( listeners, temp, event, 0, ( mlt_destructor )mlt_event_close, NULL );
240 mlt_event_inc_ref( event );
241 }
242 }
243
244 }
245 }
246 return event;
247 }
248
249 /** Block all events for a given service.
250 */
251
252 void mlt_events_block( mlt_properties this, void *service )
253 {
254 mlt_events events = mlt_events_fetch( this );
255 if ( events != NULL )
256 {
257 int i = 0, j = 0;
258 mlt_properties list = events->list;
259 for ( j = 0; j < mlt_properties_count( list ); j ++ )
260 {
261 char *temp = mlt_properties_get_name( list, j );
262 if ( !strncmp( temp, "list:", 5 ) )
263 {
264 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
265 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
266 {
267 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
268 if ( entry != NULL && entry->service == service )
269 mlt_event_block( entry );
270 }
271 }
272 }
273 }
274 }
275
276 /** Unblock all events for a given service.
277 */
278
279 void mlt_events_unblock( mlt_properties this, void *service )
280 {
281 mlt_events events = mlt_events_fetch( this );
282 if ( events != NULL )
283 {
284 int i = 0, j = 0;
285 mlt_properties list = events->list;
286 for ( j = 0; j < mlt_properties_count( list ); j ++ )
287 {
288 char *temp = mlt_properties_get_name( list, j );
289 if ( !strncmp( temp, "list:", 5 ) )
290 {
291 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
292 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
293 {
294 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
295 if ( entry != NULL && entry->service == service )
296 mlt_event_unblock( entry );
297 }
298 }
299 }
300 }
301 }
302
303 /** Disconnect all events for a given service.
304 */
305
306 void mlt_events_disconnect( mlt_properties this, void *service )
307 {
308 mlt_events events = mlt_events_fetch( this );
309 if ( events != NULL )
310 {
311 int i = 0, j = 0;
312 mlt_properties list = events->list;
313 for ( j = 0; j < mlt_properties_count( list ); j ++ )
314 {
315 char *temp = mlt_properties_get_name( list, j );
316 if ( !strncmp( temp, "list:", 5 ) )
317 {
318 mlt_properties listeners = mlt_properties_get_data( list, temp, NULL );
319 for ( i = 0; i < mlt_properties_count( listeners ); i ++ )
320 {
321 mlt_event entry = mlt_properties_get_data_at( listeners, i, NULL );
322 char *name = mlt_properties_get_name( listeners, i );
323 if ( entry != NULL && entry->service == service )
324 mlt_properties_set_data( listeners, name, NULL, 0, NULL, NULL );
325 }
326 }
327 }
328 }
329 }
330
331 /** \brief private to mlt_events_struct, used by mlt_events_wait_for() */
332
333 typedef struct
334 {
335 int done;
336 pthread_cond_t cond;
337 pthread_mutex_t mutex;
338 }
339 condition_pair;
340
341 static void mlt_events_listen_for( mlt_properties this, condition_pair *pair )
342 {
343 pthread_mutex_lock( &pair->mutex );
344 if ( pair->done == 0 )
345 {
346 pthread_cond_signal( &pair->cond );
347 pthread_mutex_unlock( &pair->mutex );
348 }
349 }
350
351 mlt_event mlt_events_setup_wait_for( mlt_properties this, char *id )
352 {
353 condition_pair *pair = malloc( sizeof( condition_pair ) );
354 pair->done = 0;
355 pthread_cond_init( &pair->cond, NULL );
356 pthread_mutex_init( &pair->mutex, NULL );
357 pthread_mutex_lock( &pair->mutex );
358 return mlt_events_listen( this, pair, id, ( mlt_listener )mlt_events_listen_for );
359 }
360
361 void mlt_events_wait_for( mlt_properties this, mlt_event event )
362 {
363 if ( event != NULL )
364 {
365 condition_pair *pair = event->service;
366 pthread_cond_wait( &pair->cond, &pair->mutex );
367 }
368 }
369
370 void mlt_events_close_wait_for( mlt_properties this, mlt_event event )
371 {
372 if ( event != NULL )
373 {
374 condition_pair *pair = event->service;
375 event->owner = NULL;
376 pair->done = 0;
377 pthread_mutex_unlock( &pair->mutex );
378 pthread_mutex_destroy( &pair->mutex );
379 pthread_cond_destroy( &pair->cond );
380 }
381 }
382
383 /** Fetch the events object.
384 */
385
386 static mlt_events mlt_events_fetch( mlt_properties this )
387 {
388 mlt_events events = NULL;
389 if ( this != NULL )
390 events = mlt_properties_get_data( this, "_events", NULL );
391 return events;
392 }
393
394 /** Store the events object.
395 */
396
397 static void mlt_events_store( mlt_properties this, mlt_events events )
398 {
399 if ( this != NULL && events != NULL )
400 mlt_properties_set_data( this, "_events", events, 0, ( mlt_destructor )mlt_events_close, NULL );
401 }
402
403 /** Close the events object.
404 */
405
406 static void mlt_events_close( mlt_events events )
407 {
408 if ( events != NULL )
409 {
410 mlt_properties_close( events->list );
411 free( events );
412 }
413 }
414