src/events/SDL_events.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 06 Jan 2010 07:34:20 +0000
changeset 3611 57823d017f02
parent 3578 0d1b16ee0bca
child 3697 f7b03b6838cb
permissions -rw-r--r--
Merged r4121:4122 from branches/SDL-1.2: more double-free fixes.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 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_sysevents.h"
    31 #include "SDL_events_c.h"
    32 #include "../timer/SDL_timer_c.h"
    33 #if !SDL_JOYSTICK_DISABLED
    34 #include "../joystick/SDL_joystick_c.h"
    35 #endif
    36 
    37 /* Public data -- the event filter */
    38 SDL_EventFilter SDL_EventOK = NULL;
    39 void *SDL_EventOKParam;
    40 Uint8 SDL_ProcessEvents[SDL_NUMEVENTS];
    41 static Uint32 SDL_eventstate = 0;
    42 
    43 /* Private data -- event queue */
    44 #define MAXEVENTS	128
    45 static struct
    46 {
    47     SDL_mutex *lock;
    48     int active;
    49     int head;
    50     int tail;
    51     SDL_Event event[MAXEVENTS];
    52     int wmmsg_next;
    53     struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    54 } SDL_EventQ;
    55 
    56 /* Private data -- event locking structure */
    57 static struct
    58 {
    59     SDL_mutex *lock;
    60     int safe;
    61 } SDL_EventLock;
    62 
    63 /* Thread functions */
    64 static SDL_Thread *SDL_EventThread = NULL;      /* Thread handle */
    65 static SDL_threadID event_thread;     /* The event thread id */
    66 
    67 void
    68 SDL_Lock_EventThread(void)
    69 {
    70     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    71         /* Grab lock and spin until we're sure event thread stopped */
    72         SDL_mutexP(SDL_EventLock.lock);
    73         while (!SDL_EventLock.safe) {
    74             SDL_Delay(1);
    75         }
    76     }
    77 }
    78 
    79 void
    80 SDL_Unlock_EventThread(void)
    81 {
    82     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    83         SDL_mutexV(SDL_EventLock.lock);
    84     }
    85 }
    86 
    87 static int SDLCALL
    88 SDL_GobbleEvents(void *unused)
    89 {
    90     event_thread = SDL_ThreadID();
    91 
    92     while (SDL_EventQ.active) {
    93         SDL_VideoDevice *_this = SDL_GetVideoDevice();
    94 
    95         /* Get events from the video subsystem */
    96         if (_this) {
    97             _this->PumpEvents(_this);
    98         }
    99 #if !SDL_JOYSTICK_DISABLED
   100         /* Check for joystick state change */
   101         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   102             SDL_JoystickUpdate();
   103         }
   104 #endif
   105 
   106         /* Give up the CPU for the rest of our timeslice */
   107         SDL_EventLock.safe = 1;
   108         if (SDL_timer_running) {
   109             SDL_ThreadedTimerCheck();
   110         }
   111         SDL_Delay(1);
   112 
   113         /* Check for event locking.
   114            On the P of the lock mutex, if the lock is held, this thread
   115            will wait until the lock is released before continuing.  The
   116            safe flag will be set, meaning that the other thread can go
   117            about it's business.  The safe flag is reset before the V,
   118            so as soon as the mutex is free, other threads can see that
   119            it's not safe to interfere with the event thread.
   120          */
   121         SDL_mutexP(SDL_EventLock.lock);
   122         SDL_EventLock.safe = 0;
   123         SDL_mutexV(SDL_EventLock.lock);
   124     }
   125     SDL_SetTimerThreaded(0);
   126     event_thread = 0;
   127     return (0);
   128 }
   129 
   130 static int
   131 SDL_StartEventThread(Uint32 flags)
   132 {
   133     /* Reset everything to zero */
   134     SDL_EventThread = NULL;
   135     SDL_memset(&SDL_EventLock, 0, sizeof(SDL_EventLock));
   136 
   137     /* Create the lock and set ourselves active */
   138 #if !SDL_THREADS_DISABLED
   139     SDL_EventQ.lock = SDL_CreateMutex();
   140     if (SDL_EventQ.lock == NULL) {
   141         return (-1);
   142     }
   143 #endif /* !SDL_THREADS_DISABLED */
   144     SDL_EventQ.active = 1;
   145 
   146     if ((flags & SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD) {
   147         SDL_EventLock.lock = SDL_CreateMutex();
   148         if (SDL_EventLock.lock == NULL) {
   149             return (-1);
   150         }
   151         SDL_EventLock.safe = 0;
   152 
   153         /* The event thread will handle timers too */
   154         SDL_SetTimerThreaded(2);
   155 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   156 #undef SDL_CreateThread
   157         SDL_EventThread =
   158             SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
   159 #else
   160         SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
   161 #endif
   162         if (SDL_EventThread == NULL) {
   163             return (-1);
   164         }
   165     } else {
   166         event_thread = 0;
   167     }
   168     return (0);
   169 }
   170 
   171 static void
   172 SDL_StopEventThread(void)
   173 {
   174     SDL_EventQ.active = 0;
   175     if (SDL_EventThread) {
   176         SDL_WaitThread(SDL_EventThread, NULL);
   177         SDL_EventThread = NULL;
   178         SDL_DestroyMutex(SDL_EventLock.lock);
   179         SDL_EventLock.lock = NULL;
   180     }
   181     if (SDL_EventQ.lock) {
   182         SDL_DestroyMutex(SDL_EventQ.lock);
   183         SDL_EventQ.lock = NULL;
   184     }
   185 }
   186 
   187 SDL_threadID
   188 SDL_EventThreadID(void)
   189 {
   190     return (event_thread);
   191 }
   192 
   193 /* Public functions */
   194 
   195 void
   196 SDL_StopEventLoop(void)
   197 {
   198     /* Halt the event thread, if running */
   199     SDL_StopEventThread();
   200 
   201     /* Shutdown event handlers */
   202     SDL_KeyboardQuit();
   203     SDL_MouseQuit();
   204     SDL_QuitQuit();
   205 
   206     /* Clean out EventQ */
   207     SDL_EventQ.head = 0;
   208     SDL_EventQ.tail = 0;
   209     SDL_EventQ.wmmsg_next = 0;
   210 }
   211 
   212 /* This function (and associated calls) may be called more than once */
   213 int
   214 SDL_StartEventLoop(Uint32 flags)
   215 {
   216     int retcode;
   217 
   218     /* Clean out the event queue */
   219     SDL_EventThread = NULL;
   220     SDL_EventQ.lock = NULL;
   221     SDL_StopEventLoop();
   222 
   223     /* No filter to start with, process most event types */
   224     SDL_EventOK = NULL;
   225     SDL_memset(SDL_ProcessEvents, SDL_ENABLE, sizeof(SDL_ProcessEvents));
   226     SDL_eventstate = ~0;
   227     /* It's not save to call SDL_EventState() yet */
   228     SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
   229     SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
   230 
   231     /* Initialize event handlers */
   232     retcode = 0;
   233     retcode += SDL_KeyboardInit();
   234     retcode += SDL_MouseInit();
   235     retcode += SDL_QuitInit();
   236     if (retcode < 0) {
   237         /* We don't expect them to fail, but... */
   238         return (-1);
   239     }
   240 
   241     /* Create the lock and event thread */
   242     if (SDL_StartEventThread(flags) < 0) {
   243         SDL_StopEventLoop();
   244         return (-1);
   245     }
   246     return (0);
   247 }
   248 
   249 
   250 /* Add an event to the event queue -- called with the queue locked */
   251 static int
   252 SDL_AddEvent(SDL_Event * event)
   253 {
   254     int tail, added;
   255 
   256     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   257     if (tail == SDL_EventQ.head) {
   258         /* Overflow, drop event */
   259         added = 0;
   260     } else {
   261         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   262         if (event->type == SDL_SYSWMEVENT) {
   263             /* Note that it's possible to lose an event */
   264             int next = SDL_EventQ.wmmsg_next;
   265             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   266             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   267                 &SDL_EventQ.wmmsg[next];
   268             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   269         }
   270         SDL_EventQ.tail = tail;
   271         added = 1;
   272     }
   273     return (added);
   274 }
   275 
   276 /* Cut an event, and return the next valid spot, or the tail */
   277 /*                           -- called with the queue locked */
   278 static int
   279 SDL_CutEvent(int spot)
   280 {
   281     if (spot == SDL_EventQ.head) {
   282         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   283         return (SDL_EventQ.head);
   284     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   285         SDL_EventQ.tail = spot;
   286         return (SDL_EventQ.tail);
   287     } else
   288         /* We cut the middle -- shift everything over */
   289     {
   290         int here, next;
   291 
   292         /* This can probably be optimized with SDL_memcpy() -- careful! */
   293         if (--SDL_EventQ.tail < 0) {
   294             SDL_EventQ.tail = MAXEVENTS - 1;
   295         }
   296         for (here = spot; here != SDL_EventQ.tail; here = next) {
   297             next = (here + 1) % MAXEVENTS;
   298             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   299         }
   300         return (spot);
   301     }
   302     /* NOTREACHED */
   303 }
   304 
   305 /* Lock the event queue, take a peep at it, and unlock it */
   306 int
   307 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   308                Uint32 mask)
   309 {
   310     int i, used;
   311 
   312     /* Don't look after we've quit */
   313     if (!SDL_EventQ.active) {
   314         return (-1);
   315     }
   316     /* Lock the event queue */
   317     used = 0;
   318     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   319         if (action == SDL_ADDEVENT) {
   320             for (i = 0; i < numevents; ++i) {
   321                 used += SDL_AddEvent(&events[i]);
   322             }
   323         } else {
   324             SDL_Event tmpevent;
   325             int spot;
   326 
   327             /* If 'events' is NULL, just see if they exist */
   328             if (events == NULL) {
   329                 action = SDL_PEEKEVENT;
   330                 numevents = 1;
   331                 events = &tmpevent;
   332             }
   333             spot = SDL_EventQ.head;
   334             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   335                 if (mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type)) {
   336                     events[used++] = SDL_EventQ.event[spot];
   337                     if (action == SDL_GETEVENT) {
   338                         spot = SDL_CutEvent(spot);
   339                     } else {
   340                         spot = (spot + 1) % MAXEVENTS;
   341                     }
   342                 } else {
   343                     spot = (spot + 1) % MAXEVENTS;
   344                 }
   345             }
   346         }
   347         SDL_mutexV(SDL_EventQ.lock);
   348     } else {
   349         SDL_SetError("Couldn't lock event queue");
   350         used = -1;
   351     }
   352     return (used);
   353 }
   354 
   355 SDL_bool
   356 SDL_HasEvent(Uint32 mask)
   357 {
   358     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, mask) > 0);
   359 }
   360 
   361 /* Run the system dependent event loops */
   362 void
   363 SDL_PumpEvents(void)
   364 {
   365     if (!SDL_EventThread) {
   366         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   367 
   368         /* Get events from the video subsystem */
   369         if (_this) {
   370             _this->PumpEvents(_this);
   371         }
   372 #if !SDL_JOYSTICK_DISABLED
   373         /* Check for joystick state change */
   374         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   375             SDL_JoystickUpdate();
   376         }
   377 #endif
   378     }
   379 }
   380 
   381 /* Public functions */
   382 
   383 int
   384 SDL_PollEvent(SDL_Event * event)
   385 {
   386     return SDL_WaitEventTimeout(event, 0);
   387 }
   388 
   389 int
   390 SDL_WaitEvent(SDL_Event * event)
   391 {
   392     return SDL_WaitEventTimeout(event, -1);
   393 }
   394 
   395 int
   396 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   397 {
   398     Uint32 expiration = 0;
   399 
   400     if (timeout > 0)
   401         expiration = SDL_GetTicks() + timeout;
   402 
   403     for (;;) {
   404         SDL_PumpEvents();
   405         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   406         case -1:
   407             return 0;
   408         case 1:
   409             return 1;
   410         case 0:
   411             if (timeout == 0) {
   412                 /* Polling and no events, just return */
   413                 return 0;
   414             }
   415             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   416                 /* Timeout expired and no events */
   417                 return 0;
   418             }
   419             SDL_Delay(10);
   420             break;
   421         }
   422     }
   423 }
   424 
   425 int
   426 SDL_PushEvent(SDL_Event * event)
   427 {
   428     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   429         return 0;
   430     }
   431     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0) {
   432         return -1;
   433     }
   434     return 1;
   435 }
   436 
   437 void
   438 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   439 {
   440     SDL_Event bitbucket;
   441 
   442     /* Set filter and discard pending events */
   443     SDL_EventOK = filter;
   444     SDL_EventOKParam = userdata;
   445     while (SDL_PollEvent(&bitbucket) > 0);
   446 }
   447 
   448 SDL_bool
   449 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   450 {
   451     if (filter) {
   452         *filter = SDL_EventOK;
   453     }
   454     if (userdata) {
   455         *userdata = SDL_EventOKParam;
   456     }
   457     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   458 }
   459 
   460 void
   461 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   462 {
   463     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   464         int spot;
   465 
   466         spot = SDL_EventQ.head;
   467         while (spot != SDL_EventQ.tail) {
   468             if (filter(userdata, &SDL_EventQ.event[spot])) {
   469                 spot = (spot + 1) % MAXEVENTS;
   470             } else {
   471                 spot = SDL_CutEvent(spot);
   472             }
   473         }
   474     }
   475     SDL_mutexV(SDL_EventQ.lock);
   476 }
   477 
   478 Uint8
   479 SDL_EventState(Uint8 type, int state)
   480 {
   481     SDL_Event bitbucket;
   482     Uint8 current_state;
   483 
   484     /* If SDL_ALLEVENTS was specified... */
   485     if (type == 0xFF) {
   486         current_state = SDL_IGNORE;
   487         for (type = 0; type < SDL_NUMEVENTS; ++type) {
   488             if (SDL_ProcessEvents[type] != SDL_IGNORE) {
   489                 current_state = SDL_ENABLE;
   490             }
   491             SDL_ProcessEvents[type] = state;
   492             if (state == SDL_ENABLE) {
   493                 SDL_eventstate |= (0x00000001 << (type));
   494             } else {
   495                 SDL_eventstate &= ~(0x00000001 << (type));
   496             }
   497         }
   498         while (SDL_PollEvent(&bitbucket) > 0);
   499         return (current_state);
   500     }
   501 
   502     /* Just set the state for one event type */
   503     current_state = SDL_ProcessEvents[type];
   504     switch (state) {
   505     case SDL_IGNORE:
   506     case SDL_ENABLE:
   507         /* Set state and discard pending events */
   508         SDL_ProcessEvents[type] = state;
   509         if (state == SDL_ENABLE) {
   510             SDL_eventstate |= (0x00000001 << (type));
   511         } else {
   512             SDL_eventstate &= ~(0x00000001 << (type));
   513         }
   514         while (SDL_PollEvent(&bitbucket) > 0);
   515         break;
   516     default:
   517         /* Querying state? */
   518         break;
   519     }
   520     return (current_state);
   521 }
   522 
   523 /* This is a generic event handler.
   524  */
   525 int
   526 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   527 {
   528     int posted;
   529 
   530     posted = 0;
   531     if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
   532         SDL_Event event;
   533         SDL_memset(&event, 0, sizeof(event));
   534         event.type = SDL_SYSWMEVENT;
   535         event.syswm.msg = message;
   536         posted = (SDL_PushEvent(&event) > 0);
   537     }
   538     /* Update internal event state */
   539     return (posted);
   540 }
   541 
   542 /* vi: set ts=4 sw=4 expandtab: */