2013-05-01 23:53:28 +00:00
|
|
|
/*
|
2013-06-15 11:37:22 +00:00
|
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
2013-05-03 14:36:17 +00:00
|
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
|
|
*
|
2013-07-21 20:20:37 +00:00
|
|
|
* http://ugfx.org/license.html
|
2013-05-03 14:36:17 +00:00
|
|
|
*/
|
2013-03-02 12:03:40 +00:00
|
|
|
|
|
|
|
#include "gfx.h"
|
|
|
|
|
|
|
|
#if GFX_USE_GEVENT || defined(__DOXYGEN__)
|
|
|
|
|
|
|
|
#if GEVENT_ASSERT_NO_RESOURCE
|
|
|
|
#define GEVENT_ASSERT(x) assert(x)
|
|
|
|
#else
|
|
|
|
#define GEVENT_ASSERT(x)
|
|
|
|
#endif
|
|
|
|
|
2014-07-16 06:44:19 +00:00
|
|
|
/* Flags in the listener structure */
|
|
|
|
#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy
|
|
|
|
#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal
|
|
|
|
#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event
|
2014-08-16 14:13:47 +00:00
|
|
|
#define GLISTENER_PENDING 0x0008 // There is an event waiting ready to go without a current listener
|
2014-07-16 06:44:19 +00:00
|
|
|
|
2013-03-02 12:03:40 +00:00
|
|
|
/* This mutex protects access to our tables */
|
2013-05-24 15:26:52 +00:00
|
|
|
static gfxMutex geventMutex;
|
2013-03-02 12:03:40 +00:00
|
|
|
|
|
|
|
/* Our table of listener/source pairs */
|
|
|
|
static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS];
|
|
|
|
|
2014-07-16 06:44:19 +00:00
|
|
|
/* Send an exit event if possible. */
|
|
|
|
/* We already have the geventMutex */
|
|
|
|
static void doExitEvent(GListener *pl) {
|
|
|
|
// Don't do the exit if someone else currently has the event lock
|
|
|
|
if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) {
|
|
|
|
pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use
|
|
|
|
pl->event.type = GEVENT_EXIT; // Set up the EXIT event
|
|
|
|
pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data)
|
|
|
|
gfxSemSignal(&pl->waitqueue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-02 12:03:40 +00:00
|
|
|
/* Loop through the assignment table deleting this listener/source pair. */
|
|
|
|
/* Null is treated as a wildcard. */
|
2014-07-16 06:44:19 +00:00
|
|
|
/* We already have the geventMutex */
|
2013-03-02 12:03:40 +00:00
|
|
|
static void deleteAssignments(GListener *pl, GSourceHandle gsh) {
|
|
|
|
GSourceListener *psl;
|
|
|
|
|
|
|
|
for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
|
|
|
if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) {
|
2014-07-16 06:44:19 +00:00
|
|
|
doExitEvent(psl->pListener);
|
2013-03-02 12:03:40 +00:00
|
|
|
psl->pListener = 0;
|
2014-07-16 06:44:19 +00:00
|
|
|
psl->pSource = 0;
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-02 18:24:43 +00:00
|
|
|
void _geventInit(void)
|
|
|
|
{
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexInit(&geventMutex);
|
|
|
|
}
|
|
|
|
|
2014-02-02 18:24:43 +00:00
|
|
|
void _geventDeinit(void)
|
|
|
|
{
|
2014-02-02 18:52:46 +00:00
|
|
|
gfxMutexDestroy(&geventMutex);
|
2014-02-02 18:24:43 +00:00
|
|
|
}
|
|
|
|
|
2013-03-02 12:03:40 +00:00
|
|
|
void geventListenerInit(GListener *pl) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block
|
|
|
|
pl->callback = 0; // No callback active
|
|
|
|
pl->event.type = GEVENT_NULL; // Always safety
|
2014-07-16 06:44:19 +00:00
|
|
|
pl->flags = 0;
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) {
|
|
|
|
GSourceListener *psl, *pslfree;
|
|
|
|
|
|
|
|
// Safety first
|
|
|
|
if (!pl || !gsh) {
|
|
|
|
GEVENT_ASSERT(FALSE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
|
|
|
|
// Check if this pair is already in the table (scan for a free slot at the same time)
|
|
|
|
pslfree = 0;
|
|
|
|
for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
|
|
|
|
|
|
|
if (pl == psl->pListener && gsh == psl->pSource) {
|
|
|
|
// Just update the flags
|
|
|
|
psl->listenflags = flags;
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (!pslfree && !psl->pListener)
|
|
|
|
pslfree = psl;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A free slot was found - allocate it
|
|
|
|
if (pslfree) {
|
|
|
|
pslfree->pListener = pl;
|
|
|
|
pslfree->pSource = gsh;
|
|
|
|
pslfree->listenflags = flags;
|
|
|
|
pslfree->srcflags = 0;
|
|
|
|
}
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
GEVENT_ASSERT(pslfree != 0);
|
|
|
|
return pslfree != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void geventDetachSource(GListener *pl, GSourceHandle gsh) {
|
|
|
|
if (pl) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
deleteAssignments(pl, gsh);
|
2014-07-16 06:44:19 +00:00
|
|
|
if (!gsh)
|
|
|
|
doExitEvent(pl);
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-24 15:26:52 +00:00
|
|
|
GEvent *geventEventWait(GListener *pl, delaytime_t timeout) {
|
2014-07-16 06:44:19 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
|
|
|
// Don't allow waiting if we are on callbacks or if there is already a thread waiting
|
|
|
|
if (pl->callback || (pl->flags & GLISTENER_WAITING)) {
|
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
return 0;
|
2014-07-16 06:44:19 +00:00
|
|
|
}
|
2014-08-16 14:13:47 +00:00
|
|
|
|
|
|
|
// Check to see if there is a pending event ready for us
|
|
|
|
if ((pl->flags & GLISTENER_PENDING)) {
|
|
|
|
pl->flags &= ~GLISTENER_PENDING; // We have now got this
|
|
|
|
pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is definitely busy
|
|
|
|
gfxMutexExit(&geventMutex);
|
|
|
|
return &pl->event;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No - wait for one.
|
2014-07-16 06:44:19 +00:00
|
|
|
pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy
|
|
|
|
pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread
|
|
|
|
gfxMutexExit(&geventMutex);
|
2014-08-16 14:13:47 +00:00
|
|
|
if (gfxSemWait(&pl->waitqueue, timeout))
|
|
|
|
return &pl->event;
|
|
|
|
|
|
|
|
// Timeout - clear the waiting flag.
|
|
|
|
// We know this is safe as any other thread will still think there is someone waiting.
|
|
|
|
gfxMutexEnter(&geventMutex);
|
|
|
|
pl->flags &= ~GLISTENER_WAITING;
|
|
|
|
gfxMutexExit(&geventMutex);
|
|
|
|
return 0;
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
|
2014-07-16 06:44:19 +00:00
|
|
|
void geventEventComplete(GListener *pl) {
|
|
|
|
pl->flags &= ~GLISTENER_EVENTBUSY;
|
|
|
|
}
|
|
|
|
|
2013-03-02 12:03:40 +00:00
|
|
|
void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
|
|
|
|
if (pl) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2014-07-16 06:44:19 +00:00
|
|
|
doExitEvent(pl);
|
|
|
|
pl->param = param; // Set the param
|
|
|
|
pl->callback = fn; // Set the callback function
|
|
|
|
if (fn)
|
|
|
|
pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr) {
|
|
|
|
GSourceListener *psl;
|
|
|
|
|
|
|
|
// Safety first
|
|
|
|
if (!gsh)
|
|
|
|
return 0;
|
|
|
|
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
|
2014-07-16 06:44:19 +00:00
|
|
|
// Unlock the last listener event buffer if it wasn't used.
|
|
|
|
if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE))
|
|
|
|
lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY);
|
2013-03-02 12:03:40 +00:00
|
|
|
|
|
|
|
// Loop through the table looking for attachments to this source
|
|
|
|
for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
|
|
|
if (gsh == psl->pSource) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
return psl;
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
GEvent *geventGetEventBuffer(GSourceListener *psl) {
|
2014-07-16 06:44:19 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
|
|
|
if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) {
|
|
|
|
// Oops - event buffer is still in use
|
|
|
|
gfxMutexExit(&geventMutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate the event buffer
|
|
|
|
psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY);
|
|
|
|
gfxMutexExit(&geventMutex);
|
|
|
|
return &psl->pListener->event;
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void geventSendEvent(GSourceListener *psl) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2014-07-16 06:44:19 +00:00
|
|
|
if (psl->pListener->callback) {
|
|
|
|
|
|
|
|
// Mark it back as free and as sent. This is early to be marking as free but it protects
|
|
|
|
// if the callback alters the listener in any way
|
2014-08-16 14:13:47 +00:00
|
|
|
psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY|GLISTENER_PENDING);
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2014-07-16 06:44:19 +00:00
|
|
|
|
|
|
|
// Do the callback
|
2013-03-02 12:03:40 +00:00
|
|
|
psl->pListener->callback(psl->pListener->param, &psl->pListener->event);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Wake up the listener
|
2014-08-16 14:13:47 +00:00
|
|
|
psl->pListener->flags &= ~GLISTENER_WITHSOURCE;
|
2014-07-16 06:44:19 +00:00
|
|
|
if ((psl->pListener->flags & GLISTENER_WAITING)) {
|
2014-08-16 14:13:47 +00:00
|
|
|
psl->pListener->flags &= ~(GLISTENER_WAITING|GLISTENER_PENDING);
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxSemSignal(&psl->pListener->waitqueue);
|
2014-08-16 14:13:47 +00:00
|
|
|
} else
|
|
|
|
psl->pListener->flags |= GLISTENER_PENDING;
|
|
|
|
|
|
|
|
// The listener thread will free the event buffer when ready
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void geventDetachSourceListeners(GSourceHandle gsh) {
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexEnter(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
deleteAssignments(0, gsh);
|
2013-05-24 15:26:52 +00:00
|
|
|
gfxMutexExit(&geventMutex);
|
2013-03-02 12:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* GFX_USE_GEVENT */
|