src/events/SDL_events.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 08 Aug 2013 15:04:30 -0700
changeset 7611 ed6f4b8557e4
parent 7306 1516fe08e6ec
child 7719 31b5f9ff36ca
child 8477 ad08f0d710f3
permissions -rw-r--r--
SDL_RegisterEvents() now rejects requests for numevents <= 0.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* General event handling code for SDL */
    24 
    25 #include "SDL.h"
    26 #include "SDL_events.h"
    27 #include "SDL_syswm.h"
    28 #include "SDL_thread.h"
    29 #include "SDL_events_c.h"
    30 #include "../timer/SDL_timer_c.h"
    31 #if !SDL_JOYSTICK_DISABLED
    32 #include "../joystick/SDL_joystick_c.h"
    33 #endif
    34 #include "../video/SDL_sysvideo.h"
    35 
    36 /* An arbitrary limit so we don't have unbounded growth */
    37 #define SDL_MAX_QUEUED_EVENTS   65535
    38 
    39 /* Public data -- the event filter */
    40 SDL_EventFilter SDL_EventOK = NULL;
    41 void *SDL_EventOKParam;
    42 
    43 typedef struct SDL_EventWatcher {
    44     SDL_EventFilter callback;
    45     void *userdata;
    46     struct SDL_EventWatcher *next;
    47 } SDL_EventWatcher;
    48 
    49 static SDL_EventWatcher *SDL_event_watchers = NULL;
    50 
    51 typedef struct {
    52     Uint32 bits[8];
    53 } SDL_DisabledEventBlock;
    54 
    55 static SDL_DisabledEventBlock *SDL_disabled_events[256];
    56 static Uint32 SDL_userevents = SDL_USEREVENT;
    57 
    58 /* Private data -- event queue */
    59 typedef struct _SDL_EventEntry
    60 {
    61     SDL_Event event;
    62     SDL_SysWMmsg msg;
    63     struct _SDL_EventEntry *prev;
    64     struct _SDL_EventEntry *next;
    65 } SDL_EventEntry;
    66 
    67 typedef struct _SDL_SysWMEntry
    68 {
    69     SDL_SysWMmsg msg;
    70     struct _SDL_SysWMEntry *next;
    71 } SDL_SysWMEntry;
    72 
    73 static struct
    74 {
    75     SDL_mutex *lock;
    76     volatile SDL_bool active;
    77     volatile int count;
    78     SDL_EventEntry *head;
    79     SDL_EventEntry *tail;
    80     SDL_EventEntry *free;
    81     SDL_SysWMEntry *wmmsg_used;
    82     SDL_SysWMEntry *wmmsg_free;
    83 } SDL_EventQ = { NULL, SDL_TRUE };
    84 
    85 
    86 static __inline__ SDL_bool
    87 SDL_ShouldPollJoystick()
    88 {
    89 #if !SDL_JOYSTICK_DISABLED
    90     if ((!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] ||
    91          SDL_JoystickEventState(SDL_QUERY)) &&
    92         SDL_PrivateJoystickNeedsPolling()) {
    93         return SDL_TRUE;
    94     }
    95 #endif
    96     return SDL_FALSE;
    97 }
    98 
    99 /* Public functions */
   100 
   101 void
   102 SDL_StopEventLoop(void)
   103 {
   104     int i;
   105     SDL_EventEntry *entry;
   106     SDL_SysWMEntry *wmmsg;
   107 
   108     if (SDL_EventQ.lock) {
   109         SDL_LockMutex(SDL_EventQ.lock);
   110     }
   111 
   112     SDL_EventQ.active = SDL_FALSE;
   113 
   114     /* Clean out EventQ */
   115     for (entry = SDL_EventQ.head; entry; ) {
   116         SDL_EventEntry *next = entry->next;
   117         SDL_free(entry);
   118         entry = next;
   119     }
   120     for (entry = SDL_EventQ.free; entry; ) {
   121         SDL_EventEntry *next = entry->next;
   122         SDL_free(entry);
   123         entry = next;
   124     }
   125     for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; ) {
   126         SDL_SysWMEntry *next = wmmsg->next;
   127         SDL_free(wmmsg);
   128         wmmsg = next;
   129     }
   130     for (wmmsg = SDL_EventQ.wmmsg_free; wmmsg; ) {
   131         SDL_SysWMEntry *next = wmmsg->next;
   132         SDL_free(wmmsg);
   133         wmmsg = next;
   134     }
   135     SDL_EventQ.count = 0;
   136     SDL_EventQ.head = NULL;
   137     SDL_EventQ.tail = NULL;
   138     SDL_EventQ.free = NULL;
   139     SDL_EventQ.wmmsg_used = NULL;
   140     SDL_EventQ.wmmsg_free = NULL;
   141 
   142     /* Clear disabled event state */
   143     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   144         if (SDL_disabled_events[i]) {
   145             SDL_free(SDL_disabled_events[i]);
   146             SDL_disabled_events[i] = NULL;
   147         }
   148     }
   149 
   150     while (SDL_event_watchers) {
   151         SDL_EventWatcher *tmp = SDL_event_watchers;
   152         SDL_event_watchers = tmp->next;
   153         SDL_free(tmp);
   154     }
   155     SDL_EventOK = NULL;
   156 
   157     if (SDL_EventQ.lock) {
   158         SDL_UnlockMutex(SDL_EventQ.lock);
   159         SDL_DestroyMutex(SDL_EventQ.lock);
   160         SDL_EventQ.lock = NULL;
   161     }
   162 }
   163 
   164 /* This function (and associated calls) may be called more than once */
   165 int
   166 SDL_StartEventLoop(void)
   167 {
   168     /* We'll leave the event queue alone, since we might have gotten
   169        some important events at launch (like SDL_DROPFILE)
   170 
   171        FIXME: Does this introduce any other bugs with events at startup?
   172      */
   173 
   174     /* Create the lock and set ourselves active */
   175 #if !SDL_THREADS_DISABLED
   176     if (!SDL_EventQ.lock) {
   177         SDL_EventQ.lock = SDL_CreateMutex();
   178     }
   179     if (SDL_EventQ.lock == NULL) {
   180         return (-1);
   181     }
   182 #endif /* !SDL_THREADS_DISABLED */
   183 
   184     /* Process most event types */
   185     SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
   186     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
   187     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   188 
   189     SDL_EventQ.active = SDL_TRUE;
   190 
   191     return (0);
   192 }
   193 
   194 
   195 /* Add an event to the event queue -- called with the queue locked */
   196 static int
   197 SDL_AddEvent(SDL_Event * event)
   198 {
   199     SDL_EventEntry *entry;
   200 
   201     if (SDL_EventQ.count >= SDL_MAX_QUEUED_EVENTS) {
   202         SDL_SetError("Event queue is full (%d events)", SDL_EventQ.count);
   203         return 0;
   204     }
   205 
   206     if (SDL_EventQ.free == NULL) {
   207         entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry));
   208         if (!entry) {
   209             return 0;
   210         }
   211     } else {
   212         entry = SDL_EventQ.free;
   213         SDL_EventQ.free = entry->next;
   214     }
   215 
   216     entry->event = *event;
   217     if (event->type == SDL_SYSWMEVENT) {
   218         entry->msg = *event->syswm.msg;
   219         entry->event.syswm.msg = &entry->msg;
   220     }
   221 
   222     if (SDL_EventQ.tail) {
   223         SDL_EventQ.tail->next = entry;
   224         entry->prev = SDL_EventQ.tail;
   225         SDL_EventQ.tail = entry;
   226         entry->next = NULL;
   227     } else {
   228         SDL_assert(!SDL_EventQ.head);
   229         SDL_EventQ.head = entry;
   230         SDL_EventQ.tail = entry;
   231         entry->prev = NULL;
   232         entry->next = NULL;
   233     }
   234     ++SDL_EventQ.count;
   235 
   236     return 1;
   237 }
   238 
   239 /* Remove an event from the queue -- called with the queue locked */
   240 static void
   241 SDL_CutEvent(SDL_EventEntry *entry)
   242 {
   243     if (entry->prev) {
   244         entry->prev->next = entry->next;
   245     }
   246     if (entry->next) {
   247         entry->next->prev = entry->prev;
   248     }
   249 
   250     if (entry == SDL_EventQ.head) {
   251         SDL_assert(entry->prev == NULL);
   252         SDL_EventQ.head = entry->next;
   253     }
   254     if (entry == SDL_EventQ.tail) {
   255         SDL_assert(entry->next == NULL);
   256         SDL_EventQ.tail = entry->prev;
   257     }
   258 
   259     entry->next = SDL_EventQ.free;
   260     SDL_EventQ.free = entry;
   261     SDL_assert(SDL_EventQ.count > 0);
   262     --SDL_EventQ.count;
   263 }
   264 
   265 /* Lock the event queue, take a peep at it, and unlock it */
   266 int
   267 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   268                Uint32 minType, Uint32 maxType)
   269 {
   270     int i, used;
   271 
   272     /* Don't look after we've quit */
   273     if (!SDL_EventQ.active) {
   274         /* We get a few spurious events at shutdown, so don't warn then */
   275         if (action != SDL_ADDEVENT) {
   276             SDL_SetError("The event system has been shut down");
   277         }
   278         return (-1);
   279     }
   280     /* Lock the event queue */
   281     used = 0;
   282     if (!SDL_EventQ.lock || SDL_LockMutex(SDL_EventQ.lock) == 0) {
   283         if (action == SDL_ADDEVENT) {
   284             for (i = 0; i < numevents; ++i) {
   285                 used += SDL_AddEvent(&events[i]);
   286             }
   287         } else {
   288             SDL_EventEntry *entry, *next;
   289             SDL_SysWMEntry *wmmsg, *wmmsg_next;
   290             SDL_Event tmpevent;
   291             Uint32 type;
   292 
   293             /* If 'events' is NULL, just see if they exist */
   294             if (events == NULL) {
   295                 action = SDL_PEEKEVENT;
   296                 numevents = 1;
   297                 events = &tmpevent;
   298             }
   299 
   300             /* Clean out any used wmmsg data
   301                FIXME: Do we want to retain the data for some period of time?
   302              */
   303             for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; wmmsg = wmmsg_next) {
   304                 wmmsg_next = wmmsg->next;
   305                 wmmsg->next = SDL_EventQ.wmmsg_free;
   306                 SDL_EventQ.wmmsg_free = wmmsg;
   307             }
   308             SDL_EventQ.wmmsg_used = NULL;
   309 
   310             for (entry = SDL_EventQ.head; entry && used < numevents; entry = next) {
   311                 next = entry->next;
   312                 type = entry->event.type;
   313                 if (minType <= type && type <= maxType) {
   314                     events[used] = entry->event;
   315                     if (entry->event.type == SDL_SYSWMEVENT) {
   316                         /* We need to copy the wmmsg somewhere safe.
   317                            For now we'll guarantee it's valid at least until
   318                            the next call to SDL_PeepEvents()
   319                          */
   320                         SDL_SysWMEntry *wmmsg;
   321                         if (SDL_EventQ.wmmsg_free) {
   322                             wmmsg = SDL_EventQ.wmmsg_free;
   323                             SDL_EventQ.wmmsg_free = wmmsg->next;
   324                         } else {
   325                             wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
   326                         }
   327                         wmmsg->msg = *entry->event.syswm.msg;
   328                         wmmsg->next = SDL_EventQ.wmmsg_used;
   329                         SDL_EventQ.wmmsg_used = wmmsg;
   330                         events[used].syswm.msg = &wmmsg->msg;
   331                     }
   332                     ++used;
   333 
   334                     if (action == SDL_GETEVENT) {
   335                         SDL_CutEvent(entry);
   336                     }
   337                 }
   338             }
   339         }
   340         SDL_UnlockMutex(SDL_EventQ.lock);
   341     } else {
   342         return SDL_SetError("Couldn't lock event queue");
   343     }
   344     return (used);
   345 }
   346 
   347 SDL_bool
   348 SDL_HasEvent(Uint32 type)
   349 {
   350     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   351 }
   352 
   353 SDL_bool
   354 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   355 {
   356     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   357 }
   358 
   359 void
   360 SDL_FlushEvent(Uint32 type)
   361 {
   362     SDL_FlushEvents(type, type);
   363 }
   364 
   365 void
   366 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   367 {
   368     /* Don't look after we've quit */
   369     if (!SDL_EventQ.active) {
   370         return;
   371     }
   372 
   373     /* Make sure the events are current */
   374 #if 0
   375     /* Actually, we can't do this since we might be flushing while processing
   376        a resize event, and calling this might trigger further resize events.
   377     */
   378     SDL_PumpEvents();
   379 #endif
   380 
   381     /* Lock the event queue */
   382     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   383         SDL_EventEntry *entry, *next;
   384         Uint32 type;
   385         for (entry = SDL_EventQ.head; entry; entry = next) {
   386             next = entry->next;
   387             type = entry->event.type;
   388             if (minType <= type && type <= maxType) {
   389                 SDL_CutEvent(entry);
   390             }
   391         }
   392         SDL_UnlockMutex(SDL_EventQ.lock);
   393     }
   394 }
   395 
   396 /* Run the system dependent event loops */
   397 void
   398 SDL_PumpEvents(void)
   399 {
   400     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   401 
   402     /* Get events from the video subsystem */
   403     if (_this) {
   404         _this->PumpEvents(_this);
   405     }
   406 #if !SDL_JOYSTICK_DISABLED
   407     /* Check for joystick state change */
   408     if (SDL_ShouldPollJoystick()) {
   409         SDL_JoystickUpdate();
   410     }
   411 #endif
   412 }
   413 
   414 /* Public functions */
   415 
   416 int
   417 SDL_PollEvent(SDL_Event * event)
   418 {
   419     return SDL_WaitEventTimeout(event, 0);
   420 }
   421 
   422 int
   423 SDL_WaitEvent(SDL_Event * event)
   424 {
   425     return SDL_WaitEventTimeout(event, -1);
   426 }
   427 
   428 int
   429 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   430 {
   431     Uint32 expiration = 0;
   432 
   433     if (timeout > 0)
   434         expiration = SDL_GetTicks() + timeout;
   435 
   436     for (;;) {
   437         SDL_PumpEvents();
   438         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   439         case -1:
   440             return 0;
   441         case 1:
   442             return 1;
   443         case 0:
   444             if (timeout == 0) {
   445                 /* Polling and no events, just return */
   446                 return 0;
   447             }
   448             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   449                 /* Timeout expired and no events */
   450                 return 0;
   451             }
   452             SDL_Delay(10);
   453             break;
   454         }
   455     }
   456 }
   457 
   458 int
   459 SDL_PushEvent(SDL_Event * event)
   460 {
   461     SDL_EventWatcher *curr;
   462 
   463     event->common.timestamp = SDL_GetTicks();
   464 
   465     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   466         return 0;
   467     }
   468 
   469     for (curr = SDL_event_watchers; curr; curr = curr->next) {
   470         curr->callback(curr->userdata, event);
   471     }
   472 
   473     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   474         return -1;
   475     }
   476 
   477     SDL_GestureProcessEvent(event);
   478 
   479     return 1;
   480 }
   481 
   482 void
   483 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   484 {
   485     /* Set filter and discard pending events */
   486     SDL_EventOK = NULL;
   487     SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
   488     SDL_EventOKParam = userdata;
   489     SDL_EventOK = filter;
   490 }
   491 
   492 SDL_bool
   493 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   494 {
   495     if (filter) {
   496         *filter = SDL_EventOK;
   497     }
   498     if (userdata) {
   499         *userdata = SDL_EventOKParam;
   500     }
   501     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   502 }
   503 
   504 /* FIXME: This is not thread-safe yet */
   505 void
   506 SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
   507 {
   508     SDL_EventWatcher *watcher;
   509 
   510     watcher = (SDL_EventWatcher *)SDL_malloc(sizeof(*watcher));
   511     if (!watcher) {
   512         /* Uh oh... */
   513         return;
   514     }
   515     watcher->callback = filter;
   516     watcher->userdata = userdata;
   517     watcher->next = SDL_event_watchers;
   518     SDL_event_watchers = watcher;
   519 }
   520 
   521 /* FIXME: This is not thread-safe yet */
   522 void
   523 SDL_DelEventWatch(SDL_EventFilter filter, void *userdata)
   524 {
   525     SDL_EventWatcher *prev = NULL;
   526     SDL_EventWatcher *curr;
   527 
   528     for (curr = SDL_event_watchers; curr; prev = curr, curr = curr->next) {
   529         if (curr->callback == filter && curr->userdata == userdata) {
   530             if (prev) {
   531                 prev->next = curr->next;
   532             } else {
   533                 SDL_event_watchers = curr->next;
   534             }
   535             SDL_free(curr);
   536             break;
   537         }
   538     }
   539 }
   540 
   541 void
   542 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   543 {
   544     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   545         SDL_EventEntry *entry, *next;
   546         for (entry = SDL_EventQ.head; entry; entry = next) {
   547             next = entry->next;
   548             if (!filter(userdata, &entry->event)) {
   549                 SDL_CutEvent(entry);
   550             }
   551         }
   552         SDL_UnlockMutex(SDL_EventQ.lock);
   553     }
   554 }
   555 
   556 Uint8
   557 SDL_EventState(Uint32 type, int state)
   558 {
   559     Uint8 current_state;
   560     Uint8 hi = ((type >> 8) & 0xff);
   561     Uint8 lo = (type & 0xff);
   562 
   563     if (SDL_disabled_events[hi] &&
   564         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   565         current_state = SDL_DISABLE;
   566     } else {
   567         current_state = SDL_ENABLE;
   568     }
   569 
   570     if (state != current_state)
   571     {
   572         switch (state) {
   573         case SDL_DISABLE:
   574             /* Disable this event type and discard pending events */
   575             if (!SDL_disabled_events[hi]) {
   576                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   577                 if (!SDL_disabled_events[hi]) {
   578                     /* Out of memory, nothing we can do... */
   579                     break;
   580                 }
   581             }
   582             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   583             SDL_FlushEvent(type);
   584             break;
   585         case SDL_ENABLE:
   586             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   587             break;
   588         default:
   589             /* Querying state... */
   590             break;
   591         }
   592     }
   593 
   594     return current_state;
   595 }
   596 
   597 Uint32
   598 SDL_RegisterEvents(int numevents)
   599 {
   600     Uint32 event_base;
   601 
   602     if ((numevents > 0) && (SDL_userevents+numevents <= SDL_LASTEVENT)) {
   603         event_base = SDL_userevents;
   604         SDL_userevents += numevents;
   605     } else {
   606         event_base = (Uint32)-1;
   607     }
   608     return event_base;
   609 }
   610 
   611 int
   612 SDL_SendAppEvent(SDL_EventType eventType)
   613 {
   614     int posted;
   615 
   616     posted = 0;
   617     if (SDL_GetEventState(eventType) == SDL_ENABLE) {
   618         SDL_Event event;
   619         event.type = eventType;
   620         posted = (SDL_PushEvent(&event) > 0);
   621     }
   622     return (posted);
   623 }
   624 
   625 int
   626 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   627 {
   628     int posted;
   629 
   630     posted = 0;
   631     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   632         SDL_Event event;
   633         SDL_memset(&event, 0, sizeof(event));
   634         event.type = SDL_SYSWMEVENT;
   635         event.syswm.msg = message;
   636         posted = (SDL_PushEvent(&event) > 0);
   637     }
   638     /* Update internal event state */
   639     return (posted);
   640 }
   641 
   642 /* vi: set ts=4 sw=4 expandtab: */