src/events/SDL_events.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Aug 2008 15:10:03 +0000
changeset 2735 204be4fc2726
parent 2129 047245361002
child 2859 99210400e8b9
permissions -rw-r--r--
Final merge of Google Summer of Code 2008 work...

Port SDL 1.3 to the Nintendo DS
by Darren Alton, mentored by Sam Lantinga
     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 
    77 void
    78 SDL_Unlock_EventThread(void)
    79 {
    80     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    81         SDL_mutexV(SDL_EventLock.lock);
    82     }
    83 }
    84 
    85 #ifdef __OS2__
    86 /*
    87  * We'll increase the priority of GobbleEvents thread, so it will process
    88  *  events in time for sure! For this, we need the DosSetPriority() API
    89  *  from the os2.h include file.
    90  */
    91 #define INCL_DOSPROCESS
    92 #include <os2.h>
    93 #include <time.h>
    94 #endif
    95 
    96 static int SDLCALL
    97 SDL_GobbleEvents(void *unused)
    98 {
    99     event_thread = SDL_ThreadID();
   100 
   101 #ifdef __OS2__
   102 #ifdef USE_DOSSETPRIORITY
   103     /* Increase thread priority, so it will process events in time for sure! */
   104     DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, +16, 0);
   105 #endif
   106 #endif
   107 
   108     while (SDL_EventQ.active) {
   109         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   110 
   111         /* Get events from the video subsystem */
   112         if (_this) {
   113             _this->PumpEvents(_this);
   114         }
   115 #if !SDL_JOYSTICK_DISABLED
   116         /* Check for joystick state change */
   117         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   118             SDL_JoystickUpdate();
   119         }
   120 #endif
   121 
   122         /* Give up the CPU for the rest of our timeslice */
   123         SDL_EventLock.safe = 1;
   124         if (SDL_timer_running) {
   125             SDL_ThreadedTimerCheck();
   126         }
   127         SDL_Delay(1);
   128 
   129         /* Check for event locking.
   130            On the P of the lock mutex, if the lock is held, this thread
   131            will wait until the lock is released before continuing.  The
   132            safe flag will be set, meaning that the other thread can go
   133            about it's business.  The safe flag is reset before the V,
   134            so as soon as the mutex is free, other threads can see that
   135            it's not safe to interfere with the event thread.
   136          */
   137         SDL_mutexP(SDL_EventLock.lock);
   138         SDL_EventLock.safe = 0;
   139         SDL_mutexV(SDL_EventLock.lock);
   140     }
   141     SDL_SetTimerThreaded(0);
   142     event_thread = 0;
   143     return (0);
   144 }
   145 
   146 static int
   147 SDL_StartEventThread(Uint32 flags)
   148 {
   149     /* Reset everything to zero */
   150     SDL_EventThread = NULL;
   151     SDL_memset(&SDL_EventLock, 0, sizeof(SDL_EventLock));
   152 
   153     /* Create the lock and set ourselves active */
   154 #if !SDL_THREADS_DISABLED
   155     SDL_EventQ.lock = SDL_CreateMutex();
   156     if (SDL_EventQ.lock == NULL) {
   157         return (-1);
   158     }
   159 #endif /* !SDL_THREADS_DISABLED */
   160     SDL_EventQ.active = 1;
   161 
   162     if ((flags & SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD) {
   163         SDL_EventLock.lock = SDL_CreateMutex();
   164         if (SDL_EventLock.lock == NULL) {
   165             return (-1);
   166         }
   167         SDL_EventLock.safe = 0;
   168 
   169         /* The event thread will handle timers too */
   170         SDL_SetTimerThreaded(2);
   171 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   172 #undef SDL_CreateThread
   173         SDL_EventThread =
   174             SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
   175 #else
   176         SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
   177 #endif
   178         if (SDL_EventThread == NULL) {
   179             return (-1);
   180         }
   181     } else {
   182         event_thread = 0;
   183     }
   184     return (0);
   185 }
   186 
   187 static void
   188 SDL_StopEventThread(void)
   189 {
   190     SDL_EventQ.active = 0;
   191     if (SDL_EventThread) {
   192         SDL_WaitThread(SDL_EventThread, NULL);
   193         SDL_EventThread = NULL;
   194         SDL_DestroyMutex(SDL_EventLock.lock);
   195     }
   196 #ifndef IPOD
   197     SDL_DestroyMutex(SDL_EventQ.lock);
   198 #endif
   199 }
   200 
   201 Uint32
   202 SDL_EventThreadID(void)
   203 {
   204     return (event_thread);
   205 }
   206 
   207 /* Public functions */
   208 
   209 void
   210 SDL_StopEventLoop(void)
   211 {
   212     /* Halt the event thread, if running */
   213     SDL_StopEventThread();
   214 
   215     /* Shutdown event handlers */
   216     SDL_KeyboardQuit();
   217     SDL_MouseQuit();
   218     SDL_QuitQuit();
   219 
   220     /* Clean out EventQ */
   221     SDL_EventQ.head = 0;
   222     SDL_EventQ.tail = 0;
   223     SDL_EventQ.wmmsg_next = 0;
   224 }
   225 
   226 /* This function (and associated calls) may be called more than once */
   227 int
   228 SDL_StartEventLoop(Uint32 flags)
   229 {
   230     int retcode;
   231 
   232     /* Clean out the event queue */
   233     SDL_EventThread = NULL;
   234     SDL_EventQ.lock = NULL;
   235     SDL_StopEventLoop();
   236 
   237     /* No filter to start with, process most event types */
   238     SDL_EventOK = NULL;
   239     SDL_memset(SDL_ProcessEvents, SDL_ENABLE, sizeof(SDL_ProcessEvents));
   240     SDL_eventstate = ~0;
   241     /* It's not save to call SDL_EventState() yet */
   242     SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
   243     SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
   244 
   245     /* Initialize event handlers */
   246     retcode = 0;
   247     retcode += SDL_KeyboardInit();
   248     retcode += SDL_MouseInit();
   249     retcode += SDL_QuitInit();
   250     if (retcode < 0) {
   251         /* We don't expect them to fail, but... */
   252         return (-1);
   253     }
   254 
   255     /* Create the lock and event thread */
   256     if (SDL_StartEventThread(flags) < 0) {
   257         SDL_StopEventLoop();
   258         return (-1);
   259     }
   260     return (0);
   261 }
   262 
   263 
   264 /* Add an event to the event queue -- called with the queue locked */
   265 static int
   266 SDL_AddEvent(SDL_Event * event)
   267 {
   268     int tail, added;
   269 
   270     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   271     if (tail == SDL_EventQ.head) {
   272         /* Overflow, drop event */
   273         added = 0;
   274     } else {
   275         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   276         if (event->type == SDL_SYSWMEVENT) {
   277             /* Note that it's possible to lose an event */
   278             int next = SDL_EventQ.wmmsg_next;
   279             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   280             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   281                 &SDL_EventQ.wmmsg[next];
   282             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   283         }
   284         SDL_EventQ.tail = tail;
   285         added = 1;
   286     }
   287     return (added);
   288 }
   289 
   290 /* Cut an event, and return the next valid spot, or the tail */
   291 /*                           -- called with the queue locked */
   292 static int
   293 SDL_CutEvent(int spot)
   294 {
   295     if (spot == SDL_EventQ.head) {
   296         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   297         return (SDL_EventQ.head);
   298     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   299         SDL_EventQ.tail = spot;
   300         return (SDL_EventQ.tail);
   301     } else
   302         /* We cut the middle -- shift everything over */
   303     {
   304         int here, next;
   305 
   306         /* This can probably be optimized with SDL_memcpy() -- careful! */
   307         if (--SDL_EventQ.tail < 0) {
   308             SDL_EventQ.tail = MAXEVENTS - 1;
   309         }
   310         for (here = spot; here != SDL_EventQ.tail; here = next) {
   311             next = (here + 1) % MAXEVENTS;
   312             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   313         }
   314         return (spot);
   315     }
   316     /* NOTREACHED */
   317 }
   318 
   319 /* Lock the event queue, take a peep at it, and unlock it */
   320 int
   321 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   322                Uint32 mask)
   323 {
   324     int i, used;
   325 
   326     /* Don't look after we've quit */
   327     if (!SDL_EventQ.active) {
   328         return (-1);
   329     }
   330     /* Lock the event queue */
   331     used = 0;
   332     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   333         if (action == SDL_ADDEVENT) {
   334             for (i = 0; i < numevents; ++i) {
   335                 used += SDL_AddEvent(&events[i]);
   336             }
   337         } else {
   338             SDL_Event tmpevent;
   339             int spot;
   340 
   341             /* If 'events' is NULL, just see if they exist */
   342             if (events == NULL) {
   343                 action = SDL_PEEKEVENT;
   344                 numevents = 1;
   345                 events = &tmpevent;
   346             }
   347             spot = SDL_EventQ.head;
   348             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   349                 if (mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type)) {
   350                     events[used++] = SDL_EventQ.event[spot];
   351                     if (action == SDL_GETEVENT) {
   352                         spot = SDL_CutEvent(spot);
   353                     } else {
   354                         spot = (spot + 1) % MAXEVENTS;
   355                     }
   356                 } else {
   357                     spot = (spot + 1) % MAXEVENTS;
   358                 }
   359             }
   360         }
   361         SDL_mutexV(SDL_EventQ.lock);
   362     } else {
   363         SDL_SetError("Couldn't lock event queue");
   364         used = -1;
   365     }
   366     return (used);
   367 }
   368 
   369 SDL_bool
   370 SDL_HasEvent(Uint32 mask)
   371 {
   372     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, mask) > 0);
   373 }
   374 
   375 /* Run the system dependent event loops */
   376 void
   377 SDL_PumpEvents(void)
   378 {
   379     if (!SDL_EventThread) {
   380         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   381 
   382         /* Get events from the video subsystem */
   383         if (_this) {
   384             _this->PumpEvents(_this);
   385         }
   386 #if !SDL_JOYSTICK_DISABLED
   387         /* Check for joystick state change */
   388         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   389             SDL_JoystickUpdate();
   390         }
   391 #endif
   392     }
   393 }
   394 
   395 /* Public functions */
   396 
   397 int
   398 SDL_PollEvent(SDL_Event * event)
   399 {
   400     SDL_PumpEvents();
   401 
   402     /* We can't return -1, just return 0 (no event) on error */
   403     if (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0)
   404         return 0;
   405     return 1;
   406 }
   407 
   408 int
   409 SDL_WaitEvent(SDL_Event * event)
   410 {
   411     while (1) {
   412         SDL_PumpEvents();
   413         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   414         case -1:
   415             return 0;
   416         case 1:
   417             return 1;
   418         case 0:
   419             SDL_Delay(10);
   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: */