src/events/SDL_events.c
author Sam Lantinga
Sat, 18 May 2013 12:48:50 -0700
changeset 7190 11612d544fcd
parent 7082 e9d6004d2978
child 7191 75360622e65f
permissions -rw-r--r--
Added mobile application events, with implementations for iOS and Android
     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     SDL_EventOK = NULL;
   115 }
   116 
   117 /* This function (and associated calls) may be called more than once */
   118 int
   119 SDL_StartEventLoop(void)
   120 {
   121     /* We'll leave the event queue alone, since we might have gotten
   122        some important events at launch (like SDL_DROPFILE)
   123 
   124        FIXME: Does this introduce any other bugs with events at startup?
   125      */
   126 
   127     /* Create the lock and set ourselves active */
   128 #if !SDL_THREADS_DISABLED
   129     if (!SDL_EventQ.lock) {
   130         SDL_EventQ.lock = SDL_CreateMutex();
   131     }
   132     if (SDL_EventQ.lock == NULL) {
   133         return (-1);
   134     }
   135 #endif /* !SDL_THREADS_DISABLED */
   136 
   137     /* Process most event types */
   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 
   369     event->common.timestamp = SDL_GetTicks();
   370 
   371     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   372         return 0;
   373     }
   374 
   375     for (curr = SDL_event_watchers; curr; curr = curr->next) {
   376         curr->callback(curr->userdata, event);
   377     }
   378 
   379     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   380         return -1;
   381     }
   382 
   383     SDL_GestureProcessEvent(event);
   384 
   385     return 1;
   386 }
   387 
   388 void
   389 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   390 {
   391     SDL_Event bitbucket;
   392 
   393     /* Set filter and discard pending events */
   394     SDL_EventOK = filter;
   395     SDL_EventOKParam = userdata;
   396     while (SDL_PollEvent(&bitbucket) > 0);
   397 }
   398 
   399 SDL_bool
   400 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   401 {
   402     if (filter) {
   403         *filter = SDL_EventOK;
   404     }
   405     if (userdata) {
   406         *userdata = SDL_EventOKParam;
   407     }
   408     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   409 }
   410 
   411 /* FIXME: This is not thread-safe yet */
   412 void
   413 SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
   414 {
   415     SDL_EventWatcher *watcher;
   416 
   417     watcher = (SDL_EventWatcher *)SDL_malloc(sizeof(*watcher));
   418     if (!watcher) {
   419         /* Uh oh... */
   420         return;
   421     }
   422     watcher->callback = filter;
   423     watcher->userdata = userdata;
   424     watcher->next = SDL_event_watchers;
   425     SDL_event_watchers = watcher;
   426 }
   427 
   428 /* FIXME: This is not thread-safe yet */
   429 void
   430 SDL_DelEventWatch(SDL_EventFilter filter, void *userdata)
   431 {
   432     SDL_EventWatcher *prev = NULL;
   433     SDL_EventWatcher *curr;
   434 
   435     for (curr = SDL_event_watchers; curr; prev = curr, curr = curr->next) {
   436         if (curr->callback == filter && curr->userdata == userdata) {
   437             if (prev) {
   438                 prev->next = curr->next;
   439             } else {
   440                 SDL_event_watchers = curr->next;
   441             }
   442             SDL_free(curr);
   443             break;
   444         }
   445     }
   446 }
   447 
   448 void
   449 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   450 {
   451     if (SDL_LockMutex(SDL_EventQ.lock) == 0) {
   452         int spot;
   453 
   454         spot = SDL_EventQ.head;
   455         while (spot != SDL_EventQ.tail) {
   456             if (filter(userdata, &SDL_EventQ.event[spot])) {
   457                 spot = (spot + 1) % MAXEVENTS;
   458             } else {
   459                 spot = SDL_CutEvent(spot);
   460             }
   461         }
   462     }
   463     SDL_UnlockMutex(SDL_EventQ.lock);
   464 }
   465 
   466 Uint8
   467 SDL_EventState(Uint32 type, int state)
   468 {
   469     Uint8 current_state;
   470     Uint8 hi = ((type >> 8) & 0xff);
   471     Uint8 lo = (type & 0xff);
   472 
   473     if (SDL_disabled_events[hi] &&
   474         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   475         current_state = SDL_DISABLE;
   476     } else {
   477         current_state = SDL_ENABLE;
   478     }
   479 
   480     if (state != current_state)
   481     {
   482         switch (state) {
   483         case SDL_DISABLE:
   484             /* Disable this event type and discard pending events */
   485             if (!SDL_disabled_events[hi]) {
   486                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   487                 if (!SDL_disabled_events[hi]) {
   488                     /* Out of memory, nothing we can do... */
   489                     break;
   490                 }
   491             }
   492             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   493             SDL_FlushEvent(type);
   494             break;
   495         case SDL_ENABLE:
   496             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   497             break;
   498         default:
   499             /* Querying state... */
   500             break;
   501         }
   502     }
   503 
   504     return current_state;
   505 }
   506 
   507 Uint32
   508 SDL_RegisterEvents(int numevents)
   509 {
   510     Uint32 event_base;
   511 
   512     if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   513         event_base = SDL_userevents;
   514         SDL_userevents += numevents;
   515     } else {
   516         event_base = (Uint32)-1;
   517     }
   518     return event_base;
   519 }
   520 
   521 int
   522 SDL_SendAppEvent(SDL_EventType eventType)
   523 {
   524     int posted;
   525 
   526     posted = 0;
   527     if (SDL_GetEventState(eventType) == SDL_ENABLE) {
   528         SDL_Event event;
   529         event.type = eventType;
   530         posted = (SDL_PushEvent(&event) > 0);
   531     }
   532     return (posted);
   533 }
   534 
   535 int
   536 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   537 {
   538     int posted;
   539 
   540     posted = 0;
   541     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   542         SDL_Event event;
   543         SDL_memset(&event, 0, sizeof(event));
   544         event.type = SDL_SYSWMEVENT;
   545         event.syswm.msg = message;
   546         posted = (SDL_PushEvent(&event) > 0);
   547     }
   548     /* Update internal event state */
   549     return (posted);
   550 }
   551 
   552 /* vi: set ts=4 sw=4 expandtab: */