src/events/SDL_events.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 29 Oct 2006 14:45:46 +0000
changeset 2063 dea73e1d07b0
parent 1978 542c78b6fb12
child 2129 047245361002
permissions -rw-r--r--
Merged Ryan's fix, in case we need it later.
     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         return (-1);
   161     }
   162 #endif /* !SDL_THREADS_DISABLED */
   163     SDL_EventQ.active = 1;
   164 
   165     if ((flags & SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD) {
   166         SDL_EventLock.lock = SDL_CreateMutex();
   167         if (SDL_EventLock.lock == NULL) {
   168             return (-1);
   169         }
   170         SDL_EventLock.safe = 0;
   171 
   172         /* The event thread will handle timers too */
   173         SDL_SetTimerThreaded(2);
   174 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   175 #undef SDL_CreateThread
   176         SDL_EventThread =
   177             SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
   178 #else
   179         SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
   180 #endif
   181         if (SDL_EventThread == NULL) {
   182             return (-1);
   183         }
   184     } else {
   185         event_thread = 0;
   186     }
   187     return (0);
   188 }
   189 
   190 static void
   191 SDL_StopEventThread(void)
   192 {
   193     SDL_EventQ.active = 0;
   194     if (SDL_EventThread) {
   195         SDL_WaitThread(SDL_EventThread, NULL);
   196         SDL_EventThread = NULL;
   197         SDL_DestroyMutex(SDL_EventLock.lock);
   198     }
   199 #ifndef IPOD
   200     SDL_DestroyMutex(SDL_EventQ.lock);
   201 #endif
   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 
   390         /* Queue pending key-repeat events */
   391         SDL_CheckKeyRepeat();
   392 
   393 #if !SDL_JOYSTICK_DISABLED
   394         /* Check for joystick state change */
   395         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   396             SDL_JoystickUpdate();
   397         }
   398 #endif
   399     }
   400 }
   401 
   402 /* Public functions */
   403 
   404 int
   405 SDL_PollEvent(SDL_Event * event)
   406 {
   407     SDL_PumpEvents();
   408 
   409     /* We can't return -1, just return 0 (no event) on error */
   410     if (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0)
   411         return 0;
   412     return 1;
   413 }
   414 
   415 int
   416 SDL_WaitEvent(SDL_Event * event)
   417 {
   418     while (1) {
   419         SDL_PumpEvents();
   420         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   421         case -1:
   422             return 0;
   423         case 1:
   424             return 1;
   425         case 0:
   426             SDL_Delay(10);
   427         }
   428     }
   429 }
   430 
   431 int
   432 SDL_PushEvent(SDL_Event * event)
   433 {
   434     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   435         return 0;
   436     }
   437     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0) {
   438         return -1;
   439     }
   440     return 1;
   441 }
   442 
   443 void
   444 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   445 {
   446     SDL_Event bitbucket;
   447 
   448     /* Set filter and discard pending events */
   449     SDL_EventOK = filter;
   450     SDL_EventOKParam = userdata;
   451     while (SDL_PollEvent(&bitbucket) > 0);
   452 }
   453 
   454 SDL_bool
   455 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   456 {
   457     if (filter) {
   458         *filter = SDL_EventOK;
   459     }
   460     if (userdata) {
   461         *userdata = SDL_EventOKParam;
   462     }
   463     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   464 }
   465 
   466 void
   467 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   468 {
   469     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   470         int spot;
   471 
   472         spot = SDL_EventQ.head;
   473         while (spot != SDL_EventQ.tail) {
   474             if (filter(userdata, &SDL_EventQ.event[spot])) {
   475                 spot = (spot + 1) % MAXEVENTS;
   476             } else {
   477                 spot = SDL_CutEvent(spot);
   478             }
   479         }
   480     }
   481     SDL_mutexV(SDL_EventQ.lock);
   482 }
   483 
   484 Uint8
   485 SDL_EventState(Uint8 type, int state)
   486 {
   487     SDL_Event bitbucket;
   488     Uint8 current_state;
   489 
   490     /* If SDL_ALLEVENTS was specified... */
   491     if (type == 0xFF) {
   492         current_state = SDL_IGNORE;
   493         for (type = 0; type < SDL_NUMEVENTS; ++type) {
   494             if (SDL_ProcessEvents[type] != SDL_IGNORE) {
   495                 current_state = SDL_ENABLE;
   496             }
   497             SDL_ProcessEvents[type] = state;
   498             if (state == SDL_ENABLE) {
   499                 SDL_eventstate |= (0x00000001 << (type));
   500             } else {
   501                 SDL_eventstate &= ~(0x00000001 << (type));
   502             }
   503         }
   504         while (SDL_PollEvent(&bitbucket) > 0);
   505         return (current_state);
   506     }
   507 
   508     /* Just set the state for one event type */
   509     current_state = SDL_ProcessEvents[type];
   510     switch (state) {
   511     case SDL_IGNORE:
   512     case SDL_ENABLE:
   513         /* Set state and discard pending events */
   514         SDL_ProcessEvents[type] = state;
   515         if (state == SDL_ENABLE) {
   516             SDL_eventstate |= (0x00000001 << (type));
   517         } else {
   518             SDL_eventstate &= ~(0x00000001 << (type));
   519         }
   520         while (SDL_PollEvent(&bitbucket) > 0);
   521         break;
   522     default:
   523         /* Querying state? */
   524         break;
   525     }
   526     return (current_state);
   527 }
   528 
   529 /* This is a generic event handler.
   530  */
   531 int
   532 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   533 {
   534     int posted;
   535 
   536     posted = 0;
   537     if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
   538         SDL_Event event;
   539         SDL_memset(&event, 0, sizeof(event));
   540         event.type = SDL_SYSWMEVENT;
   541         event.syswm.msg = message;
   542         posted = (SDL_PushEvent(&event) > 0);
   543     }
   544     /* Update internal event state */
   545     return (posted);
   546 }
   547 
   548 /* vi: set ts=4 sw=4 expandtab: */