1 /*(LGPL)
2 ---------------------------------------------------------------------------
3 	a_events.h - Internal Event System
4 ---------------------------------------------------------------------------
5  * Copyright (C) 2002, David Olofson
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #ifndef _A_EVENTS_H_
23 #define _A_EVENTS_H_
24 
25 #define	AEV_TRACKING
26 
27 #include "a_globals.h"
28 #include "a_types.h"
29 
30 /*----------------------------------------------------------
31 	Open/Close
32 ----------------------------------------------------------*/
33 int aev_open(int pool_size);
34 void aev_close(void);
35 
36 
37 /*----------------------------------------------------------
38 	The global event timestamp time base
39 ----------------------------------------------------------*/
40 #define	AEV_TIMESTAMP_MASK	0xffff
41 typedef Uint16 aev_timestamp_t;
42 
43 extern aev_timestamp_t aev_timer;
44 
aev_reset_timer(void)45 static inline void aev_reset_timer(void)
46 {
47 	aev_timer = 0;
48 }
49 
aev_advance_timer(unsigned frames)50 static inline void aev_advance_timer(unsigned frames)
51 {
52 	aev_timer += (aev_timestamp_t)frames;
53 }
54 
55 
56 /*----------------------------------------------------------
57 	Event
58 ----------------------------------------------------------*/
59 #define	AEV_ET_FREE		0xff
60 #define	AEV_ET_ALLOCATED	0xfe
61 
62 struct aev_port_t;
63 typedef struct aev_event_t
64 {
65 	struct aev_event_t	*next;
66 	aev_timestamp_t		frame;		/* When */
67 	Uint8			type;		/* What */
68 	Uint8			index;		/* Qualifier */
69 	Sint32			arg1;		/* Argument 1 */
70 	Sint32			arg2;		/* Argument 2 */
71 	Sint32			arg3;		/* Argument 3 */
72 #ifdef AEV_TRACKING
73 	const char		*client;	/* Client name */
74 	struct aev_port_t	*port;		/* Receiver port */
75 #endif
76 } aev_event_t;
77 
78 
79 /* Macro for interpreting event timestamps. */
80 #define	AEV_TIME(frame, offset)					\
81 		((unsigned)(frame - aev_timer - offset) &	\
82 		AEV_TIMESTAMP_MASK)
83 
84 /* The global event pool. */
85 extern aev_event_t		*aev_event_pool;
86 
87 #ifdef AEV_TRACKING
88 extern int aev_event_counter;			//Current number of events in use
89 extern int aev_event_counter_max;		//Peak use tracker
90 extern const char *aev_current_client;	//Name of current client
91 #endif
92 
93 #ifdef AEV_TRACKING
94 #define	aev_client(x)	aev_current_client = x;
95 #else
96 #define	aev_client(x)
97 #endif
98 
99 /* Refill event pool. */
100 int _aev_refill_pool(void);
101 
102 /*
103  * Allocate an event.
104  *
105  * Will try to refill the event pool if required.
106  * Returns the event, or NULL, if the pool was empty
107  * and could not be refilled.
108  */
aev_new(void)109 static inline aev_event_t *aev_new(void)
110 {
111 	aev_event_t *ev;
112 	if(!aev_event_pool)
113 		if(_aev_refill_pool() < 0)
114 			return NULL;
115 
116 	ev = aev_event_pool;
117 	aev_event_pool = ev->next;
118 #ifdef AEV_TRACKING
119 	ev->type = AEV_ET_ALLOCATED;
120 	ev->client = aev_current_client;
121 	++aev_event_counter;
122 	if(aev_event_counter > aev_event_counter_max)
123 		aev_event_counter_max = aev_event_counter;
124 #endif
125 	return ev;
126 }
127 
128 
129 /* Free an event. */
aev_free(aev_event_t * ev)130 static inline void aev_free(aev_event_t *ev)
131 {
132 	ev->next = aev_event_pool;
133 	aev_event_pool = ev;
134 #ifdef AEV_TRACKING
135 	ev->type = AEV_ET_FREE;
136 	--aev_event_counter;
137 #endif
138 }
139 
140 
141 /*----------------------------------------------------------
142 	Event Input Port
143 ----------------------------------------------------------*/
144 typedef struct aev_port_t
145 {
146 	/*
147 	 * Note that when 'first' is NULL, 'last' is undefined!
148 	 * (If there's no first event, there can't be a last
149 	 * event either - makes sense, eh? :-)
150 	 */
151 	aev_event_t		*first;
152 	aev_event_t		*last;
153 	const char		*name;
154 } aev_port_t;
155 
156 
157 /*
158  * Initialize a port.
159  *****************************************
160  *	DO NOT FORGET TO USE THIS!
161  *****************************************
162  */
aev_port_init(aev_port_t * evp,const char * name)163 static inline void aev_port_init(aev_port_t *evp, const char *name)
164 {
165 	evp->first = evp->last = NULL;
166 	evp->name = name;
167 }
168 
169 
170 /*
171  * Write one event to a port. The event will belong to the
172  * owner of the port, who is responsible for freeing or
173  * reusing the event after reading it.
174  *
175  * NOTE: Events must be queued in increasing timestamp order!
176  */
aev_write(aev_port_t * evp,aev_event_t * ev)177 static inline void aev_write(aev_port_t *evp, aev_event_t *ev)
178 {
179 	ev->next = NULL;
180 	if(!evp->first)
181 		evp->first = evp->last = ev;	/* Oops, empty port! */
182 	else
183 	{
184 		evp->last->next = ev;
185 		evp->last = ev;
186 	}
187 #ifdef AEV_TRACKING
188 	ev->port = evp;
189 #endif
190 }
191 
192 
193 /*
194  * Insert an event at the right position in the queue of
195  * the specified port. The event will be placed *after*
196  * any other events with the same timestamp.
197  *
198  * The event will belong to the owner of the port, who is
199  * responsible for freeing or reusing the event after
200  * reading it.
201  */
202 void aev_insert(aev_port_t *evp, aev_event_t *ev);
203 
204 
205 /*
206  * Read one event from a port. The event will be removed
207  * from the port, and is effectively detached from the
208  * event system until you reuse it by sending it somewhere,
209  * or free it.
210  *
211  *	WARNING: Events *MUST* be either freed or reused!
212  *
213  * Event leaks will result in severe timing issues, as the
214  * pool will need to be refilled regularly, which is *not*
215  * a real time compatible operation.
216  *
217  * Returns the event, or NULL, if no event was queued.
218  */
aev_read(aev_port_t * evp)219 static inline aev_event_t *aev_read(aev_port_t *evp)
220 {
221 	aev_event_t *ev = evp->first;
222 	if(!ev)
223 		return NULL;
224 
225 	evp->first = ev->next;
226 	return ev;
227 }
228 
229 /*
230  * Return number of frames from the start of the current
231  * buffer (current value of 'aev_timer') + 'offset' to
232  * the next event queued on this port.
233  *
234  * If there's no event enqueued, the maximum possible
235  * number of frames is returned.
236  */
aev_next(aev_port_t * evp,unsigned offset)237 static inline unsigned aev_next(aev_port_t *evp, unsigned offset)
238 {
239 	aev_event_t *ev = evp->first;
240 	if(ev)
241 		return AEV_TIME(ev->frame, offset);
242 	else
243 		return AEV_TIMESTAMP_MASK;
244 }
245 
246 
247 /*
248  * Remove and free all events on the specified port.
249  */
aev_flush(aev_port_t * evp)250 static inline void aev_flush(aev_port_t *evp)
251 {
252 #ifdef AEV_TRACKING
253 	while(1)
254 	{
255 		aev_event_t *ev = evp->first;
256 		if(!ev)
257 			return;
258 
259 		evp->first = ev->next;
260 		aev_free(ev);
261 	}
262 #else
263 	/*
264 	 * Silly me! I've pulled this simple trick
265 	 * before, but just forgot somehow... :-)
266 	 */
267 	if(!evp->first)
268 		return;
269 
270 	evp->last->next = aev_event_pool;
271 	aev_event_pool = evp->first;
272 	evp->first = NULL;
273 #endif
274 }
275 
276 
277 /*
278  * Short and handy "send" functions.
279  *
280  * Allocates an event, fills it in, and sends it to the
281  * specified port. The address of the event used is
282  * returned - which means that you can detect an error
283  * by checking that the return is not NULL.
284  */
aev_send0(aev_port_t * evp,aev_timestamp_t delay,Uint8 type)285 static inline aev_event_t *aev_send0(aev_port_t *evp,
286 		aev_timestamp_t delay, Uint8 type)
287 {
288 	aev_event_t *ev = aev_new();
289 	if(!ev)
290 		return NULL;
291 	ev->type = type;
292 	ev->frame = delay + aev_timer;
293 	aev_write(evp, ev);
294 	return ev;
295 }
296 
aev_send1(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Sint32 arg1)297 static inline aev_event_t *aev_send1(aev_port_t *evp,
298 		aev_timestamp_t delay, Uint8 type, Sint32 arg1)
299 {
300 	aev_event_t *ev = aev_new();
301 	if(!ev)
302 		return NULL;
303 	ev->type = type;
304 	ev->frame = delay + aev_timer;
305 	ev->arg1 = arg1;
306 	aev_write(evp, ev);
307 	return ev;
308 }
309 
aev_send2(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Sint32 arg1,Sint32 arg2)310 static inline aev_event_t *aev_send2(aev_port_t *evp,
311 		aev_timestamp_t delay, Uint8 type, Sint32 arg1, Sint32 arg2)
312 {
313 	aev_event_t *ev = aev_new();
314 	if(!ev)
315 		return NULL;
316 	ev->type = type;
317 	ev->frame = delay + aev_timer;
318 	ev->arg1 = arg1;
319 	ev->arg2 = arg2;
320 	aev_write(evp, ev);
321 	return ev;
322 }
323 
aev_sendi0(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Uint8 index)324 static inline aev_event_t *aev_sendi0(aev_port_t *evp,
325 		aev_timestamp_t delay, Uint8 type, Uint8 index)
326 {
327 	aev_event_t *ev = aev_new();
328 	if(!ev)
329 		return NULL;
330 	ev->type = type;
331 	ev->index = index;
332 	ev->frame = delay + aev_timer;
333 	aev_write(evp, ev);
334 	return ev;
335 }
336 
aev_sendi1(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Uint8 index,Sint32 arg1)337 static inline aev_event_t *aev_sendi1(aev_port_t *evp,
338 		aev_timestamp_t delay, Uint8 type,
339 		Uint8 index, Sint32 arg1)
340 {
341 	aev_event_t *ev = aev_new();
342 	if(!ev)
343 		return NULL;
344 	ev->type = type;
345 	ev->index = index;
346 	ev->frame = delay + aev_timer;
347 	ev->arg1 = arg1;
348 	aev_write(evp, ev);
349 	return ev;
350 }
351 
aev_sendi2(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Uint8 index,Sint32 arg1,Sint32 arg2)352 static inline aev_event_t *aev_sendi2(aev_port_t *evp,
353 		aev_timestamp_t delay, Uint8 type,
354 		Uint8 index, Sint32 arg1, Sint32 arg2)
355 {
356 	aev_event_t *ev = aev_new();
357 	if(!ev)
358 		return NULL;
359 	ev->type = type;
360 	ev->index = index;
361 	ev->frame = delay + aev_timer;
362 	ev->arg1 = arg1;
363 	ev->arg2 = arg2;
364 	aev_write(evp, ev);
365 	return ev;
366 }
367 
368 /*
369  * Like the above, but using aev_insert().
370  */
aev_insert0(aev_port_t * evp,aev_timestamp_t delay,Uint8 type)371 static inline aev_event_t *aev_insert0(aev_port_t *evp,
372 		aev_timestamp_t delay, Uint8 type)
373 {
374 	aev_event_t *ev = aev_new();
375 	if(!ev)
376 		return NULL;
377 	ev->type = type;
378 	ev->frame = delay + aev_timer;
379 	aev_insert(evp, ev);
380 	return ev;
381 }
382 
aev_insert1(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Sint32 arg1)383 static inline aev_event_t *aev_insert1(aev_port_t *evp,
384 		aev_timestamp_t delay, Uint8 type, Sint32 arg1)
385 {
386 	aev_event_t *ev = aev_new();
387 	if(!ev)
388 		return NULL;
389 	ev->type = type;
390 	ev->frame = delay + aev_timer;
391 	ev->arg1 = arg1;
392 	aev_insert(evp, ev);
393 	return ev;
394 }
395 
aev_insert2(aev_port_t * evp,aev_timestamp_t delay,Uint8 type,Sint32 arg1,Sint32 arg2)396 static inline aev_event_t *aev_insert2(aev_port_t *evp,
397 		aev_timestamp_t delay, Uint8 type,
398 		Sint32 arg1, Sint32 arg2)
399 {
400 	aev_event_t *ev = aev_new();
401 	if(!ev)
402 		return NULL;
403 	ev->type = type;
404 	ev->frame = delay + aev_timer;
405 	ev->arg1 = arg1;
406 	ev->arg2 = arg2;
407 	aev_insert(evp, ev);
408 	return ev;
409 }
410 
411 #endif /*_A_EVENTS_H_*/
412