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