src/events/SDL_events.c
author Sam Lantinga
Thu, 27 Jan 2011 14:45:06 -0800
changeset 5111 481dabb098ef
parent 5086 c2539ff054c8
child 5123 dc0dfdd58f27
permissions -rw-r--r--
Improved timer implementation

The new timer model is formalized as using a separate thread to handle timer callbacks. This was the case on almost every platform before, but it's now a requirement, and simplifies the implementation and makes it perform consistently across platforms.

Goals:
* Minimize timer thread blocking
* Dispatch timers as accurately as possible
* SDL_AddTimer() and SDL_RemoveTimer() are completely threadsafe
* SDL_RemoveTimer() doesn't crash with a timer that's expired or removed
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* General event handling code for SDL */
    25 
    26 #include "SDL.h"
    27 #include "SDL_events.h"
    28 #include "SDL_syswm.h"
    29 #include "SDL_thread.h"
    30 #include "SDL_sysevents.h"
    31 #include "SDL_events_c.h"
    32 #include "../timer/SDL_timer_c.h"
    33 #if !SDL_JOYSTICK_DISABLED
    34 #include "../joystick/SDL_joystick_c.h"
    35 #endif
    36 
    37 /* Public data -- the event filter */
    38 SDL_EventFilter SDL_EventOK = NULL;
    39 void *SDL_EventOKParam;
    40 
    41 typedef struct {
    42     Uint32 bits[8];
    43 } SDL_DisabledEventBlock;
    44 
    45 static SDL_DisabledEventBlock *SDL_disabled_events[256];
    46 static Uint32 SDL_userevents = SDL_USEREVENT;
    47 
    48 /* Private data -- event queue */
    49 #define MAXEVENTS	128
    50 static struct
    51 {
    52     SDL_mutex *lock;
    53     int active;
    54     int head;
    55     int tail;
    56     SDL_Event event[MAXEVENTS];
    57     int wmmsg_next;
    58     struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    59 } SDL_EventQ;
    60 
    61 /* Private data -- event locking structure */
    62 static struct
    63 {
    64     SDL_mutex *lock;
    65     int safe;
    66 } SDL_EventLock;
    67 
    68 /* Thread functions */
    69 static SDL_Thread *SDL_EventThread = NULL;      /* Thread handle */
    70 static SDL_threadID event_thread;     /* The event thread id */
    71 
    72 void
    73 SDL_Lock_EventThread(void)
    74 {
    75     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    76         /* Grab lock and spin until we're sure event thread stopped */
    77         SDL_mutexP(SDL_EventLock.lock);
    78         while (!SDL_EventLock.safe) {
    79             SDL_Delay(1);
    80         }
    81     }
    82 }
    83 
    84 void
    85 SDL_Unlock_EventThread(void)
    86 {
    87     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    88         SDL_mutexV(SDL_EventLock.lock);
    89     }
    90 }
    91 
    92 static __inline__ SDL_bool
    93 SDL_ShouldPollJoystick()
    94 {
    95 #if !SDL_JOYSTICK_DISABLED
    96     if (SDL_numjoysticks &&
    97         (!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] ||
    98          SDL_JoystickEventState(SDL_QUERY))) {
    99         return SDL_TRUE;
   100     }
   101 #endif
   102     return SDL_FALSE;
   103 }
   104 
   105 static int SDLCALL
   106 SDL_GobbleEvents(void *unused)
   107 {
   108     event_thread = SDL_ThreadID();
   109 
   110     while (SDL_EventQ.active) {
   111         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   112 
   113         /* Get events from the video subsystem */
   114         if (_this) {
   115             _this->PumpEvents(_this);
   116         }
   117 #if !SDL_JOYSTICK_DISABLED
   118         /* Check for joystick state change */
   119         if (SDL_ShouldPollJoystick()) {
   120             SDL_JoystickUpdate();
   121         }
   122 #endif
   123 
   124         /* Give up the CPU for the rest of our timeslice */
   125         SDL_EventLock.safe = 1;
   126         SDL_Delay(1);
   127 
   128         /* Check for event locking.
   129            On the P of the lock mutex, if the lock is held, this thread
   130            will wait until the lock is released before continuing.  The
   131            safe flag will be set, meaning that the other thread can go
   132            about it's business.  The safe flag is reset before the V,
   133            so as soon as the mutex is free, other threads can see that
   134            it's not safe to interfere with the event thread.
   135          */
   136         SDL_mutexP(SDL_EventLock.lock);
   137         SDL_EventLock.safe = 0;
   138         SDL_mutexV(SDL_EventLock.lock);
   139     }
   140     event_thread = 0;
   141     return (0);
   142 }
   143 
   144 static int
   145 SDL_StartEventThread(Uint32 flags)
   146 {
   147     /* Reset everything to zero */
   148     SDL_EventThread = NULL;
   149     SDL_memset(&SDL_EventLock, 0, sizeof(SDL_EventLock));
   150 
   151     /* Create the lock and set ourselves active */
   152 #if !SDL_THREADS_DISABLED
   153     SDL_EventQ.lock = SDL_CreateMutex();
   154     if (SDL_EventQ.lock == NULL) {
   155         return (-1);
   156     }
   157 #endif /* !SDL_THREADS_DISABLED */
   158     SDL_EventQ.active = 1;
   159 
   160     if ((flags & SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD) {
   161         SDL_EventLock.lock = SDL_CreateMutex();
   162         if (SDL_EventLock.lock == NULL) {
   163             return (-1);
   164         }
   165         SDL_EventLock.safe = 0;
   166 
   167 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   168 #undef SDL_CreateThread
   169         SDL_EventThread =
   170             SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
   171 #else
   172         SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
   173 #endif
   174         if (SDL_EventThread == NULL) {
   175             return (-1);
   176         }
   177     } else {
   178         event_thread = 0;
   179     }
   180     return (0);
   181 }
   182 
   183 static void
   184 SDL_StopEventThread(void)
   185 {
   186     SDL_EventQ.active = 0;
   187     if (SDL_EventThread) {
   188         SDL_WaitThread(SDL_EventThread, NULL);
   189         SDL_EventThread = NULL;
   190         SDL_DestroyMutex(SDL_EventLock.lock);
   191         SDL_EventLock.lock = NULL;
   192     }
   193     if (SDL_EventQ.lock) {
   194         SDL_DestroyMutex(SDL_EventQ.lock);
   195         SDL_EventQ.lock = NULL;
   196     }
   197 }
   198 
   199 SDL_threadID
   200 SDL_EventThreadID(void)
   201 {
   202     return (event_thread);
   203 }
   204 
   205 /* Public functions */
   206 
   207 void
   208 SDL_StopEventLoop(void)
   209 {
   210     int i;
   211 
   212     /* Halt the event thread, if running */
   213     SDL_StopEventThread();
   214 
   215     /* Shutdown event handlers */
   216     SDL_KeyboardQuit();
   217     SDL_MouseQuit();
   218     SDL_QuitQuit();
   219 
   220     /* Clean out EventQ */
   221     SDL_EventQ.head = 0;
   222     SDL_EventQ.tail = 0;
   223     SDL_EventQ.wmmsg_next = 0;
   224 
   225     /* Clear disabled event state */
   226     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   227         if (SDL_disabled_events[i]) {
   228             SDL_free(SDL_disabled_events[i]);
   229             SDL_disabled_events[i] = NULL;
   230         }
   231     }
   232 }
   233 
   234 /* This function (and associated calls) may be called more than once */
   235 int
   236 SDL_StartEventLoop(Uint32 flags)
   237 {
   238     int retcode;
   239 
   240     /* Clean out the event queue */
   241     SDL_EventThread = NULL;
   242     SDL_EventQ.lock = NULL;
   243     SDL_StopEventLoop();
   244 
   245     /* No filter to start with, process most event types */
   246     SDL_EventOK = NULL;
   247     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   248 
   249     /* Initialize event handlers */
   250     retcode = 0;
   251     retcode += SDL_KeyboardInit();
   252     retcode += SDL_MouseInit();
   253     retcode += SDL_TouchInit();
   254     retcode += SDL_QuitInit();
   255     if (retcode < 0) {
   256         /* We don't expect them to fail, but... */
   257         return (-1);
   258     }
   259 
   260     /* Create the lock and event thread */
   261     if (SDL_StartEventThread(flags) < 0) {
   262         SDL_StopEventLoop();
   263         return (-1);
   264     }
   265     return (0);
   266 }
   267 
   268 
   269 /* Add an event to the event queue -- called with the queue locked */
   270 static int
   271 SDL_AddEvent(SDL_Event * event)
   272 {
   273     int tail, added;
   274 
   275     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   276     if (tail == SDL_EventQ.head) {
   277         /* Overflow, drop event */
   278         added = 0;
   279     } else {
   280         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   281         if (event->type == SDL_SYSWMEVENT) {
   282             /* Note that it's possible to lose an event */
   283             int next = SDL_EventQ.wmmsg_next;
   284             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   285             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   286                 &SDL_EventQ.wmmsg[next];
   287             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   288         }
   289         SDL_EventQ.tail = tail;
   290         added = 1;
   291     }
   292     return (added);
   293 }
   294 
   295 /* Cut an event, and return the next valid spot, or the tail */
   296 /*                           -- called with the queue locked */
   297 static int
   298 SDL_CutEvent(int spot)
   299 {
   300     if (spot == SDL_EventQ.head) {
   301         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   302         return (SDL_EventQ.head);
   303     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   304         SDL_EventQ.tail = spot;
   305         return (SDL_EventQ.tail);
   306     } else
   307         /* We cut the middle -- shift everything over */
   308     {
   309         int here, next;
   310 
   311         /* This can probably be optimized with SDL_memcpy() -- careful! */
   312         if (--SDL_EventQ.tail < 0) {
   313             SDL_EventQ.tail = MAXEVENTS - 1;
   314         }
   315         for (here = spot; here != SDL_EventQ.tail; here = next) {
   316             next = (here + 1) % MAXEVENTS;
   317             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   318         }
   319         return (spot);
   320     }
   321     /* NOTREACHED */
   322 }
   323 
   324 /* Lock the event queue, take a peep at it, and unlock it */
   325 int
   326 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   327                Uint32 minType, Uint32 maxType)
   328 {
   329     int i, used;
   330 
   331     /* Don't look after we've quit */
   332     if (!SDL_EventQ.active) {
   333         return (-1);
   334     }
   335     /* Lock the event queue */
   336     used = 0;
   337     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   338         if (action == SDL_ADDEVENT) {
   339             for (i = 0; i < numevents; ++i) {
   340                 used += SDL_AddEvent(&events[i]);
   341             }
   342         } else {
   343             SDL_Event tmpevent;
   344             int spot;
   345 
   346             /* If 'events' is NULL, just see if they exist */
   347             if (events == NULL) {
   348                 action = SDL_PEEKEVENT;
   349                 numevents = 1;
   350                 events = &tmpevent;
   351             }
   352             spot = SDL_EventQ.head;
   353             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   354                 Uint32 type = SDL_EventQ.event[spot].type;
   355                 if (minType <= type && type <= maxType) {
   356                     events[used++] = SDL_EventQ.event[spot];
   357                     if (action == SDL_GETEVENT) {
   358                         spot = SDL_CutEvent(spot);
   359                     } else {
   360                         spot = (spot + 1) % MAXEVENTS;
   361                     }
   362                 } else {
   363                     spot = (spot + 1) % MAXEVENTS;
   364                 }
   365             }
   366         }
   367         SDL_mutexV(SDL_EventQ.lock);
   368     } else {
   369         SDL_SetError("Couldn't lock event queue");
   370         used = -1;
   371     }
   372     return (used);
   373 }
   374 
   375 SDL_bool
   376 SDL_HasEvent(Uint32 type)
   377 {
   378     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   379 }
   380 
   381 SDL_bool
   382 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   383 {
   384     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   385 }
   386 
   387 void
   388 SDL_FlushEvent(Uint32 type)
   389 {
   390     SDL_FlushEvents(type, type);
   391 }
   392 
   393 void
   394 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   395 {
   396     /* Don't look after we've quit */
   397     if (!SDL_EventQ.active) {
   398         return;
   399     }
   400 
   401     /* Make sure the events are current */
   402     SDL_PumpEvents();
   403 
   404     /* Lock the event queue */
   405     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   406         int spot = SDL_EventQ.head;
   407         while (spot != SDL_EventQ.tail) {
   408             Uint32 type = SDL_EventQ.event[spot].type;
   409             if (minType <= type && type <= maxType) {
   410                 spot = SDL_CutEvent(spot);
   411             } else {
   412                 spot = (spot + 1) % MAXEVENTS;
   413             }
   414         }
   415         SDL_mutexV(SDL_EventQ.lock);
   416     }
   417 }
   418 
   419 /* Run the system dependent event loops */
   420 void
   421 SDL_PumpEvents(void)
   422 {
   423     if (!SDL_EventThread) {
   424         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   425 
   426         /* Get events from the video subsystem */
   427         if (_this) {
   428             _this->PumpEvents(_this);
   429         }
   430 #if !SDL_JOYSTICK_DISABLED
   431         /* Check for joystick state change */
   432         if (SDL_ShouldPollJoystick()) {
   433             SDL_JoystickUpdate();
   434         }
   435 #endif
   436     }
   437 }
   438 
   439 /* Public functions */
   440 
   441 int
   442 SDL_PollEvent(SDL_Event * event)
   443 {
   444     return SDL_WaitEventTimeout(event, 0);
   445 }
   446 
   447 int
   448 SDL_WaitEvent(SDL_Event * event)
   449 {
   450     return SDL_WaitEventTimeout(event, -1);
   451 }
   452 
   453 int
   454 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   455 {
   456     Uint32 expiration = 0;
   457 
   458     if (timeout > 0)
   459         expiration = SDL_GetTicks() + timeout;
   460 
   461     for (;;) {
   462         SDL_PumpEvents();
   463         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   464         case -1:
   465             return 0;
   466         case 1:
   467             return 1;
   468         case 0:
   469             if (timeout == 0) {
   470                 /* Polling and no events, just return */
   471                 return 0;
   472             }
   473             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   474                 /* Timeout expired and no events */
   475                 return 0;
   476             }
   477             SDL_Delay(10);
   478             break;
   479         }
   480     }
   481 }
   482 
   483 int
   484 SDL_PushEvent(SDL_Event * event)
   485 {
   486     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   487         return 0;
   488     }
   489     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   490         return -1;
   491     }
   492 
   493     SDL_GestureProcessEvent(event);
   494     
   495 
   496     return 1;
   497 }
   498 
   499 void
   500 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   501 {
   502     SDL_Event bitbucket;
   503 
   504     /* Set filter and discard pending events */
   505     SDL_EventOK = filter;
   506     SDL_EventOKParam = userdata;
   507     while (SDL_PollEvent(&bitbucket) > 0);
   508 }
   509 
   510 SDL_bool
   511 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   512 {
   513     if (filter) {
   514         *filter = SDL_EventOK;
   515     }
   516     if (userdata) {
   517         *userdata = SDL_EventOKParam;
   518     }
   519     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   520 }
   521 
   522 void
   523 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   524 {
   525     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   526         int spot;
   527 
   528         spot = SDL_EventQ.head;
   529         while (spot != SDL_EventQ.tail) {
   530             if (filter(userdata, &SDL_EventQ.event[spot])) {
   531                 spot = (spot + 1) % MAXEVENTS;
   532             } else {
   533                 spot = SDL_CutEvent(spot);
   534             }
   535         }
   536     }
   537     SDL_mutexV(SDL_EventQ.lock);
   538 }
   539 
   540 Uint8
   541 SDL_EventState(Uint32 type, int state)
   542 {
   543     Uint8 current_state;
   544     Uint8 hi = ((type >> 8) & 0xff);
   545     Uint8 lo = (type & 0xff);
   546 
   547     if (SDL_disabled_events[hi] &&
   548         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   549         current_state = SDL_DISABLE;
   550     } else {
   551         current_state = SDL_ENABLE;
   552     }
   553 
   554     if (state != current_state)
   555     {
   556         switch (state) {
   557         case SDL_DISABLE:
   558             /* Disable this event type and discard pending events */
   559             if (!SDL_disabled_events[hi]) {
   560                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   561                 if (!SDL_disabled_events[hi]) {
   562                     /* Out of memory, nothing we can do... */
   563                     break;
   564                 }
   565             }
   566             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   567             SDL_FlushEvent(type);
   568             break;
   569         case SDL_ENABLE:
   570             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   571             break;
   572         default:
   573             /* Querying state... */
   574             break;
   575         }
   576     }
   577 
   578     return current_state;
   579 }
   580 
   581 Uint32
   582 SDL_RegisterEvents(int numevents)
   583 {
   584     Uint32 event_base;
   585 
   586     if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   587         event_base = SDL_userevents;
   588         SDL_userevents += numevents;
   589     } else {
   590         event_base = (Uint32)-1;
   591     }
   592     return event_base;
   593 }
   594 
   595 /* This is a generic event handler.
   596  */
   597 int
   598 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   599 {
   600     int posted;
   601 
   602     posted = 0;
   603     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   604         SDL_Event event;
   605         SDL_memset(&event, 0, sizeof(event));
   606         event.type = SDL_SYSWMEVENT;
   607         event.syswm.msg = message;
   608         posted = (SDL_PushEvent(&event) > 0);
   609     }
   610     /* Update internal event state */
   611     return (posted);
   612 }
   613 
   614 /* vi: set ts=4 sw=4 expandtab: */