src/events/SDL_events.c
changeset 8477 ad08f0d710f3
parent 8465 be2dce468a8c
parent 7611 ed6f4b8557e4
child 8535 e8ee0708ef5c
     1.1 --- a/src/events/SDL_events.c	Sat Jun 08 14:34:09 2013 -0400
     1.2 +++ b/src/events/SDL_events.c	Mon Aug 12 22:29:55 2013 -0400
     1.3 @@ -33,6 +33,9 @@
     1.4  #endif
     1.5  #include "../video/SDL_sysvideo.h"
     1.6  
     1.7 +/* An arbitrary limit so we don't have unbounded growth */
     1.8 +#define SDL_MAX_QUEUED_EVENTS   65535
     1.9 +
    1.10  /* Public data -- the event filter */
    1.11  SDL_EventFilter SDL_EventOK = NULL;
    1.12  void *SDL_EventOKParam;
    1.13 @@ -53,17 +56,31 @@
    1.14  static Uint32 SDL_userevents = SDL_USEREVENT;
    1.15  
    1.16  /* Private data -- event queue */
    1.17 -#define MAXEVENTS	128
    1.18 +typedef struct _SDL_EventEntry
    1.19 +{
    1.20 +    SDL_Event event;
    1.21 +    SDL_SysWMmsg msg;
    1.22 +    struct _SDL_EventEntry *prev;
    1.23 +    struct _SDL_EventEntry *next;
    1.24 +} SDL_EventEntry;
    1.25 +
    1.26 +typedef struct _SDL_SysWMEntry
    1.27 +{
    1.28 +    SDL_SysWMmsg msg;
    1.29 +    struct _SDL_SysWMEntry *next;
    1.30 +} SDL_SysWMEntry;
    1.31 +
    1.32  static struct
    1.33  {
    1.34      SDL_mutex *lock;
    1.35 -    int active;
    1.36 -    int head;
    1.37 -    int tail;
    1.38 -    SDL_Event event[MAXEVENTS];
    1.39 -    int wmmsg_next;
    1.40 -    struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    1.41 -} SDL_EventQ = { NULL, 1 };
    1.42 +    volatile SDL_bool active;
    1.43 +    volatile int count;
    1.44 +    SDL_EventEntry *head;
    1.45 +    SDL_EventEntry *tail;
    1.46 +    SDL_EventEntry *free;
    1.47 +    SDL_SysWMEntry *wmmsg_used;
    1.48 +    SDL_SysWMEntry *wmmsg_free;
    1.49 +} SDL_EventQ = { NULL, SDL_TRUE };
    1.50  
    1.51  
    1.52  static __inline__ SDL_bool
    1.53 @@ -85,18 +102,42 @@
    1.54  SDL_StopEventLoop(void)
    1.55  {
    1.56      int i;
    1.57 -
    1.58 -    SDL_EventQ.active = 0;
    1.59 +    SDL_EventEntry *entry;
    1.60 +    SDL_SysWMEntry *wmmsg;
    1.61  
    1.62      if (SDL_EventQ.lock) {
    1.63 -        SDL_DestroyMutex(SDL_EventQ.lock);
    1.64 -        SDL_EventQ.lock = NULL;
    1.65 +        SDL_LockMutex(SDL_EventQ.lock);
    1.66      }
    1.67  
    1.68 +    SDL_EventQ.active = SDL_FALSE;
    1.69 +
    1.70      /* Clean out EventQ */
    1.71 -    SDL_EventQ.head = 0;
    1.72 -    SDL_EventQ.tail = 0;
    1.73 -    SDL_EventQ.wmmsg_next = 0;
    1.74 +    for (entry = SDL_EventQ.head; entry; ) {
    1.75 +        SDL_EventEntry *next = entry->next;
    1.76 +        SDL_free(entry);
    1.77 +        entry = next;
    1.78 +    }
    1.79 +    for (entry = SDL_EventQ.free; entry; ) {
    1.80 +        SDL_EventEntry *next = entry->next;
    1.81 +        SDL_free(entry);
    1.82 +        entry = next;
    1.83 +    }
    1.84 +    for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; ) {
    1.85 +        SDL_SysWMEntry *next = wmmsg->next;
    1.86 +        SDL_free(wmmsg);
    1.87 +        wmmsg = next;
    1.88 +    }
    1.89 +    for (wmmsg = SDL_EventQ.wmmsg_free; wmmsg; ) {
    1.90 +        SDL_SysWMEntry *next = wmmsg->next;
    1.91 +        SDL_free(wmmsg);
    1.92 +        wmmsg = next;
    1.93 +    }
    1.94 +    SDL_EventQ.count = 0;
    1.95 +    SDL_EventQ.head = NULL;
    1.96 +    SDL_EventQ.tail = NULL;
    1.97 +    SDL_EventQ.free = NULL;
    1.98 +    SDL_EventQ.wmmsg_used = NULL;
    1.99 +    SDL_EventQ.wmmsg_free = NULL;
   1.100  
   1.101      /* Clear disabled event state */
   1.102      for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   1.103 @@ -111,6 +152,13 @@
   1.104          SDL_event_watchers = tmp->next;
   1.105          SDL_free(tmp);
   1.106      }
   1.107 +    SDL_EventOK = NULL;
   1.108 +
   1.109 +    if (SDL_EventQ.lock) {
   1.110 +        SDL_UnlockMutex(SDL_EventQ.lock);
   1.111 +        SDL_DestroyMutex(SDL_EventQ.lock);
   1.112 +        SDL_EventQ.lock = NULL;
   1.113 +    }
   1.114  }
   1.115  
   1.116  /* This function (and associated calls) may be called more than once */
   1.117 @@ -133,13 +181,12 @@
   1.118      }
   1.119  #endif /* !SDL_THREADS_DISABLED */
   1.120  
   1.121 -    /* No filter to start with, process most event types */
   1.122 -    SDL_EventOK = NULL;
   1.123 +    /* Process most event types */
   1.124      SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
   1.125      SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
   1.126      SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   1.127  
   1.128 -    SDL_EventQ.active = 1;
   1.129 +    SDL_EventQ.active = SDL_TRUE;
   1.130  
   1.131      return (0);
   1.132  }
   1.133 @@ -149,55 +196,70 @@
   1.134  static int
   1.135  SDL_AddEvent(SDL_Event * event)
   1.136  {
   1.137 -    int tail, added;
   1.138 +    SDL_EventEntry *entry;
   1.139  
   1.140 -    tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   1.141 -    if (tail == SDL_EventQ.head) {
   1.142 -        /* Overflow, drop event */
   1.143 -        added = 0;
   1.144 +    if (SDL_EventQ.count >= SDL_MAX_QUEUED_EVENTS) {
   1.145 +        SDL_SetError("Event queue is full (%d events)", SDL_EventQ.count);
   1.146 +        return 0;
   1.147 +    }
   1.148 +
   1.149 +    if (SDL_EventQ.free == NULL) {
   1.150 +        entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry));
   1.151 +        if (!entry) {
   1.152 +            return 0;
   1.153 +        }
   1.154      } else {
   1.155 -        SDL_EventQ.event[SDL_EventQ.tail] = *event;
   1.156 -        if (event->type == SDL_SYSWMEVENT) {
   1.157 -            /* Note that it's possible to lose an event */
   1.158 -            int next = SDL_EventQ.wmmsg_next;
   1.159 -            SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   1.160 -            SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   1.161 -                &SDL_EventQ.wmmsg[next];
   1.162 -            SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   1.163 -        }
   1.164 -        SDL_EventQ.tail = tail;
   1.165 -        added = 1;
   1.166 +        entry = SDL_EventQ.free;
   1.167 +        SDL_EventQ.free = entry->next;
   1.168 +    }
   1.169 +
   1.170 +    entry->event = *event;
   1.171 +    if (event->type == SDL_SYSWMEVENT) {
   1.172 +        entry->msg = *event->syswm.msg;
   1.173 +        entry->event.syswm.msg = &entry->msg;
   1.174      }
   1.175 -    return (added);
   1.176 +
   1.177 +    if (SDL_EventQ.tail) {
   1.178 +        SDL_EventQ.tail->next = entry;
   1.179 +        entry->prev = SDL_EventQ.tail;
   1.180 +        SDL_EventQ.tail = entry;
   1.181 +        entry->next = NULL;
   1.182 +    } else {
   1.183 +        SDL_assert(!SDL_EventQ.head);
   1.184 +        SDL_EventQ.head = entry;
   1.185 +        SDL_EventQ.tail = entry;
   1.186 +        entry->prev = NULL;
   1.187 +        entry->next = NULL;
   1.188 +    }
   1.189 +    ++SDL_EventQ.count;
   1.190 +
   1.191 +    return 1;
   1.192  }
   1.193  
   1.194 -/* Cut an event, and return the next valid spot, or the tail */
   1.195 -/*                           -- called with the queue locked */
   1.196 -static int
   1.197 -SDL_CutEvent(int spot)
   1.198 +/* Remove an event from the queue -- called with the queue locked */
   1.199 +static void
   1.200 +SDL_CutEvent(SDL_EventEntry *entry)
   1.201  {
   1.202 -    if (spot == SDL_EventQ.head) {
   1.203 -        SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   1.204 -        return (SDL_EventQ.head);
   1.205 -    } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   1.206 -        SDL_EventQ.tail = spot;
   1.207 -        return (SDL_EventQ.tail);
   1.208 -    } else
   1.209 -        /* We cut the middle -- shift everything over */
   1.210 -    {
   1.211 -        int here, next;
   1.212 +    if (entry->prev) {
   1.213 +        entry->prev->next = entry->next;
   1.214 +    }
   1.215 +    if (entry->next) {
   1.216 +        entry->next->prev = entry->prev;
   1.217 +    }
   1.218  
   1.219 -        /* This can probably be optimized with SDL_memcpy() -- careful! */
   1.220 -        if (--SDL_EventQ.tail < 0) {
   1.221 -            SDL_EventQ.tail = MAXEVENTS - 1;
   1.222 -        }
   1.223 -        for (here = spot; here != SDL_EventQ.tail; here = next) {
   1.224 -            next = (here + 1) % MAXEVENTS;
   1.225 -            SDL_EventQ.event[here] = SDL_EventQ.event[next];
   1.226 -        }
   1.227 -        return (spot);
   1.228 +    if (entry == SDL_EventQ.head) {
   1.229 +        SDL_assert(entry->prev == NULL);
   1.230 +        SDL_EventQ.head = entry->next;
   1.231      }
   1.232 -    /* NOTREACHED */
   1.233 +    if (entry == SDL_EventQ.tail) {
   1.234 +        SDL_assert(entry->next == NULL);
   1.235 +        SDL_EventQ.tail = entry->prev;
   1.236 +    }
   1.237 +
   1.238 +    entry->next = SDL_EventQ.free;
   1.239 +    SDL_EventQ.free = entry;
   1.240 +    SDL_assert(SDL_EventQ.count > 0);
   1.241 +    --SDL_EventQ.count;
   1.242  }
   1.243  
   1.244  /* Lock the event queue, take a peep at it, and unlock it */
   1.245 @@ -209,6 +271,10 @@
   1.246  
   1.247      /* Don't look after we've quit */
   1.248      if (!SDL_EventQ.active) {
   1.249 +        /* We get a few spurious events at shutdown, so don't warn then */
   1.250 +        if (action != SDL_ADDEVENT) {
   1.251 +            SDL_SetError("The event system has been shut down");
   1.252 +        }
   1.253          return (-1);
   1.254      }
   1.255      /* Lock the event queue */
   1.256 @@ -219,8 +285,10 @@
   1.257                  used += SDL_AddEvent(&events[i]);
   1.258              }
   1.259          } else {
   1.260 +            SDL_EventEntry *entry, *next;
   1.261 +            SDL_SysWMEntry *wmmsg, *wmmsg_next;
   1.262              SDL_Event tmpevent;
   1.263 -            int spot;
   1.264 +            Uint32 type;
   1.265  
   1.266              /* If 'events' is NULL, just see if they exist */
   1.267              if (events == NULL) {
   1.268 @@ -228,18 +296,44 @@
   1.269                  numevents = 1;
   1.270                  events = &tmpevent;
   1.271              }
   1.272 -            spot = SDL_EventQ.head;
   1.273 -            while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   1.274 -                Uint32 type = SDL_EventQ.event[spot].type;
   1.275 +
   1.276 +            /* Clean out any used wmmsg data
   1.277 +               FIXME: Do we want to retain the data for some period of time?
   1.278 +             */
   1.279 +            for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) {
   1.280 +                wmmsg_next = wmmsg->next;
   1.281 +                wmmsg->next = SDL_EventQ.wmmsg_free;
   1.282 +                SDL_EventQ.wmmsg_free = wmmsg;
   1.283 +            }
   1.284 +            SDL_EventQ.wmmsg_used = NULL;
   1.285 +
   1.286 +            for (entry = SDL_EventQ.head; entry && used < numevents; entry = next) {
   1.287 +                next = entry->next;
   1.288 +                type = entry->event.type;
   1.289                  if (minType <= type && type <= maxType) {
   1.290 -                    events[used++] = SDL_EventQ.event[spot];
   1.291 +                    events[used] = entry->event;
   1.292 +                    if (entry->event.type == SDL_SYSWMEVENT) {
   1.293 +                        /* We need to copy the wmmsg somewhere safe.
   1.294 +                           For now we'll guarantee it's valid at least until
   1.295 +                           the next call to SDL_PeepEvents()
   1.296 +                         */
   1.297 +                        SDL_SysWMEntry *wmmsg;
   1.298 +                        if (SDL_EventQ.wmmsg_free) {
   1.299 +                            wmmsg = SDL_EventQ.wmmsg_free;
   1.300 +                            SDL_EventQ.wmmsg_free = wmmsg->next;
   1.301 +                        } else {
   1.302 +                            wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
   1.303 +                        }
   1.304 +                        wmmsg->msg = *entry->event.syswm.msg;
   1.305 +                        wmmsg->next = SDL_EventQ.wmmsg_used;
   1.306 +                        SDL_EventQ.wmmsg_used = wmmsg;
   1.307 +                        events[used].syswm.msg = &wmmsg->msg;
   1.308 +                    }
   1.309 +                    ++used;
   1.310 +
   1.311                      if (action == SDL_GETEVENT) {
   1.312 -                        spot = SDL_CutEvent(spot);
   1.313 -                    } else {
   1.314 -                        spot = (spot + 1) % MAXEVENTS;
   1.315 +                        SDL_CutEvent(entry);
   1.316                      }
   1.317 -                } else {
   1.318 -                    spot = (spot + 1) % MAXEVENTS;
   1.319                  }
   1.320              }
   1.321          }
   1.322 @@ -286,13 +380,13 @@
   1.323  
   1.324      /* Lock the event queue */
   1.325      if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   1.326 -        int spot = SDL_EventQ.head;
   1.327 -        while (spot != SDL_EventQ.tail) {
   1.328 -            Uint32 type = SDL_EventQ.event[spot].type;
   1.329 +        SDL_EventEntry *entry, *next;
   1.330 +        Uint32 type;
   1.331 +        for (entry = SDL_EventQ.head; entry; entry = next) {
   1.332 +            next = entry->next;
   1.333 +            type = entry->event.type;
   1.334              if (minType <= type && type <= maxType) {
   1.335 -                spot = SDL_CutEvent(spot);
   1.336 -            } else {
   1.337 -                spot = (spot + 1) % MAXEVENTS;
   1.338 +                SDL_CutEvent(entry);
   1.339              }
   1.340          }
   1.341          SDL_UnlockMutex(SDL_EventQ.lock);
   1.342 @@ -365,7 +459,9 @@
   1.343  SDL_PushEvent(SDL_Event * event)
   1.344  {
   1.345      SDL_EventWatcher *curr;
   1.346 +
   1.347      event->common.timestamp = SDL_GetTicks();
   1.348 +
   1.349      if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   1.350          return 0;
   1.351      }
   1.352 @@ -386,12 +482,11 @@
   1.353  void
   1.354  SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   1.355  {
   1.356 -    SDL_Event bitbucket;
   1.357 -
   1.358      /* Set filter and discard pending events */
   1.359 -    SDL_EventOK = filter;
   1.360 +    SDL_EventOK = NULL;
   1.361 +    SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
   1.362      SDL_EventOKParam = userdata;
   1.363 -    while (SDL_PollEvent(&bitbucket) > 0);
   1.364 +    SDL_EventOK = filter;
   1.365  }
   1.366  
   1.367  SDL_bool
   1.368 @@ -447,18 +542,15 @@
   1.369  SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   1.370  {
   1.371      if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   1.372 -        int spot;
   1.373 -
   1.374 -        spot = SDL_EventQ.head;
   1.375 -        while (spot != SDL_EventQ.tail) {
   1.376 -            if (filter(userdata, &SDL_EventQ.event[spot])) {
   1.377 -                spot = (spot + 1) % MAXEVENTS;
   1.378 -            } else {
   1.379 -                spot = SDL_CutEvent(spot);
   1.380 +        SDL_EventEntry *entry, *next;
   1.381 +        for (entry = SDL_EventQ.head; entry; entry = next) {
   1.382 +            next = entry->next;
   1.383 +            if (!filter(userdata, &entry->event)) {
   1.384 +                SDL_CutEvent(entry);
   1.385              }
   1.386          }
   1.387 +        SDL_UnlockMutex(SDL_EventQ.lock);
   1.388      }
   1.389 -    SDL_UnlockMutex(SDL_EventQ.lock);
   1.390  }
   1.391  
   1.392  Uint8
   1.393 @@ -507,7 +599,7 @@
   1.394  {
   1.395      Uint32 event_base;
   1.396  
   1.397 -    if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   1.398 +    if ((numevents > 0) && (SDL_userevents+numevents <= SDL_LASTEVENT)) {
   1.399          event_base = SDL_userevents;
   1.400          SDL_userevents += numevents;
   1.401      } else {
   1.402 @@ -516,8 +608,20 @@
   1.403      return event_base;
   1.404  }
   1.405  
   1.406 -/* This is a generic event handler.
   1.407 - */
   1.408 +int
   1.409 +SDL_SendAppEvent(SDL_EventType eventType)
   1.410 +{
   1.411 +    int posted;
   1.412 +
   1.413 +    posted = 0;
   1.414 +    if (SDL_GetEventState(eventType) == SDL_ENABLE) {
   1.415 +        SDL_Event event;
   1.416 +        event.type = eventType;
   1.417 +        posted = (SDL_PushEvent(&event) > 0);
   1.418 +    }
   1.419 +    return (posted);
   1.420 +}
   1.421 +
   1.422  int
   1.423  SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   1.424  {