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