src/events/SDL_events.c
author Ryan C. Gordon
Sun, 03 Jan 2016 06:50:50 -0500
changeset 10003 d91a2c45825e
parent 9998 f67cf37e9cd4
child 10060 739bc5c7d339
permissions -rw-r--r--
Remove almost all instances of "volatile" keyword.

As Tiffany pointed out in Bugzilla, volatile is not useful for thread safety:

https://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/

Some of these volatiles didn't need to be, some were otherwise protected by
spinlocks or mutexes, and some got moved over to SDL_atomic_t data, etc.

Fixes Bugzilla #3220.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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_internal.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     SDL_atomic_t active;
    77     SDL_atomic_t count;
    78     int max_events_seen;
    79     SDL_EventEntry *head;
    80     SDL_EventEntry *tail;
    81     SDL_EventEntry *free;
    82     SDL_SysWMEntry *wmmsg_used;
    83     SDL_SysWMEntry *wmmsg_free;
    84 } SDL_EventQ = { NULL, { 1 }, { 0 }, 0, NULL, NULL, NULL, NULL, NULL };
    85 
    86 
    87 /* Public functions */
    88 
    89 void
    90 SDL_StopEventLoop(void)
    91 {
    92     const char *report = SDL_GetHint("SDL_EVENT_QUEUE_STATISTICS");
    93     int i;
    94     SDL_EventEntry *entry;
    95     SDL_SysWMEntry *wmmsg;
    96 
    97     if (SDL_EventQ.lock) {
    98         SDL_LockMutex(SDL_EventQ.lock);
    99     }
   100 
   101     SDL_AtomicSet(&SDL_EventQ.active, 0);
   102 
   103     if (report && SDL_atoi(report)) {
   104         SDL_Log("SDL EVENT QUEUE: Maximum events in-flight: %d\n",
   105                 SDL_EventQ.max_events_seen);
   106     }
   107 
   108     /* Clean out EventQ */
   109     for (entry = SDL_EventQ.head; entry; ) {
   110         SDL_EventEntry *next = entry->next;
   111         SDL_free(entry);
   112         entry = next;
   113     }
   114     for (entry = SDL_EventQ.free; entry; ) {
   115         SDL_EventEntry *next = entry->next;
   116         SDL_free(entry);
   117         entry = next;
   118     }
   119     for (wmmsg = SDL_EventQ.wmmsg_used; wmmsg; ) {
   120         SDL_SysWMEntry *next = wmmsg->next;
   121         SDL_free(wmmsg);
   122         wmmsg = next;
   123     }
   124     for (wmmsg = SDL_EventQ.wmmsg_free; wmmsg; ) {
   125         SDL_SysWMEntry *next = wmmsg->next;
   126         SDL_free(wmmsg);
   127         wmmsg = next;
   128     }
   129 
   130     SDL_AtomicSet(&SDL_EventQ.count, 0);
   131     SDL_EventQ.max_events_seen = 0;
   132     SDL_EventQ.head = NULL;
   133     SDL_EventQ.tail = NULL;
   134     SDL_EventQ.free = NULL;
   135     SDL_EventQ.wmmsg_used = NULL;
   136     SDL_EventQ.wmmsg_free = NULL;
   137 
   138     /* Clear disabled event state */
   139     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   140         SDL_free(SDL_disabled_events[i]);
   141         SDL_disabled_events[i] = NULL;
   142     }
   143 
   144     while (SDL_event_watchers) {
   145         SDL_EventWatcher *tmp = SDL_event_watchers;
   146         SDL_event_watchers = tmp->next;
   147         SDL_free(tmp);
   148     }
   149     SDL_EventOK = NULL;
   150 
   151     if (SDL_EventQ.lock) {
   152         SDL_UnlockMutex(SDL_EventQ.lock);
   153         SDL_DestroyMutex(SDL_EventQ.lock);
   154         SDL_EventQ.lock = NULL;
   155     }
   156 }
   157 
   158 /* This function (and associated calls) may be called more than once */
   159 int
   160 SDL_StartEventLoop(void)
   161 {
   162     /* We'll leave the event queue alone, since we might have gotten
   163        some important events at launch (like SDL_DROPFILE)
   164 
   165        FIXME: Does this introduce any other bugs with events at startup?
   166      */
   167 
   168     /* Create the lock and set ourselves active */
   169 #if !SDL_THREADS_DISABLED
   170     if (!SDL_EventQ.lock) {
   171         SDL_EventQ.lock = SDL_CreateMutex();
   172     }
   173     if (SDL_EventQ.lock == NULL) {
   174         return -1;
   175     }
   176 #endif /* !SDL_THREADS_DISABLED */
   177 
   178     /* Process most event types */
   179     SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
   180     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
   181     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   182 
   183     SDL_AtomicSet(&SDL_EventQ.active, 1);
   184 
   185     return 0;
   186 }
   187 
   188 
   189 /* Add an event to the event queue -- called with the queue locked */
   190 static int
   191 SDL_AddEvent(SDL_Event * event)
   192 {
   193     SDL_EventEntry *entry;
   194     const int initial_count = SDL_AtomicGet(&SDL_EventQ.count);
   195     int final_count;
   196 
   197     if (initial_count >= SDL_MAX_QUEUED_EVENTS) {
   198         SDL_SetError("Event queue is full (%d events)", initial_count);
   199         return 0;
   200     }
   201 
   202     if (SDL_EventQ.free == NULL) {
   203         entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry));
   204         if (!entry) {
   205             return 0;
   206         }
   207     } else {
   208         entry = SDL_EventQ.free;
   209         SDL_EventQ.free = entry->next;
   210     }
   211 
   212     entry->event = *event;
   213     if (event->type == SDL_SYSWMEVENT) {
   214         entry->msg = *event->syswm.msg;
   215         entry->event.syswm.msg = &entry->msg;
   216     }
   217 
   218     if (SDL_EventQ.tail) {
   219         SDL_EventQ.tail->next = entry;
   220         entry->prev = SDL_EventQ.tail;
   221         SDL_EventQ.tail = entry;
   222         entry->next = NULL;
   223     } else {
   224         SDL_assert(!SDL_EventQ.head);
   225         SDL_EventQ.head = entry;
   226         SDL_EventQ.tail = entry;
   227         entry->prev = NULL;
   228         entry->next = NULL;
   229     }
   230 
   231     final_count = SDL_AtomicAdd(&SDL_EventQ.count, 1) + 1;
   232     if (final_count > SDL_EventQ.max_events_seen) {
   233         SDL_EventQ.max_events_seen = final_count;
   234     }
   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_AtomicGet(&SDL_EventQ.count) > 0);
   262     SDL_AtomicAdd(&SDL_EventQ.count, -1);
   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_AtomicGet(&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                         if (SDL_EventQ.wmmsg_free) {
   321                             wmmsg = SDL_EventQ.wmmsg_free;
   322                             SDL_EventQ.wmmsg_free = wmmsg->next;
   323                         } else {
   324                             wmmsg = (SDL_SysWMEntry *)SDL_malloc(sizeof(*wmmsg));
   325                         }
   326                         wmmsg->msg = *entry->event.syswm.msg;
   327                         wmmsg->next = SDL_EventQ.wmmsg_used;
   328                         SDL_EventQ.wmmsg_used = wmmsg;
   329                         events[used].syswm.msg = &wmmsg->msg;
   330                     }
   331                     ++used;
   332 
   333                     if (action == SDL_GETEVENT) {
   334                         SDL_CutEvent(entry);
   335                     }
   336                 }
   337             }
   338         }
   339         SDL_UnlockMutex(SDL_EventQ.lock);
   340     } else {
   341         return SDL_SetError("Couldn't lock event queue");
   342     }
   343     return (used);
   344 }
   345 
   346 SDL_bool
   347 SDL_HasEvent(Uint32 type)
   348 {
   349     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   350 }
   351 
   352 SDL_bool
   353 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   354 {
   355     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   356 }
   357 
   358 void
   359 SDL_FlushEvent(Uint32 type)
   360 {
   361     SDL_FlushEvents(type, type);
   362 }
   363 
   364 void
   365 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   366 {
   367     /* Don't look after we've quit */
   368     if (!SDL_AtomicGet(&SDL_EventQ.active)) {
   369         return;
   370     }
   371 
   372     /* Make sure the events are current */
   373 #if 0
   374     /* Actually, we can't do this since we might be flushing while processing
   375        a resize event, and calling this might trigger further resize events.
   376     */
   377     SDL_PumpEvents();
   378 #endif
   379 
   380     /* Lock the event queue */
   381     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   382         SDL_EventEntry *entry, *next;
   383         Uint32 type;
   384         for (entry = SDL_EventQ.head; entry; entry = next) {
   385             next = entry->next;
   386             type = entry->event.type;
   387             if (minType <= type && type <= maxType) {
   388                 SDL_CutEvent(entry);
   389             }
   390         }
   391         SDL_UnlockMutex(SDL_EventQ.lock);
   392     }
   393 }
   394 
   395 /* Run the system dependent event loops */
   396 void
   397 SDL_PumpEvents(void)
   398 {
   399     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   400 
   401     /* Get events from the video subsystem */
   402     if (_this) {
   403         _this->PumpEvents(_this);
   404     }
   405 #if !SDL_JOYSTICK_DISABLED
   406     /* Check for joystick state change */
   407     if ((!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] || SDL_JoystickEventState(SDL_QUERY))) {
   408         SDL_JoystickUpdate();
   409     }
   410 #endif
   411 
   412     SDL_SendPendingQuit();  /* in case we had a signal handler fire, etc. */
   413 }
   414 
   415 /* Public functions */
   416 
   417 int
   418 SDL_PollEvent(SDL_Event * event)
   419 {
   420     return SDL_WaitEventTimeout(event, 0);
   421 }
   422 
   423 int
   424 SDL_WaitEvent(SDL_Event * event)
   425 {
   426     return SDL_WaitEventTimeout(event, -1);
   427 }
   428 
   429 int
   430 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   431 {
   432     Uint32 expiration = 0;
   433 
   434     if (timeout > 0)
   435         expiration = SDL_GetTicks() + timeout;
   436 
   437     for (;;) {
   438         SDL_PumpEvents();
   439         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   440         case -1:
   441             return 0;
   442         case 1:
   443             return 1;
   444         case 0:
   445             if (timeout == 0) {
   446                 /* Polling and no events, just return */
   447                 return 0;
   448             }
   449             if (timeout > 0 && SDL_TICKS_PASSED(SDL_GetTicks(), expiration)) {
   450                 /* Timeout expired and no events */
   451                 return 0;
   452             }
   453             SDL_Delay(10);
   454             break;
   455         }
   456     }
   457 }
   458 
   459 int
   460 SDL_PushEvent(SDL_Event * event)
   461 {
   462     SDL_EventWatcher *curr;
   463 
   464     event->common.timestamp = SDL_GetTicks();
   465 
   466     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   467         return 0;
   468     }
   469 
   470     for (curr = SDL_event_watchers; curr; curr = curr->next) {
   471         curr->callback(curr->userdata, event);
   472     }
   473 
   474     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   475         return -1;
   476     }
   477 
   478     SDL_GestureProcessEvent(event);
   479 
   480     return 1;
   481 }
   482 
   483 void
   484 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   485 {
   486     /* Set filter and discard pending events */
   487     SDL_EventOK = NULL;
   488     SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
   489     SDL_EventOKParam = userdata;
   490     SDL_EventOK = filter;
   491 }
   492 
   493 SDL_bool
   494 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   495 {
   496     if (filter) {
   497         *filter = SDL_EventOK;
   498     }
   499     if (userdata) {
   500         *userdata = SDL_EventOKParam;
   501     }
   502     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   503 }
   504 
   505 /* FIXME: This is not thread-safe yet */
   506 void
   507 SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
   508 {
   509     SDL_EventWatcher *watcher, *tail;
   510 
   511     watcher = (SDL_EventWatcher *)SDL_malloc(sizeof(*watcher));
   512     if (!watcher) {
   513         /* Uh oh... */
   514         return;
   515     }
   516 
   517     /* create the watcher */
   518     watcher->callback = filter;
   519     watcher->userdata = userdata;
   520     watcher->next = NULL;
   521 
   522     /* add the watcher to the end of the list */
   523     if (SDL_event_watchers) {
   524         for (tail = SDL_event_watchers; tail->next; tail = tail->next) {
   525             continue;
   526         }
   527         tail->next = watcher;
   528     } else {
   529         SDL_event_watchers = watcher;
   530     }
   531 }
   532 
   533 /* FIXME: This is not thread-safe yet */
   534 void
   535 SDL_DelEventWatch(SDL_EventFilter filter, void *userdata)
   536 {
   537     SDL_EventWatcher *prev = NULL;
   538     SDL_EventWatcher *curr;
   539 
   540     for (curr = SDL_event_watchers; curr; prev = curr, curr = curr->next) {
   541         if (curr->callback == filter && curr->userdata == userdata) {
   542             if (prev) {
   543                 prev->next = curr->next;
   544             } else {
   545                 SDL_event_watchers = curr->next;
   546             }
   547             SDL_free(curr);
   548             break;
   549         }
   550     }
   551 }
   552 
   553 void
   554 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   555 {
   556     if (SDL_EventQ.lock && SDL_LockMutex(SDL_EventQ.lock) == 0) {
   557         SDL_EventEntry *entry, *next;
   558         for (entry = SDL_EventQ.head; entry; entry = next) {
   559             next = entry->next;
   560             if (!filter(userdata, &entry->event)) {
   561                 SDL_CutEvent(entry);
   562             }
   563         }
   564         SDL_UnlockMutex(SDL_EventQ.lock);
   565     }
   566 }
   567 
   568 Uint8
   569 SDL_EventState(Uint32 type, int state)
   570 {
   571     Uint8 current_state;
   572     Uint8 hi = ((type >> 8) & 0xff);
   573     Uint8 lo = (type & 0xff);
   574 
   575     if (SDL_disabled_events[hi] &&
   576         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   577         current_state = SDL_DISABLE;
   578     } else {
   579         current_state = SDL_ENABLE;
   580     }
   581 
   582     if (state != current_state)
   583     {
   584         switch (state) {
   585         case SDL_DISABLE:
   586             /* Disable this event type and discard pending events */
   587             if (!SDL_disabled_events[hi]) {
   588                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   589                 if (!SDL_disabled_events[hi]) {
   590                     /* Out of memory, nothing we can do... */
   591                     break;
   592                 }
   593             }
   594             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   595             SDL_FlushEvent(type);
   596             break;
   597         case SDL_ENABLE:
   598             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   599             break;
   600         default:
   601             /* Querying state... */
   602             break;
   603         }
   604     }
   605 
   606     return current_state;
   607 }
   608 
   609 Uint32
   610 SDL_RegisterEvents(int numevents)
   611 {
   612     Uint32 event_base;
   613 
   614     if ((numevents > 0) && (SDL_userevents+numevents <= SDL_LASTEVENT)) {
   615         event_base = SDL_userevents;
   616         SDL_userevents += numevents;
   617     } else {
   618         event_base = (Uint32)-1;
   619     }
   620     return event_base;
   621 }
   622 
   623 int
   624 SDL_SendAppEvent(SDL_EventType eventType)
   625 {
   626     int posted;
   627 
   628     posted = 0;
   629     if (SDL_GetEventState(eventType) == SDL_ENABLE) {
   630         SDL_Event event;
   631         event.type = eventType;
   632         posted = (SDL_PushEvent(&event) > 0);
   633     }
   634     return (posted);
   635 }
   636 
   637 int
   638 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   639 {
   640     int posted;
   641 
   642     posted = 0;
   643     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   644         SDL_Event event;
   645         SDL_memset(&event, 0, sizeof(event));
   646         event.type = SDL_SYSWMEVENT;
   647         event.syswm.msg = message;
   648         posted = (SDL_PushEvent(&event) > 0);
   649     }
   650     /* Update internal event state */
   651     return (posted);
   652 }
   653 
   654 int
   655 SDL_SendKeymapChangedEvent(void)
   656 {
   657     return SDL_SendAppEvent(SDL_KEYMAPCHANGED);
   658 }
   659 
   660 /* vi: set ts=4 sw=4 expandtab: */