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