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