From 6d0a8a645b9a510a92eacc482e5d03059d8de381 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 10 Jun 2013 23:24:02 -0700 Subject: [PATCH] Made the SDL event queue dynamically allocated so we don't ever drop events. --- src/events/SDL_events.c | 241 ++++++++++++++++++++++++++-------------- 1 file changed, 159 insertions(+), 82 deletions(-) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 047a07d7a..3918580dd 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -53,17 +53,30 @@ static SDL_DisabledEventBlock *SDL_disabled_events[256]; static Uint32 SDL_userevents = SDL_USEREVENT; /* Private data -- event queue */ -#define MAXEVENTS 128 +typedef struct _SDL_EventEntry +{ + SDL_Event event; + SDL_SysWMmsg msg; + struct _SDL_EventEntry *prev; + struct _SDL_EventEntry *next; +} SDL_EventEntry; + +typedef struct _SDL_SysWMEntry +{ + SDL_SysWMmsg msg; + struct _SDL_SysWMEntry *next; +} SDL_SysWMEntry; + static struct { SDL_mutex *lock; - int active; - int head; - int tail; - SDL_Event event[MAXEVENTS]; - int wmmsg_next; - struct SDL_SysWMmsg wmmsg[MAXEVENTS]; -} SDL_EventQ = { NULL, 1 }; + volatile SDL_bool active; + SDL_EventEntry *head; + SDL_EventEntry *tail; + SDL_EventEntry *free; + SDL_SysWMEntry *wmmsg_used; + SDL_SysWMEntry *wmmsg_free; +} SDL_EventQ = { NULL, SDL_TRUE }; static __inline__ SDL_bool @@ -85,18 +98,41 @@ void SDL_StopEventLoop(void) { int i; - - SDL_EventQ.active = 0; + SDL_EventEntry *entry; + SDL_SysWMEntry *wmmsg; if (SDL_EventQ.lock) { - SDL_DestroyMutex(SDL_EventQ.lock); - SDL_EventQ.lock = NULL; + SDL_LockMutex(SDL_EventQ.lock); } + SDL_EventQ.active = SDL_FALSE; + /* Clean out EventQ */ - SDL_EventQ.head = 0; - SDL_EventQ.tail = 0; - SDL_EventQ.wmmsg_next = 0; + for (entry = SDL_EventQ.head; entry; ) { + SDL_EventEntry *next = entry->next; + SDL_free(entry); + entry = next; + } + for (entry = SDL_EventQ.free; entry; ) { + SDL_EventEntry *next = entry->next; + SDL_free(entry); + entry = next; + } + for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; ) { + SDL_SysWMEntry *next = wmmsg->next; + SDL_free(wmmsg); + wmmsg = next; + } + for (wmmsg = SDL_EventQ.wmmsg_free; wmmsg; ) { + SDL_SysWMEntry *next = wmmsg->next; + SDL_free(wmmsg); + wmmsg = next; + } + SDL_EventQ.head = NULL; + SDL_EventQ.tail = NULL; + SDL_EventQ.free = NULL; + SDL_EventQ.wmmsg_used = NULL; + SDL_EventQ.wmmsg_free = NULL; /* Clear disabled event state */ for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) { @@ -112,6 +148,12 @@ SDL_StopEventLoop(void) SDL_free(tmp); } SDL_EventOK = NULL; + + if (SDL_EventQ.lock) { + SDL_UnlockMutex(SDL_EventQ.lock); + SDL_DestroyMutex(SDL_EventQ.lock); + SDL_EventQ.lock = NULL; + } } /* This function (and associated calls) may be called more than once */ @@ -139,7 +181,7 @@ SDL_StartEventLoop(void) SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE); SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE); - SDL_EventQ.active = 1; + SDL_EventQ.active = SDL_TRUE; return (0); } @@ -149,55 +191,61 @@ SDL_StartEventLoop(void) static int SDL_AddEvent(SDL_Event * event) { - int tail, added; + SDL_EventEntry *entry; - tail = (SDL_EventQ.tail + 1) % MAXEVENTS; - if (tail == SDL_EventQ.head) { - /* Overflow, drop event */ - added = 0; - } else { - SDL_EventQ.event[SDL_EventQ.tail] = *event; - if (event->type == SDL_SYSWMEVENT) { - /* Note that it's possible to lose an event */ - int next = SDL_EventQ.wmmsg_next; - SDL_EventQ.wmmsg[next] = *event->syswm.msg; - SDL_EventQ.event[SDL_EventQ.tail].syswm.msg = - &SDL_EventQ.wmmsg[next]; - SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS; + if (SDL_EventQ.free == NULL) { + entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry)); + if (!entry) { + return 0; } - SDL_EventQ.tail = tail; - added = 1; + } else { + entry = SDL_EventQ.free; + SDL_EventQ.free = entry->next; + } + + entry->event = *event; + if (event->type == SDL_SYSWMEVENT) { + entry->msg = *event->syswm.msg; } - return (added); + + if (SDL_EventQ.tail) { + SDL_EventQ.tail->next = entry; + entry->prev = SDL_EventQ.tail; + SDL_EventQ.tail = entry; + entry->next = NULL; + } else { + SDL_assert(!SDL_EventQ.head); + SDL_EventQ.head = entry; + SDL_EventQ.tail = entry; + entry->prev = NULL; + entry->next = NULL; + } + + return 1; } -/* Cut an event, and return the next valid spot, or the tail */ -/* -- called with the queue locked */ -static int -SDL_CutEvent(int spot) +/* Remove an event from the queue -- called with the queue locked */ +static void +SDL_CutEvent(SDL_EventEntry *entry) { - if (spot == SDL_EventQ.head) { - SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS; - return (SDL_EventQ.head); - } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) { - SDL_EventQ.tail = spot; - return (SDL_EventQ.tail); - } else - /* We cut the middle -- shift everything over */ - { - int here, next; + if (entry->prev) { + entry->prev->next = entry->next; + } + if (entry->next) { + entry->next->prev = entry->prev; + } - /* This can probably be optimized with SDL_memcpy() -- careful! */ - if (--SDL_EventQ.tail < 0) { - SDL_EventQ.tail = MAXEVENTS - 1; - } - for (here = spot; here != SDL_EventQ.tail; here = next) { - next = (here + 1) % MAXEVENTS; - SDL_EventQ.event[here] = SDL_EventQ.event[next]; - } - return (spot); + if (entry == SDL_EventQ.head) { + SDL_assert(entry->prev == NULL); + SDL_EventQ.head = entry->next; + } + if (entry == SDL_EventQ.tail) { + SDL_assert(entry->next == NULL); + SDL_EventQ.tail = entry->prev; } - /* NOTREACHED */ + + entry->next = SDL_EventQ.free; + SDL_EventQ.free = entry; } /* Lock the event queue, take a peep at it, and unlock it */ @@ -209,6 +257,10 @@ SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action, /* Don't look after we've quit */ if (!SDL_EventQ.active) { + /* We get a few spurious events at shutdown, so don't warn then */ + if (action != SDL_ADDEVENT) { + SDL_SetError("The event system has been shut down"); + } return (-1); } /* Lock the event queue */ @@ -219,8 +271,10 @@ SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action, used += SDL_AddEvent(&events[i]); } } else { + SDL_EventEntry *entry, *next; + SDL_SysWMEntry *wmmsg, *wmmsg_next; SDL_Event tmpevent; - int spot; + Uint32 type; /* If 'events' is NULL, just see if they exist */ if (events == NULL) { @@ -228,18 +282,44 @@ SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action, numevents = 1; events = &tmpevent; } - spot = SDL_EventQ.head; - while ((used < numevents) && (spot != SDL_EventQ.tail)) { - Uint32 type = SDL_EventQ.event[spot].type; + + /* Clean out any used wmmsg data + FIXME: Do we want to retain the data for some period of time? + */ + for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) { + wmmsg_next = wmmsg->next; + wmmsg->next = SDL_EventQ.wmmsg_free; + SDL_EventQ.wmmsg_free = wmmsg; + } + SDL_EventQ.wmmsg_used = NULL; + + for (entry = SDL_EventQ.head; entry && used < numevents; entry = next) { + next = entry->next; + type = entry->event.type; if (minType <= type && type <= maxType) { - events[used++] = SDL_EventQ.event[spot]; + events[used] = entry->event; + if (entry->event.type == SDL_SYSWMEVENT) { + /* We need to copy the wmmsg somewhere safe. + For now we'll guarantee it's valid at least until + the next call to SDL_PeepEvents() + */ + SDL_SysWMEntry *wmmsg; + if (SDL_EventQ.wmmsg_free) { + wmmsg = SDL_EventQ.wmmsg_free; + SDL_EventQ.wmmsg_free = wmmsg->next; + } else { + wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg)); + } + wmmsg->msg = *entry->event.syswm.msg; + wmmsg->next = SDL_EventQ.wmmsg_used; + SDL_EventQ.wmmsg_used = wmmsg; + events[used].syswm.msg = &wmmsg->msg; + } + ++used; + if (action == SDL_GETEVENT) { - spot = SDL_CutEvent(spot); - } else { - spot = (spot + 1) % MAXEVENTS; + SDL_CutEvent(entry); } - } else { - spot = (spot + 1) % MAXEVENTS; } } } @@ -286,13 +366,13 @@ SDL_FlushEvents(Uint32 minType, Uint32 maxType) /* Lock the event queue */ if (SDL_LockMutex(SDL_EventQ.lock) == 0) { - int spot = SDL_EventQ.head; - while (spot != SDL_EventQ.tail) { - Uint32 type = SDL_EventQ.event[spot].type; + SDL_EventEntry *entry, *next; + Uint32 type; + for (entry = SDL_EventQ.head; entry; entry = next) { + next = entry->next; + type = entry->event.type; if (minType <= type && type <= maxType) { - spot = SDL_CutEvent(spot); - } else { - spot = (spot + 1) % MAXEVENTS; + SDL_CutEvent(entry); } } SDL_UnlockMutex(SDL_EventQ.lock); @@ -448,18 +528,15 @@ void SDL_FilterEvents(SDL_EventFilter filter, void *userdata) { if (SDL_LockMutex(SDL_EventQ.lock) == 0) { - int spot; - - spot = SDL_EventQ.head; - while (spot != SDL_EventQ.tail) { - if (filter(userdata, &SDL_EventQ.event[spot])) { - spot = (spot + 1) % MAXEVENTS; - } else { - spot = SDL_CutEvent(spot); + SDL_EventEntry *entry, *next; + for (entry = SDL_EventQ.head; entry; entry = next) { + next = entry->next; + if (!filter(userdata, &entry->event)) { + SDL_CutEvent(entry); } } + SDL_UnlockMutex(SDL_EventQ.lock); } - SDL_UnlockMutex(SDL_EventQ.lock); } Uint8