src/events/SDL_events.c
author Paul Hunkin
Sun, 23 May 2010 15:12:41 +1200
changeset 4698 52697090c967
parent 4460 363604b42e84
child 4729 1f7ad083fd3c
permissions -rw-r--r--
Fix a compile error when SDL_JOYSTICK_DISABLED is set
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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 
    41 typedef struct {
    42     Uint32 bits[8];
    43 } SDL_DisabledEventBlock;
    44 
    45 static SDL_DisabledEventBlock *SDL_disabled_events[256];
    46 static Uint32 SDL_userevents = SDL_USEREVENT;
    47 
    48 /* Private data -- event queue */
    49 #define MAXEVENTS	128
    50 static struct
    51 {
    52     SDL_mutex *lock;
    53     int active;
    54     int head;
    55     int tail;
    56     SDL_Event event[MAXEVENTS];
    57     int wmmsg_next;
    58     struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    59 } SDL_EventQ;
    60 
    61 /* Private data -- event locking structure */
    62 static struct
    63 {
    64     SDL_mutex *lock;
    65     int safe;
    66 } SDL_EventLock;
    67 
    68 /* Thread functions */
    69 static SDL_Thread *SDL_EventThread = NULL;      /* Thread handle */
    70 static SDL_threadID event_thread;     /* The event thread id */
    71 
    72 void
    73 SDL_Lock_EventThread(void)
    74 {
    75     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    76         /* Grab lock and spin until we're sure event thread stopped */
    77         SDL_mutexP(SDL_EventLock.lock);
    78         while (!SDL_EventLock.safe) {
    79             SDL_Delay(1);
    80         }
    81     }
    82 }
    83 
    84 void
    85 SDL_Unlock_EventThread(void)
    86 {
    87     if (SDL_EventThread && (SDL_ThreadID() != event_thread)) {
    88         SDL_mutexV(SDL_EventLock.lock);
    89     }
    90 }
    91 
    92 static __inline__ SDL_bool
    93 SDL_ShouldPollJoystick()
    94 {
    95 #if !SDL_JOYSTICK_DISABLED
    96     if (SDL_numjoysticks &&
    97         (!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] ||
    98          SDL_JoystickEventState(SDL_QUERY))) {
    99         return SDL_TRUE;
   100     }
   101 #endif
   102     return SDL_FALSE;
   103 }
   104 
   105 static int SDLCALL
   106 SDL_GobbleEvents(void *unused)
   107 {
   108     event_thread = SDL_ThreadID();
   109 
   110     while (SDL_EventQ.active) {
   111         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   112 
   113         /* Get events from the video subsystem */
   114         if (_this) {
   115             _this->PumpEvents(_this);
   116         }
   117 #if !SDL_JOYSTICK_DISABLED
   118         /* Check for joystick state change */
   119         if (SDL_ShouldPollJoystick()) {
   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         return (-1);
   160     }
   161 #endif /* !SDL_THREADS_DISABLED */
   162     SDL_EventQ.active = 1;
   163 
   164     if ((flags & SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD) {
   165         SDL_EventLock.lock = SDL_CreateMutex();
   166         if (SDL_EventLock.lock == NULL) {
   167             return (-1);
   168         }
   169         SDL_EventLock.safe = 0;
   170 
   171         /* The event thread will handle timers too */
   172         SDL_SetTimerThreaded(2);
   173 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC)
   174 #undef SDL_CreateThread
   175         SDL_EventThread =
   176             SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
   177 #else
   178         SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
   179 #endif
   180         if (SDL_EventThread == NULL) {
   181             return (-1);
   182         }
   183     } else {
   184         event_thread = 0;
   185     }
   186     return (0);
   187 }
   188 
   189 static void
   190 SDL_StopEventThread(void)
   191 {
   192     SDL_EventQ.active = 0;
   193     if (SDL_EventThread) {
   194         SDL_WaitThread(SDL_EventThread, NULL);
   195         SDL_EventThread = NULL;
   196         SDL_DestroyMutex(SDL_EventLock.lock);
   197         SDL_EventLock.lock = NULL;
   198     }
   199     if (SDL_EventQ.lock) {
   200         SDL_DestroyMutex(SDL_EventQ.lock);
   201         SDL_EventQ.lock = NULL;
   202     }
   203 }
   204 
   205 SDL_threadID
   206 SDL_EventThreadID(void)
   207 {
   208     return (event_thread);
   209 }
   210 
   211 /* Public functions */
   212 
   213 void
   214 SDL_StopEventLoop(void)
   215 {
   216     int i;
   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     /* Clear disabled event state */
   232     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   233         if (SDL_disabled_events[i]) {
   234             SDL_free(SDL_disabled_events[i]);
   235             SDL_disabled_events[i] = NULL;
   236         }
   237     }
   238 }
   239 
   240 /* This function (and associated calls) may be called more than once */
   241 int
   242 SDL_StartEventLoop(Uint32 flags)
   243 {
   244     int retcode;
   245 
   246     /* Clean out the event queue */
   247     SDL_EventThread = NULL;
   248     SDL_EventQ.lock = NULL;
   249     SDL_StopEventLoop();
   250 
   251     /* No filter to start with, process most event types */
   252     SDL_EventOK = NULL;
   253     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   254 
   255     /* Initialize event handlers */
   256     retcode = 0;
   257     retcode += SDL_KeyboardInit();
   258     retcode += SDL_MouseInit();
   259     retcode += SDL_QuitInit();
   260     if (retcode < 0) {
   261         /* We don't expect them to fail, but... */
   262         return (-1);
   263     }
   264 
   265     /* Create the lock and event thread */
   266     if (SDL_StartEventThread(flags) < 0) {
   267         SDL_StopEventLoop();
   268         return (-1);
   269     }
   270     return (0);
   271 }
   272 
   273 
   274 /* Add an event to the event queue -- called with the queue locked */
   275 static int
   276 SDL_AddEvent(SDL_Event * event)
   277 {
   278     int tail, added;
   279 
   280     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   281     if (tail == SDL_EventQ.head) {
   282         /* Overflow, drop event */
   283         added = 0;
   284     } else {
   285         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   286         if (event->type == SDL_SYSWMEVENT) {
   287             /* Note that it's possible to lose an event */
   288             int next = SDL_EventQ.wmmsg_next;
   289             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   290             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   291                 &SDL_EventQ.wmmsg[next];
   292             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   293         }
   294         SDL_EventQ.tail = tail;
   295         added = 1;
   296     }
   297     return (added);
   298 }
   299 
   300 /* Cut an event, and return the next valid spot, or the tail */
   301 /*                           -- called with the queue locked */
   302 static int
   303 SDL_CutEvent(int spot)
   304 {
   305     if (spot == SDL_EventQ.head) {
   306         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   307         return (SDL_EventQ.head);
   308     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   309         SDL_EventQ.tail = spot;
   310         return (SDL_EventQ.tail);
   311     } else
   312         /* We cut the middle -- shift everything over */
   313     {
   314         int here, next;
   315 
   316         /* This can probably be optimized with SDL_memcpy() -- careful! */
   317         if (--SDL_EventQ.tail < 0) {
   318             SDL_EventQ.tail = MAXEVENTS - 1;
   319         }
   320         for (here = spot; here != SDL_EventQ.tail; here = next) {
   321             next = (here + 1) % MAXEVENTS;
   322             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   323         }
   324         return (spot);
   325     }
   326     /* NOTREACHED */
   327 }
   328 
   329 /* Lock the event queue, take a peep at it, and unlock it */
   330 int
   331 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   332                Uint32 minType, Uint32 maxType)
   333 {
   334     int i, used;
   335 
   336     /* Don't look after we've quit */
   337     if (!SDL_EventQ.active) {
   338         return (-1);
   339     }
   340     /* Lock the event queue */
   341     used = 0;
   342     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   343         if (action == SDL_ADDEVENT) {
   344             for (i = 0; i < numevents; ++i) {
   345                 used += SDL_AddEvent(&events[i]);
   346             }
   347         } else {
   348             SDL_Event tmpevent;
   349             int spot;
   350 
   351             /* If 'events' is NULL, just see if they exist */
   352             if (events == NULL) {
   353                 action = SDL_PEEKEVENT;
   354                 numevents = 1;
   355                 events = &tmpevent;
   356             }
   357             spot = SDL_EventQ.head;
   358             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   359                 Uint32 type = SDL_EventQ.event[spot].type;
   360                 if (minType <= type && type <= maxType) {
   361                     events[used++] = SDL_EventQ.event[spot];
   362                     if (action == SDL_GETEVENT) {
   363                         spot = SDL_CutEvent(spot);
   364                     } else {
   365                         spot = (spot + 1) % MAXEVENTS;
   366                     }
   367                 } else {
   368                     spot = (spot + 1) % MAXEVENTS;
   369                 }
   370             }
   371         }
   372         SDL_mutexV(SDL_EventQ.lock);
   373     } else {
   374         SDL_SetError("Couldn't lock event queue");
   375         used = -1;
   376     }
   377     return (used);
   378 }
   379 
   380 SDL_bool
   381 SDL_HasEvent(Uint32 type)
   382 {
   383     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   384 }
   385 
   386 SDL_bool
   387 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   388 {
   389     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   390 }
   391 
   392 void
   393 SDL_FlushEvent(Uint32 type)
   394 {
   395     SDL_FlushEvents(type, type);
   396 }
   397 
   398 void
   399 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   400 {
   401     /* Don't look after we've quit */
   402     if (!SDL_EventQ.active) {
   403         return;
   404     }
   405 
   406     /* Make sure the events are current */
   407     SDL_PumpEvents();
   408 
   409     /* Lock the event queue */
   410     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   411         int spot = SDL_EventQ.head;
   412         while (spot != SDL_EventQ.tail) {
   413             Uint32 type = SDL_EventQ.event[spot].type;
   414             if (minType <= type && type <= maxType) {
   415                 spot = SDL_CutEvent(spot);
   416             } else {
   417                 spot = (spot + 1) % MAXEVENTS;
   418             }
   419         }
   420         SDL_mutexV(SDL_EventQ.lock);
   421     }
   422 }
   423 
   424 /* Run the system dependent event loops */
   425 void
   426 SDL_PumpEvents(void)
   427 {
   428     if (!SDL_EventThread) {
   429         SDL_VideoDevice *_this = SDL_GetVideoDevice();
   430 
   431         /* Get events from the video subsystem */
   432         if (_this) {
   433             _this->PumpEvents(_this);
   434         }
   435 #if !SDL_JOYSTICK_DISABLED
   436         /* Check for joystick state change */
   437         if (SDL_ShouldPollJoystick()) {
   438             SDL_JoystickUpdate();
   439         }
   440 #endif
   441     }
   442 }
   443 
   444 /* Public functions */
   445 
   446 int
   447 SDL_PollEvent(SDL_Event * event)
   448 {
   449     return SDL_WaitEventTimeout(event, 0);
   450 }
   451 
   452 int
   453 SDL_WaitEvent(SDL_Event * event)
   454 {
   455     return SDL_WaitEventTimeout(event, -1);
   456 }
   457 
   458 int
   459 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   460 {
   461     Uint32 expiration = 0;
   462 
   463     if (timeout > 0)
   464         expiration = SDL_GetTicks() + timeout;
   465 
   466     for (;;) {
   467         SDL_PumpEvents();
   468         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   469         case -1:
   470             return 0;
   471         case 1:
   472             return 1;
   473         case 0:
   474             if (timeout == 0) {
   475                 /* Polling and no events, just return */
   476                 return 0;
   477             }
   478             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   479                 /* Timeout expired and no events */
   480                 return 0;
   481             }
   482             SDL_Delay(10);
   483             break;
   484         }
   485     }
   486 }
   487 
   488 int
   489 SDL_PushEvent(SDL_Event * event)
   490 {
   491     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   492         return 0;
   493     }
   494     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   495         return -1;
   496     }
   497     return 1;
   498 }
   499 
   500 void
   501 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   502 {
   503     SDL_Event bitbucket;
   504 
   505     /* Set filter and discard pending events */
   506     SDL_EventOK = filter;
   507     SDL_EventOKParam = userdata;
   508     while (SDL_PollEvent(&bitbucket) > 0);
   509 }
   510 
   511 SDL_bool
   512 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   513 {
   514     if (filter) {
   515         *filter = SDL_EventOK;
   516     }
   517     if (userdata) {
   518         *userdata = SDL_EventOKParam;
   519     }
   520     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   521 }
   522 
   523 void
   524 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   525 {
   526     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   527         int spot;
   528 
   529         spot = SDL_EventQ.head;
   530         while (spot != SDL_EventQ.tail) {
   531             if (filter(userdata, &SDL_EventQ.event[spot])) {
   532                 spot = (spot + 1) % MAXEVENTS;
   533             } else {
   534                 spot = SDL_CutEvent(spot);
   535             }
   536         }
   537     }
   538     SDL_mutexV(SDL_EventQ.lock);
   539 }
   540 
   541 Uint8
   542 SDL_EventState(Uint32 type, int state)
   543 {
   544     Uint8 current_state;
   545     Uint8 hi = ((type >> 8) & 0xff);
   546     Uint8 lo = (type & 0xff);
   547 
   548     if (SDL_disabled_events[hi] &&
   549         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   550         current_state = SDL_DISABLE;
   551     } else {
   552         current_state = SDL_ENABLE;
   553     }
   554 
   555     if (state != current_state)
   556     {
   557         switch (state) {
   558         case SDL_DISABLE:
   559             /* Disable this event type and discard pending events */
   560             if (!SDL_disabled_events[hi]) {
   561                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   562                 if (!SDL_disabled_events[hi]) {
   563                     /* Out of memory, nothing we can do... */
   564                     break;
   565                 }
   566             }
   567             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   568             SDL_FlushEvent(type);
   569             break;
   570         case SDL_ENABLE:
   571             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   572             break;
   573         default:
   574             /* Querying state... */
   575             break;
   576         }
   577     }
   578 
   579     return current_state;
   580 }
   581 
   582 Uint32
   583 SDL_RegisterEvents(int numevents)
   584 {
   585     Uint32 event_base;
   586 
   587     if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   588         event_base = SDL_userevents;
   589         SDL_userevents += numevents;
   590     } else {
   591         event_base = (Uint32)-1;
   592     }
   593     return event_base;
   594 }
   595 
   596 /* This is a generic event handler.
   597  */
   598 int
   599 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   600 {
   601     int posted;
   602 
   603     posted = 0;
   604     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   605         SDL_Event event;
   606         SDL_memset(&event, 0, sizeof(event));
   607         event.type = SDL_SYSWMEVENT;
   608         event.syswm.msg = message;
   609         posted = (SDL_PushEvent(&event) > 0);
   610     }
   611     /* Update internal event state */
   612     return (posted);
   613 }
   614 
   615 /* vi: set ts=4 sw=4 expandtab: */