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