src/events/SDL_events.c
author Sam Lantinga
Sat, 19 Sep 2009 06:24:07 +0000
changeset 3268 82ce350be859
parent 3259 22ac66da0765
child 3269 a67a961e2171
permissions -rw-r--r--
Removed outdated iPodLinux support
     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     if (SDL_EventQ.lock) {
   199         SDL_DestroyMutex(SDL_EventQ.lock);
   200         SDL_EventQ.lock = NULL;
   201     }
   202 }
   203 
   204 Uint32
   205 SDL_EventThreadID(void)
   206 {
   207     return (event_thread);
   208 }
   209 
   210 /* Public functions */
   211 
   212 void
   213 SDL_StopEventLoop(void)
   214 {
   215     /* Halt the event thread, if running */
   216     SDL_StopEventThread();
   217 
   218     /* Shutdown event handlers */
   219     SDL_KeyboardQuit();
   220     SDL_MouseQuit();
   221     SDL_QuitQuit();
   222 
   223     /* Clean out EventQ */
   224     SDL_EventQ.head = 0;
   225     SDL_EventQ.tail = 0;
   226     SDL_EventQ.wmmsg_next = 0;
   227 }
   228 
   229 /* This function (and associated calls) may be called more than once */
   230 int
   231 SDL_StartEventLoop(Uint32 flags)
   232 {
   233     int retcode;
   234 
   235     /* Clean out the event queue */
   236     SDL_EventThread = NULL;
   237     SDL_EventQ.lock = NULL;
   238     SDL_StopEventLoop();
   239 
   240     /* No filter to start with, process most event types */
   241     SDL_EventOK = NULL;
   242     SDL_memset(SDL_ProcessEvents, SDL_ENABLE, sizeof(SDL_ProcessEvents));
   243     SDL_eventstate = ~0;
   244     /* It's not save to call SDL_EventState() yet */
   245     SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
   246     SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
   247 
   248     /* Initialize event handlers */
   249     retcode = 0;
   250     retcode += SDL_KeyboardInit();
   251     retcode += SDL_MouseInit();
   252     retcode += SDL_QuitInit();
   253     if (retcode < 0) {
   254         /* We don't expect them to fail, but... */
   255         return (-1);
   256     }
   257 
   258     /* Create the lock and event thread */
   259     if (SDL_StartEventThread(flags) < 0) {
   260         SDL_StopEventLoop();
   261         return (-1);
   262     }
   263     return (0);
   264 }
   265 
   266 
   267 /* Add an event to the event queue -- called with the queue locked */
   268 static int
   269 SDL_AddEvent(SDL_Event * event)
   270 {
   271     int tail, added;
   272 
   273     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   274     if (tail == SDL_EventQ.head) {
   275         /* Overflow, drop event */
   276         added = 0;
   277     } else {
   278         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   279         if (event->type == SDL_SYSWMEVENT) {
   280             /* Note that it's possible to lose an event */
   281             int next = SDL_EventQ.wmmsg_next;
   282             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   283             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   284                 &SDL_EventQ.wmmsg[next];
   285             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   286         }
   287         SDL_EventQ.tail = tail;
   288         added = 1;
   289     }
   290     return (added);
   291 }
   292 
   293 /* Cut an event, and return the next valid spot, or the tail */
   294 /*                           -- called with the queue locked */
   295 static int
   296 SDL_CutEvent(int spot)
   297 {
   298     if (spot == SDL_EventQ.head) {
   299         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   300         return (SDL_EventQ.head);
   301     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   302         SDL_EventQ.tail = spot;
   303         return (SDL_EventQ.tail);
   304     } else
   305         /* We cut the middle -- shift everything over */
   306     {
   307         int here, next;
   308 
   309         /* This can probably be optimized with SDL_memcpy() -- careful! */
   310         if (--SDL_EventQ.tail < 0) {
   311             SDL_EventQ.tail = MAXEVENTS - 1;
   312         }
   313         for (here = spot; here != SDL_EventQ.tail; here = next) {
   314             next = (here + 1) % MAXEVENTS;
   315             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   316         }
   317         return (spot);
   318     }
   319     /* NOTREACHED */
   320 }
   321 
   322 /* Lock the event queue, take a peep at it, and unlock it */
   323 int
   324 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   325                Uint32 mask)
   326 {
   327     int i, used;
   328 
   329     /* Don't look after we've quit */
   330     if (!SDL_EventQ.active) {
   331         return (-1);
   332     }
   333     /* Lock the event queue */
   334     used = 0;
   335     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   336         if (action == SDL_ADDEVENT) {
   337             for (i = 0; i < numevents; ++i) {
   338                 used += SDL_AddEvent(&events[i]);
   339             }
   340         } else {
   341             SDL_Event tmpevent;
   342             int spot;
   343 
   344             /* If 'events' is NULL, just see if they exist */
   345             if (events == NULL) {
   346                 action = SDL_PEEKEVENT;
   347                 numevents = 1;
   348                 events = &tmpevent;
   349             }
   350             spot = SDL_EventQ.head;
   351             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   352                 if (mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type)) {
   353                     events[used++] = SDL_EventQ.event[spot];
   354                     if (action == SDL_GETEVENT) {
   355                         spot = SDL_CutEvent(spot);
   356                     } else {
   357                         spot = (spot + 1) % MAXEVENTS;
   358                     }
   359                 } else {
   360                     spot = (spot + 1) % MAXEVENTS;
   361                 }
   362             }
   363         }
   364         SDL_mutexV(SDL_EventQ.lock);
   365     } else {
   366         SDL_SetError("Couldn't lock event queue");
   367         used = -1;
   368     }
   369     return (used);
   370 }
   371 
   372 SDL_bool
   373 SDL_HasEvent(Uint32 mask)
   374 {
   375     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, mask) > 0);
   376 }
   377 
   378 /* Run the system dependent event loops */
   379 void
   380 SDL_PumpEvents(void)
   381 {
   382     if (!SDL_EventThread) {
   383         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   384 
   385         /* Get events from the video subsystem */
   386         if (_this) {
   387             _this->PumpEvents(_this);
   388         }
   389 #if !SDL_JOYSTICK_DISABLED
   390         /* Check for joystick state change */
   391         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   392             SDL_JoystickUpdate();
   393         }
   394 #endif
   395     }
   396 }
   397 
   398 /* Public functions */
   399 
   400 int
   401 SDL_PollEvent(SDL_Event * event)
   402 {
   403     return SDL_WaitEventTimeout(event, 0);
   404 }
   405 
   406 int
   407 SDL_WaitEvent(SDL_Event * event)
   408 {
   409     return SDL_WaitEventTimeout(event, -1);
   410 }
   411 
   412 int
   413 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   414 {
   415     Uint32 expiration = 0;
   416 
   417     if (timeout > 0)
   418         expiration = SDL_GetTicks() + timeout;
   419 
   420     for (;;) {
   421         SDL_PumpEvents();
   422         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   423         case -1:
   424             return 0;
   425         case 1:
   426             return 1;
   427         case 0:
   428             if (timeout == 0) {
   429                 /* Polling and no events, just return */
   430                 return 0;
   431             }
   432             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   433                 /* Timeout expired and no events */
   434                 return 0;
   435             }
   436             SDL_Delay(10);
   437             break;
   438         }
   439     }
   440 }
   441 
   442 int
   443 SDL_PushEvent(SDL_Event * event)
   444 {
   445     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   446         return 0;
   447     }
   448     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0) {
   449         return -1;
   450     }
   451     return 1;
   452 }
   453 
   454 void
   455 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   456 {
   457     SDL_Event bitbucket;
   458 
   459     /* Set filter and discard pending events */
   460     SDL_EventOK = filter;
   461     SDL_EventOKParam = userdata;
   462     while (SDL_PollEvent(&bitbucket) > 0);
   463 }
   464 
   465 SDL_bool
   466 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   467 {
   468     if (filter) {
   469         *filter = SDL_EventOK;
   470     }
   471     if (userdata) {
   472         *userdata = SDL_EventOKParam;
   473     }
   474     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   475 }
   476 
   477 void
   478 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   479 {
   480     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   481         int spot;
   482 
   483         spot = SDL_EventQ.head;
   484         while (spot != SDL_EventQ.tail) {
   485             if (filter(userdata, &SDL_EventQ.event[spot])) {
   486                 spot = (spot + 1) % MAXEVENTS;
   487             } else {
   488                 spot = SDL_CutEvent(spot);
   489             }
   490         }
   491     }
   492     SDL_mutexV(SDL_EventQ.lock);
   493 }
   494 
   495 Uint8
   496 SDL_EventState(Uint8 type, int state)
   497 {
   498     SDL_Event bitbucket;
   499     Uint8 current_state;
   500 
   501     /* If SDL_ALLEVENTS was specified... */
   502     if (type == 0xFF) {
   503         current_state = SDL_IGNORE;
   504         for (type = 0; type < SDL_NUMEVENTS; ++type) {
   505             if (SDL_ProcessEvents[type] != SDL_IGNORE) {
   506                 current_state = SDL_ENABLE;
   507             }
   508             SDL_ProcessEvents[type] = state;
   509             if (state == SDL_ENABLE) {
   510                 SDL_eventstate |= (0x00000001 << (type));
   511             } else {
   512                 SDL_eventstate &= ~(0x00000001 << (type));
   513             }
   514         }
   515         while (SDL_PollEvent(&bitbucket) > 0);
   516         return (current_state);
   517     }
   518 
   519     /* Just set the state for one event type */
   520     current_state = SDL_ProcessEvents[type];
   521     switch (state) {
   522     case SDL_IGNORE:
   523     case SDL_ENABLE:
   524         /* Set state and discard pending events */
   525         SDL_ProcessEvents[type] = state;
   526         if (state == SDL_ENABLE) {
   527             SDL_eventstate |= (0x00000001 << (type));
   528         } else {
   529             SDL_eventstate &= ~(0x00000001 << (type));
   530         }
   531         while (SDL_PollEvent(&bitbucket) > 0);
   532         break;
   533     default:
   534         /* Querying state? */
   535         break;
   536     }
   537     return (current_state);
   538 }
   539 
   540 /* This is a generic event handler.
   541  */
   542 int
   543 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   544 {
   545     int posted;
   546 
   547     posted = 0;
   548     if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
   549         SDL_Event event;
   550         SDL_memset(&event, 0, sizeof(event));
   551         event.type = SDL_SYSWMEVENT;
   552         event.syswm.msg = message;
   553         posted = (SDL_PushEvent(&event) > 0);
   554     }
   555     /* Update internal event state */
   556     return (posted);
   557 }
   558 
   559 /* vi: set ts=4 sw=4 expandtab: */