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