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