src/events/SDL_events.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1659 14717b52abc0
child 1666 6e7ec5cb83c3
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     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_AppActiveQuit ();
   223     SDL_KeyboardQuit ();
   224     SDL_MouseQuit ();
   225     SDL_QuitQuit ();
   226 
   227     /* Clean out EventQ */
   228     SDL_EventQ.head = 0;
   229     SDL_EventQ.tail = 0;
   230     SDL_EventQ.wmmsg_next = 0;
   231 }
   232 
   233 /* This function (and associated calls) may be called more than once */
   234 int
   235 SDL_StartEventLoop (Uint32 flags)
   236 {
   237     int retcode;
   238 
   239     /* Clean out the event queue */
   240     SDL_EventThread = NULL;
   241     SDL_EventQ.lock = NULL;
   242     SDL_StopEventLoop ();
   243 
   244     /* No filter to start with, process most event types */
   245     SDL_EventOK = NULL;
   246     SDL_memset (SDL_ProcessEvents, SDL_ENABLE, sizeof (SDL_ProcessEvents));
   247     SDL_eventstate = ~0;
   248     /* It's not save to call SDL_EventState() yet */
   249     SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
   250     SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
   251 
   252     /* Initialize event handlers */
   253     retcode = 0;
   254     retcode += SDL_AppActiveInit ();
   255     retcode += SDL_KeyboardInit ();
   256     retcode += SDL_MouseInit ();
   257     retcode += SDL_QuitInit ();
   258     if (retcode < 0) {
   259         /* We don't expect them to fail, but... */
   260         return (-1);
   261     }
   262 
   263     /* Create the lock and event thread */
   264     if (SDL_StartEventThread (flags) < 0) {
   265         SDL_StopEventLoop ();
   266         return (-1);
   267     }
   268     return (0);
   269 }
   270 
   271 
   272 /* Add an event to the event queue -- called with the queue locked */
   273 static int
   274 SDL_AddEvent (SDL_Event * event)
   275 {
   276     int tail, added;
   277 
   278     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   279     if (tail == SDL_EventQ.head) {
   280         /* Overflow, drop event */
   281         added = 0;
   282     } else {
   283         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   284         if (event->type == SDL_SYSWMEVENT) {
   285             /* Note that it's possible to lose an event */
   286             int next = SDL_EventQ.wmmsg_next;
   287             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   288             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   289                 &SDL_EventQ.wmmsg[next];
   290             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   291         }
   292         SDL_EventQ.tail = tail;
   293         added = 1;
   294     }
   295     return (added);
   296 }
   297 
   298 /* Cut an event, and return the next valid spot, or the tail */
   299 /*                           -- called with the queue locked */
   300 static int
   301 SDL_CutEvent (int spot)
   302 {
   303     if (spot == SDL_EventQ.head) {
   304         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   305         return (SDL_EventQ.head);
   306     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   307         SDL_EventQ.tail = spot;
   308         return (SDL_EventQ.tail);
   309     } else
   310         /* We cut the middle -- shift everything over */
   311     {
   312         int here, next;
   313 
   314         /* This can probably be optimized with SDL_memcpy() -- careful! */
   315         if (--SDL_EventQ.tail < 0) {
   316             SDL_EventQ.tail = MAXEVENTS - 1;
   317         }
   318         for (here = spot; here != SDL_EventQ.tail; here = next) {
   319             next = (here + 1) % MAXEVENTS;
   320             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   321         }
   322         return (spot);
   323     }
   324     /* NOTREACHED */
   325 }
   326 
   327 /* Lock the event queue, take a peep at it, and unlock it */
   328 int
   329 SDL_PeepEvents (SDL_Event * events, int numevents, SDL_eventaction action,
   330                 Uint32 mask)
   331 {
   332     int i, used;
   333 
   334     /* Don't look after we've quit */
   335     if (!SDL_EventQ.active) {
   336         return (-1);
   337     }
   338     /* Lock the event queue */
   339     used = 0;
   340     if (SDL_mutexP (SDL_EventQ.lock) == 0) {
   341         if (action == SDL_ADDEVENT) {
   342             for (i = 0; i < numevents; ++i) {
   343                 used += SDL_AddEvent (&events[i]);
   344             }
   345         } else {
   346             SDL_Event tmpevent;
   347             int spot;
   348 
   349             /* If 'events' is NULL, just see if they exist */
   350             if (events == NULL) {
   351                 action = SDL_PEEKEVENT;
   352                 numevents = 1;
   353                 events = &tmpevent;
   354             }
   355             spot = SDL_EventQ.head;
   356             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   357                 if (mask & SDL_EVENTMASK (SDL_EventQ.event[spot].type)) {
   358                     events[used++] = SDL_EventQ.event[spot];
   359                     if (action == SDL_GETEVENT) {
   360                         spot = SDL_CutEvent (spot);
   361                     } else {
   362                         spot = (spot + 1) % MAXEVENTS;
   363                     }
   364                 } else {
   365                     spot = (spot + 1) % MAXEVENTS;
   366                 }
   367             }
   368         }
   369         SDL_mutexV (SDL_EventQ.lock);
   370     } else {
   371         SDL_SetError ("Couldn't lock event queue");
   372         used = -1;
   373     }
   374     return (used);
   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 
   389         /* Queue pending key-repeat events */
   390         SDL_CheckKeyRepeat ();
   391 
   392 #if !SDL_JOYSTICK_DISABLED
   393         /* Check for joystick state change */
   394         if (SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK)) {
   395             SDL_JoystickUpdate ();
   396         }
   397 #endif
   398     }
   399 }
   400 
   401 /* Public functions */
   402 
   403 int
   404 SDL_PollEvent (SDL_Event * event)
   405 {
   406     SDL_PumpEvents ();
   407 
   408     /* We can't return -1, just return 0 (no event) on error */
   409     if (SDL_PeepEvents (event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0)
   410         return 0;
   411     return 1;
   412 }
   413 
   414 int
   415 SDL_WaitEvent (SDL_Event * event)
   416 {
   417     while (1) {
   418         SDL_PumpEvents ();
   419         switch (SDL_PeepEvents (event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   420         case -1:
   421             return 0;
   422         case 1:
   423             return 1;
   424         case 0:
   425             SDL_Delay (10);
   426         }
   427     }
   428 }
   429 
   430 int
   431 SDL_PushEvent (SDL_Event * event)
   432 {
   433     if (SDL_PeepEvents (event, 1, SDL_ADDEVENT, 0) <= 0)
   434         return -1;
   435     return 0;
   436 }
   437 
   438 void
   439 SDL_SetEventFilter (SDL_EventFilter filter)
   440 {
   441     SDL_Event bitbucket;
   442 
   443     /* Set filter and discard pending events */
   444     SDL_EventOK = filter;
   445     while (SDL_PollEvent (&bitbucket) > 0);
   446 }
   447 
   448 SDL_EventFilter
   449 SDL_GetEventFilter (void)
   450 {
   451     return (SDL_EventOK);
   452 }
   453 
   454 Uint8
   455 SDL_EventState (Uint8 type, int state)
   456 {
   457     SDL_Event bitbucket;
   458     Uint8 current_state;
   459 
   460     /* If SDL_ALLEVENTS was specified... */
   461     if (type == 0xFF) {
   462         current_state = SDL_IGNORE;
   463         for (type = 0; type < SDL_NUMEVENTS; ++type) {
   464             if (SDL_ProcessEvents[type] != SDL_IGNORE) {
   465                 current_state = SDL_ENABLE;
   466             }
   467             SDL_ProcessEvents[type] = state;
   468             if (state == SDL_ENABLE) {
   469                 SDL_eventstate |= (0x00000001 << (type));
   470             } else {
   471                 SDL_eventstate &= ~(0x00000001 << (type));
   472             }
   473         }
   474         while (SDL_PollEvent (&bitbucket) > 0);
   475         return (current_state);
   476     }
   477 
   478     /* Just set the state for one event type */
   479     current_state = SDL_ProcessEvents[type];
   480     switch (state) {
   481     case SDL_IGNORE:
   482     case SDL_ENABLE:
   483         /* Set state and discard pending events */
   484         SDL_ProcessEvents[type] = state;
   485         if (state == SDL_ENABLE) {
   486             SDL_eventstate |= (0x00000001 << (type));
   487         } else {
   488             SDL_eventstate &= ~(0x00000001 << (type));
   489         }
   490         while (SDL_PollEvent (&bitbucket) > 0);
   491         break;
   492     default:
   493         /* Querying state? */
   494         break;
   495     }
   496     return (current_state);
   497 }
   498 
   499 /* This is a generic event handler.
   500  */
   501 int
   502 SDL_PrivateSysWMEvent (SDL_SysWMmsg * message)
   503 {
   504     int posted;
   505 
   506     posted = 0;
   507     if (SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE) {
   508         SDL_Event event;
   509         SDL_memset (&event, 0, sizeof (event));
   510         event.type = SDL_SYSWMEVENT;
   511         event.syswm.msg = message;
   512         if ((SDL_EventOK == NULL) || (*SDL_EventOK) (&event)) {
   513             posted = 1;
   514             SDL_PushEvent (&event);
   515         }
   516     }
   517     /* Update internal event state */
   518     return (posted);
   519 }
   520 
   521 /* vi: set ts=4 sw=4 expandtab: */