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