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