src/events/SDL_events.c
author David Ludwig
Sat, 20 Apr 2013 23:05:08 -0400
changeset 7082 e9d6004d2978
parent 7077 f8e3dcefed59
child 7190 11612d544fcd
child 8464 a2a909304cfe
permissions -rw-r--r--
future-proofing for Microsoft's C++/CX extensions, whereby "generic" is a reserved keyword
     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 #define MAXEVENTS	128
    57 static struct
    58 {
    59     SDL_mutex *lock;
    60     int active;
    61     int head;
    62     int tail;
    63     SDL_Event event[MAXEVENTS];
    64     int wmmsg_next;
    65     struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    66 } SDL_EventQ = { NULL, 1 };
    67 
    68 
    69 static __inline__ SDL_bool
    70 SDL_ShouldPollJoystick()
    71 {
    72 #if !SDL_JOYSTICK_DISABLED
    73     if ((!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] ||
    74          SDL_JoystickEventState(SDL_QUERY)) &&
    75         SDL_PrivateJoystickNeedsPolling()) {
    76         return SDL_TRUE;
    77     }
    78 #endif
    79     return SDL_FALSE;
    80 }
    81 
    82 /* Public functions */
    83 
    84 void
    85 SDL_StopEventLoop(void)
    86 {
    87     int i;
    88 
    89     SDL_EventQ.active = 0;
    90 
    91     if (SDL_EventQ.lock) {
    92         SDL_DestroyMutex(SDL_EventQ.lock);
    93         SDL_EventQ.lock = NULL;
    94     }
    95 
    96     /* Clean out EventQ */
    97     SDL_EventQ.head = 0;
    98     SDL_EventQ.tail = 0;
    99     SDL_EventQ.wmmsg_next = 0;
   100 
   101     /* Clear disabled event state */
   102     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   103         if (SDL_disabled_events[i]) {
   104             SDL_free(SDL_disabled_events[i]);
   105             SDL_disabled_events[i] = NULL;
   106         }
   107     }
   108 
   109     while (SDL_event_watchers) {
   110         SDL_EventWatcher *tmp = SDL_event_watchers;
   111         SDL_event_watchers = tmp->next;
   112         SDL_free(tmp);
   113     }
   114 }
   115 
   116 /* This function (and associated calls) may be called more than once */
   117 int
   118 SDL_StartEventLoop(void)
   119 {
   120     /* We'll leave the event queue alone, since we might have gotten
   121        some important events at launch (like SDL_DROPFILE)
   122 
   123        FIXME: Does this introduce any other bugs with events at startup?
   124      */
   125 
   126     /* Create the lock and set ourselves active */
   127 #if !SDL_THREADS_DISABLED
   128     if (!SDL_EventQ.lock) {
   129         SDL_EventQ.lock = SDL_CreateMutex();
   130     }
   131     if (SDL_EventQ.lock == NULL) {
   132         return (-1);
   133     }
   134 #endif /* !SDL_THREADS_DISABLED */
   135 
   136     /* No filter to start with, process most event types */
   137     SDL_EventOK = NULL;
   138     SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
   139     SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
   140     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   141 
   142     SDL_EventQ.active = 1;
   143 
   144     return (0);
   145 }
   146 
   147 
   148 /* Add an event to the event queue -- called with the queue locked */
   149 static int
   150 SDL_AddEvent(SDL_Event * event)
   151 {
   152     int tail, added;
   153 
   154     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   155     if (tail == SDL_EventQ.head) {
   156         /* Overflow, drop event */
   157         added = 0;
   158     } else {
   159         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   160         if (event->type == SDL_SYSWMEVENT) {
   161             /* Note that it's possible to lose an event */
   162             int next = SDL_EventQ.wmmsg_next;
   163             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   164             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   165                 &SDL_EventQ.wmmsg[next];
   166             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   167         }
   168         SDL_EventQ.tail = tail;
   169         added = 1;
   170     }
   171     return (added);
   172 }
   173 
   174 /* Cut an event, and return the next valid spot, or the tail */
   175 /*                           -- called with the queue locked */
   176 static int
   177 SDL_CutEvent(int spot)
   178 {
   179     if (spot == SDL_EventQ.head) {
   180         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   181         return (SDL_EventQ.head);
   182     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   183         SDL_EventQ.tail = spot;
   184         return (SDL_EventQ.tail);
   185     } else
   186         /* We cut the middle -- shift everything over */
   187     {
   188         int here, next;
   189 
   190         /* This can probably be optimized with SDL_memcpy() -- careful! */
   191         if (--SDL_EventQ.tail < 0) {
   192             SDL_EventQ.tail = MAXEVENTS - 1;
   193         }
   194         for (here = spot; here != SDL_EventQ.tail; here = next) {
   195             next = (here + 1) % MAXEVENTS;
   196             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   197         }
   198         return (spot);
   199     }
   200     /* NOTREACHED */
   201 }
   202 
   203 /* Lock the event queue, take a peep at it, and unlock it */
   204 int
   205 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   206                Uint32 minType, Uint32 maxType)
   207 {
   208     int i, used;
   209 
   210     /* Don't look after we've quit */
   211     if (!SDL_EventQ.active) {
   212         return (-1);
   213     }
   214     /* Lock the event queue */
   215     used = 0;
   216     if (!SDL_EventQ.lock || SDL_LockMutex(SDL_EventQ.lock) == 0) {
   217         if (action == SDL_ADDEVENT) {
   218             for (i = 0; i < numevents; ++i) {
   219                 used += SDL_AddEvent(&events[i]);
   220             }
   221         } else {
   222             SDL_Event tmpevent;
   223             int spot;
   224 
   225             /* If 'events' is NULL, just see if they exist */
   226             if (events == NULL) {
   227                 action = SDL_PEEKEVENT;
   228                 numevents = 1;
   229                 events = &tmpevent;
   230             }
   231             spot = SDL_EventQ.head;
   232             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   233                 Uint32 type = SDL_EventQ.event[spot].type;
   234                 if (minType <= type && type <= maxType) {
   235                     events[used++] = SDL_EventQ.event[spot];
   236                     if (action == SDL_GETEVENT) {
   237                         spot = SDL_CutEvent(spot);
   238                     } else {
   239                         spot = (spot + 1) % MAXEVENTS;
   240                     }
   241                 } else {
   242                     spot = (spot + 1) % MAXEVENTS;
   243                 }
   244             }
   245         }
   246         SDL_UnlockMutex(SDL_EventQ.lock);
   247     } else {
   248         return SDL_SetError("Couldn't lock event queue");
   249     }
   250     return (used);
   251 }
   252 
   253 SDL_bool
   254 SDL_HasEvent(Uint32 type)
   255 {
   256     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   257 }
   258 
   259 SDL_bool
   260 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   261 {
   262     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   263 }
   264 
   265 void
   266 SDL_FlushEvent(Uint32 type)
   267 {
   268     SDL_FlushEvents(type, type);
   269 }
   270 
   271 void
   272 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   273 {
   274     /* Don't look after we've quit */
   275     if (!SDL_EventQ.active) {
   276         return;
   277     }
   278 
   279     /* Make sure the events are current */
   280 #if 0
   281     /* Actually, we can't do this since we might be flushing while processing
   282        a resize event, and calling this might trigger further resize events.
   283     */
   284     SDL_PumpEvents();
   285 #endif
   286 
   287     /* Lock the event queue */
   288     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   289         int spot = SDL_EventQ.head;
   290         while (spot != SDL_EventQ.tail) {
   291             Uint32 type = SDL_EventQ.event[spot].type;
   292             if (minType <= type && type <= maxType) {
   293                 spot = SDL_CutEvent(spot);
   294             } else {
   295                 spot = (spot + 1) % MAXEVENTS;
   296             }
   297         }
   298         SDL_UnlockMutex(SDL_EventQ.lock);
   299     }
   300 }
   301 
   302 /* Run the system dependent event loops */
   303 void
   304 SDL_PumpEvents(void)
   305 {
   306     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   307 
   308     /* Get events from the video subsystem */
   309     if (_this) {
   310         _this->PumpEvents(_this);
   311     }
   312 #if !SDL_JOYSTICK_DISABLED
   313     /* Check for joystick state change */
   314     if (SDL_ShouldPollJoystick()) {
   315         SDL_JoystickUpdate();
   316     }
   317 #endif
   318 }
   319 
   320 /* Public functions */
   321 
   322 int
   323 SDL_PollEvent(SDL_Event * event)
   324 {
   325     return SDL_WaitEventTimeout(event, 0);
   326 }
   327 
   328 int
   329 SDL_WaitEvent(SDL_Event * event)
   330 {
   331     return SDL_WaitEventTimeout(event, -1);
   332 }
   333 
   334 int
   335 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   336 {
   337     Uint32 expiration = 0;
   338 
   339     if (timeout > 0)
   340         expiration = SDL_GetTicks() + timeout;
   341 
   342     for (;;) {
   343         SDL_PumpEvents();
   344         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   345         case -1:
   346             return 0;
   347         case 1:
   348             return 1;
   349         case 0:
   350             if (timeout == 0) {
   351                 /* Polling and no events, just return */
   352                 return 0;
   353             }
   354             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   355                 /* Timeout expired and no events */
   356                 return 0;
   357             }
   358             SDL_Delay(10);
   359             break;
   360         }
   361     }
   362 }
   363 
   364 int
   365 SDL_PushEvent(SDL_Event * event)
   366 {
   367     SDL_EventWatcher *curr;
   368     event->common.timestamp = SDL_GetTicks();
   369     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   370         return 0;
   371     }
   372 
   373     for (curr = SDL_event_watchers; curr; curr = curr->next) {
   374         curr->callback(curr->userdata, event);
   375     }
   376 
   377     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   378         return -1;
   379     }
   380 
   381     SDL_GestureProcessEvent(event);
   382 
   383     return 1;
   384 }
   385 
   386 void
   387 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   388 {
   389     SDL_Event bitbucket;
   390 
   391     /* Set filter and discard pending events */
   392     SDL_EventOK = filter;
   393     SDL_EventOKParam = userdata;
   394     while (SDL_PollEvent(&bitbucket) > 0);
   395 }
   396 
   397 SDL_bool
   398 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   399 {
   400     if (filter) {
   401         *filter = SDL_EventOK;
   402     }
   403     if (userdata) {
   404         *userdata = SDL_EventOKParam;
   405     }
   406     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   407 }
   408 
   409 /* FIXME: This is not thread-safe yet */
   410 void
   411 SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
   412 {
   413     SDL_EventWatcher *watcher;
   414 
   415     watcher = (SDL_EventWatcher *)SDL_malloc(sizeof(*watcher));
   416     if (!watcher) {
   417         /* Uh oh... */
   418         return;
   419     }
   420     watcher->callback = filter;
   421     watcher->userdata = userdata;
   422     watcher->next = SDL_event_watchers;
   423     SDL_event_watchers = watcher;
   424 }
   425 
   426 /* FIXME: This is not thread-safe yet */
   427 void
   428 SDL_DelEventWatch(SDL_EventFilter filter, void *userdata)
   429 {
   430     SDL_EventWatcher *prev = NULL;
   431     SDL_EventWatcher *curr;
   432 
   433     for (curr = SDL_event_watchers; curr; prev = curr, curr = curr->next) {
   434         if (curr->callback == filter && curr->userdata == userdata) {
   435             if (prev) {
   436                 prev->next = curr->next;
   437             } else {
   438                 SDL_event_watchers = curr->next;
   439             }
   440             SDL_free(curr);
   441             break;
   442         }
   443     }
   444 }
   445 
   446 void
   447 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   448 {
   449     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   450         int spot;
   451 
   452         spot = SDL_EventQ.head;
   453         while (spot != SDL_EventQ.tail) {
   454             if (filter(userdata, &SDL_EventQ.event[spot])) {
   455                 spot = (spot + 1) % MAXEVENTS;
   456             } else {
   457                 spot = SDL_CutEvent(spot);
   458             }
   459         }
   460     }
   461     SDL_UnlockMutex(SDL_EventQ.lock);
   462 }
   463 
   464 Uint8
   465 SDL_EventState(Uint32 type, int state)
   466 {
   467     Uint8 current_state;
   468     Uint8 hi = ((type >> 8) & 0xff);
   469     Uint8 lo = (type & 0xff);
   470 
   471     if (SDL_disabled_events[hi] &&
   472         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   473         current_state = SDL_DISABLE;
   474     } else {
   475         current_state = SDL_ENABLE;
   476     }
   477 
   478     if (state != current_state)
   479     {
   480         switch (state) {
   481         case SDL_DISABLE:
   482             /* Disable this event type and discard pending events */
   483             if (!SDL_disabled_events[hi]) {
   484                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   485                 if (!SDL_disabled_events[hi]) {
   486                     /* Out of memory, nothing we can do... */
   487                     break;
   488                 }
   489             }
   490             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   491             SDL_FlushEvent(type);
   492             break;
   493         case SDL_ENABLE:
   494             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   495             break;
   496         default:
   497             /* Querying state... */
   498             break;
   499         }
   500     }
   501 
   502     return current_state;
   503 }
   504 
   505 Uint32
   506 SDL_RegisterEvents(int numevents)
   507 {
   508     Uint32 event_base;
   509 
   510     if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   511         event_base = SDL_userevents;
   512         SDL_userevents += numevents;
   513     } else {
   514         event_base = (Uint32)-1;
   515     }
   516     return event_base;
   517 }
   518 
   519 /* This is a generic event handler.
   520  */
   521 int
   522 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   523 {
   524     int posted;
   525 
   526     posted = 0;
   527     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   528         SDL_Event event;
   529         SDL_memset(&event, 0, sizeof(event));
   530         event.type = SDL_SYSWMEVENT;
   531         event.syswm.msg = message;
   532         posted = (SDL_PushEvent(&event) > 0);
   533     }
   534     /* Update internal event state */
   535     return (posted);
   536 }
   537 
   538 /* vi: set ts=4 sw=4 expandtab: */