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