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