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