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