Import existing source tree; original VCS history is no longer available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
412 lines
9.2 KiB
C
412 lines
9.2 KiB
C
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
a_events.h - Internal Event System
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2002, David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#ifndef _A_EVENTS_H_
|
|
#define _A_EVENTS_H_
|
|
|
|
#define AEV_TRACKING
|
|
|
|
#include "a_globals.h"
|
|
#include "a_types.h"
|
|
|
|
/*----------------------------------------------------------
|
|
Open/Close
|
|
----------------------------------------------------------*/
|
|
int aev_open(int pool_size);
|
|
void aev_close(void);
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
The global event timestamp time base
|
|
----------------------------------------------------------*/
|
|
#define AEV_TIMESTAMP_MASK 0xffff
|
|
typedef Uint16 aev_timestamp_t;
|
|
|
|
extern aev_timestamp_t aev_timer;
|
|
|
|
static inline void aev_reset_timer(void)
|
|
{
|
|
aev_timer = 0;
|
|
}
|
|
|
|
static inline void aev_advance_timer(unsigned frames)
|
|
{
|
|
aev_timer += (aev_timestamp_t)frames;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Event
|
|
----------------------------------------------------------*/
|
|
#define AEV_ET_FREE 0xff
|
|
#define AEV_ET_ALLOCATED 0xfe
|
|
|
|
struct aev_port_t;
|
|
typedef struct aev_event_t
|
|
{
|
|
struct aev_event_t *next;
|
|
aev_timestamp_t frame; /* When */
|
|
Uint8 type; /* What */
|
|
Uint8 index; /* Qualifier */
|
|
Sint32 arg1; /* Argument 1 */
|
|
Sint32 arg2; /* Argument 2 */
|
|
Sint32 arg3; /* Argument 3 */
|
|
#ifdef AEV_TRACKING
|
|
const char *client; /* Client name */
|
|
struct aev_port_t *port; /* Receiver port */
|
|
#endif
|
|
} aev_event_t;
|
|
|
|
|
|
/* Macro for interpreting event timestamps. */
|
|
#define AEV_TIME(frame, offset) \
|
|
((unsigned)(frame - aev_timer - offset) & \
|
|
AEV_TIMESTAMP_MASK)
|
|
|
|
/* The global event pool. */
|
|
extern aev_event_t *aev_event_pool;
|
|
|
|
#ifdef AEV_TRACKING
|
|
extern int aev_event_counter; //Current number of events in use
|
|
extern int aev_event_counter_max; //Peak use tracker
|
|
extern const char *aev_current_client; //Name of current client
|
|
#endif
|
|
|
|
#ifdef AEV_TRACKING
|
|
#define aev_client(x) aev_current_client = x;
|
|
#else
|
|
#define aev_client(x)
|
|
#endif
|
|
|
|
/* Refill event pool. */
|
|
int _aev_refill_pool(void);
|
|
|
|
/*
|
|
* Allocate an event.
|
|
*
|
|
* Will try to refill the event pool if required.
|
|
* Returns the event, or NULL, if the pool was empty
|
|
* and could not be refilled.
|
|
*/
|
|
static inline aev_event_t *aev_new(void)
|
|
{
|
|
aev_event_t *ev;
|
|
if(!aev_event_pool)
|
|
if(_aev_refill_pool() < 0)
|
|
return NULL;
|
|
|
|
ev = aev_event_pool;
|
|
aev_event_pool = ev->next;
|
|
#ifdef AEV_TRACKING
|
|
ev->type = AEV_ET_ALLOCATED;
|
|
ev->client = aev_current_client;
|
|
++aev_event_counter;
|
|
if(aev_event_counter > aev_event_counter_max)
|
|
aev_event_counter_max = aev_event_counter;
|
|
#endif
|
|
return ev;
|
|
}
|
|
|
|
|
|
/* Free an event. */
|
|
static inline void aev_free(aev_event_t *ev)
|
|
{
|
|
ev->next = aev_event_pool;
|
|
aev_event_pool = ev;
|
|
#ifdef AEV_TRACKING
|
|
ev->type = AEV_ET_FREE;
|
|
--aev_event_counter;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Event Input Port
|
|
----------------------------------------------------------*/
|
|
typedef struct aev_port_t
|
|
{
|
|
/*
|
|
* Note that when 'first' is NULL, 'last' is undefined!
|
|
* (If there's no first event, there can't be a last
|
|
* event either - makes sense, eh? :-)
|
|
*/
|
|
aev_event_t *first;
|
|
aev_event_t *last;
|
|
const char *name;
|
|
} aev_port_t;
|
|
|
|
|
|
/*
|
|
* Initialize a port.
|
|
*****************************************
|
|
* DO NOT FORGET TO USE THIS!
|
|
*****************************************
|
|
*/
|
|
static inline void aev_port_init(aev_port_t *evp, const char *name)
|
|
{
|
|
evp->first = evp->last = NULL;
|
|
evp->name = name;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write one event to a port. The event will belong to the
|
|
* owner of the port, who is responsible for freeing or
|
|
* reusing the event after reading it.
|
|
*
|
|
* NOTE: Events must be queued in increasing timestamp order!
|
|
*/
|
|
static inline void aev_write(aev_port_t *evp, aev_event_t *ev)
|
|
{
|
|
ev->next = NULL;
|
|
if(!evp->first)
|
|
evp->first = evp->last = ev; /* Oops, empty port! */
|
|
else
|
|
{
|
|
evp->last->next = ev;
|
|
evp->last = ev;
|
|
}
|
|
#ifdef AEV_TRACKING
|
|
ev->port = evp;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert an event at the right position in the queue of
|
|
* the specified port. The event will be placed *after*
|
|
* any other events with the same timestamp.
|
|
*
|
|
* The event will belong to the owner of the port, who is
|
|
* responsible for freeing or reusing the event after
|
|
* reading it.
|
|
*/
|
|
void aev_insert(aev_port_t *evp, aev_event_t *ev);
|
|
|
|
|
|
/*
|
|
* Read one event from a port. The event will be removed
|
|
* from the port, and is effectively detached from the
|
|
* event system until you reuse it by sending it somewhere,
|
|
* or free it.
|
|
*
|
|
* WARNING: Events *MUST* be either freed or reused!
|
|
*
|
|
* Event leaks will result in severe timing issues, as the
|
|
* pool will need to be refilled regularly, which is *not*
|
|
* a real time compatible operation.
|
|
*
|
|
* Returns the event, or NULL, if no event was queued.
|
|
*/
|
|
static inline aev_event_t *aev_read(aev_port_t *evp)
|
|
{
|
|
aev_event_t *ev = evp->first;
|
|
if(!ev)
|
|
return NULL;
|
|
|
|
evp->first = ev->next;
|
|
return ev;
|
|
}
|
|
|
|
/*
|
|
* Return number of frames from the start of the current
|
|
* buffer (current value of 'aev_timer') + 'offset' to
|
|
* the next event queued on this port.
|
|
*
|
|
* If there's no event enqueued, the maximum possible
|
|
* number of frames is returned.
|
|
*/
|
|
static inline unsigned aev_next(aev_port_t *evp, unsigned offset)
|
|
{
|
|
aev_event_t *ev = evp->first;
|
|
if(ev)
|
|
return AEV_TIME(ev->frame, offset);
|
|
else
|
|
return AEV_TIMESTAMP_MASK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove and free all events on the specified port.
|
|
*/
|
|
static inline void aev_flush(aev_port_t *evp)
|
|
{
|
|
#ifdef AEV_TRACKING
|
|
while(1)
|
|
{
|
|
aev_event_t *ev = evp->first;
|
|
if(!ev)
|
|
return;
|
|
|
|
evp->first = ev->next;
|
|
aev_free(ev);
|
|
}
|
|
#else
|
|
/*
|
|
* Silly me! I've pulled this simple trick
|
|
* before, but just forgot somehow... :-)
|
|
*/
|
|
if(!evp->first)
|
|
return;
|
|
|
|
evp->last->next = aev_event_pool;
|
|
aev_event_pool = evp->first;
|
|
evp->first = NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Short and handy "send" functions.
|
|
*
|
|
* Allocates an event, fills it in, and sends it to the
|
|
* specified port. The address of the event used is
|
|
* returned - which means that you can detect an error
|
|
* by checking that the return is not NULL.
|
|
*/
|
|
static inline aev_event_t *aev_send0(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_send1(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type, Sint32 arg1)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_send2(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type, Sint32 arg1, Sint32 arg2)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
ev->arg2 = arg2;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_sendi0(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type, Uint8 index)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->index = index;
|
|
ev->frame = delay + aev_timer;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_sendi1(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type,
|
|
Uint8 index, Sint32 arg1)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->index = index;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_sendi2(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type,
|
|
Uint8 index, Sint32 arg1, Sint32 arg2)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->index = index;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
ev->arg2 = arg2;
|
|
aev_write(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
/*
|
|
* Like the above, but using aev_insert().
|
|
*/
|
|
static inline aev_event_t *aev_insert0(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
aev_insert(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_insert1(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type, Sint32 arg1)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
aev_insert(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
static inline aev_event_t *aev_insert2(aev_port_t *evp,
|
|
aev_timestamp_t delay, Uint8 type,
|
|
Sint32 arg1, Sint32 arg2)
|
|
{
|
|
aev_event_t *ev = aev_new();
|
|
if(!ev)
|
|
return NULL;
|
|
ev->type = type;
|
|
ev->frame = delay + aev_timer;
|
|
ev->arg1 = arg1;
|
|
ev->arg2 = arg2;
|
|
aev_insert(evp, ev);
|
|
return ev;
|
|
}
|
|
|
|
#endif /*_A_EVENTS_H_*/
|