src/events/SDL_events.c
author Sam Lantinga
Thu, 08 Nov 2012 11:18:21 -0800
changeset 6668 59c7c2db7638
parent 6654 2ecfb25be1e2
child 6670 d4883f657288
permissions -rw-r--r--
Leave text input enabled unless text input shows some on-screen UI elements
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* General event handling code for SDL */
    24 
    25 #include "SDL.h"
    26 #include "SDL_events.h"
    27 #include "SDL_syswm.h"
    28 #include "SDL_thread.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 #include "../video/SDL_sysvideo.h"
    35 
    36 /* Public data -- the event filter */
    37 SDL_EventFilter SDL_EventOK = NULL;
    38 void *SDL_EventOKParam;
    39 
    40 typedef struct SDL_EventWatcher {
    41     SDL_EventFilter callback;
    42     void *userdata;
    43     struct SDL_EventWatcher *next;
    44 } SDL_EventWatcher;
    45 
    46 static SDL_EventWatcher *SDL_event_watchers = NULL;
    47 
    48 typedef struct {
    49     Uint32 bits[8];
    50 } SDL_DisabledEventBlock;
    51 
    52 static SDL_DisabledEventBlock *SDL_disabled_events[256];
    53 static Uint32 SDL_userevents = SDL_USEREVENT;
    54 
    55 /* Private data -- event queue */
    56 #define MAXEVENTS	128
    57 static struct
    58 {
    59     SDL_mutex *lock;
    60     int active;
    61     int head;
    62     int tail;
    63     SDL_Event event[MAXEVENTS];
    64     int wmmsg_next;
    65     struct SDL_SysWMmsg wmmsg[MAXEVENTS];
    66 } SDL_EventQ = { NULL, 1 };
    67 
    68 
    69 static __inline__ SDL_bool
    70 SDL_ShouldPollJoystick()
    71 {
    72 #if !SDL_JOYSTICK_DISABLED
    73     if (SDL_numjoysticks &&
    74         (!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] ||
    75          SDL_JoystickEventState(SDL_QUERY))) {
    76         return SDL_TRUE;
    77     }
    78 #endif
    79     return SDL_FALSE;
    80 }
    81 
    82 /* Public functions */
    83 
    84 void
    85 SDL_StopEventLoop(void)
    86 {
    87     int i;
    88 
    89     SDL_EventQ.active = 0;
    90 
    91     if (SDL_EventQ.lock) {
    92         SDL_DestroyMutex(SDL_EventQ.lock);
    93         SDL_EventQ.lock = NULL;
    94     }
    95 
    96     /* Clean out EventQ */
    97     SDL_EventQ.head = 0;
    98     SDL_EventQ.tail = 0;
    99     SDL_EventQ.wmmsg_next = 0;
   100 
   101     /* Clear disabled event state */
   102     for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
   103         if (SDL_disabled_events[i]) {
   104             SDL_free(SDL_disabled_events[i]);
   105             SDL_disabled_events[i] = NULL;
   106         }
   107     }
   108 
   109     while (SDL_event_watchers) {
   110         SDL_EventWatcher *tmp = SDL_event_watchers;
   111         SDL_event_watchers = tmp->next;
   112         SDL_free(tmp);
   113     }
   114 }
   115 
   116 /* This function (and associated calls) may be called more than once */
   117 int
   118 SDL_StartEventLoop(void)
   119 {
   120     /* We'll leave the event queue alone, since we might have gotten
   121        some important events at launch (like SDL_DROPFILE)
   122 
   123        FIXME: Does this introduce any other bugs with events at startup?
   124      */
   125 
   126     /* No filter to start with, process most event types */
   127     SDL_EventOK = NULL;
   128     SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
   129 
   130     /* If text input shows UI onscreen we want to start with it disabled */
   131     if (SDL_HasScreenKeyboardSupport()) {
   132         SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);
   133         SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);
   134     }
   135 
   136     /* Create the lock and set ourselves active */
   137 #if !SDL_THREADS_DISABLED
   138     if (!SDL_EventQ.lock) {
   139         SDL_EventQ.lock = SDL_CreateMutex();
   140     }
   141     if (SDL_EventQ.lock == NULL) {
   142         return (-1);
   143     }
   144 #endif /* !SDL_THREADS_DISABLED */
   145     SDL_EventQ.active = 1;
   146 
   147     return (0);
   148 }
   149 
   150 
   151 /* Add an event to the event queue -- called with the queue locked */
   152 static int
   153 SDL_AddEvent(SDL_Event * event)
   154 {
   155     int tail, added;
   156 
   157     tail = (SDL_EventQ.tail + 1) % MAXEVENTS;
   158     if (tail == SDL_EventQ.head) {
   159         /* Overflow, drop event */
   160         added = 0;
   161     } else {
   162         SDL_EventQ.event[SDL_EventQ.tail] = *event;
   163         if (event->type == SDL_SYSWMEVENT) {
   164             /* Note that it's possible to lose an event */
   165             int next = SDL_EventQ.wmmsg_next;
   166             SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   167             SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   168                 &SDL_EventQ.wmmsg[next];
   169             SDL_EventQ.wmmsg_next = (next + 1) % MAXEVENTS;
   170         }
   171         SDL_EventQ.tail = tail;
   172         added = 1;
   173     }
   174     return (added);
   175 }
   176 
   177 /* Cut an event, and return the next valid spot, or the tail */
   178 /*                           -- called with the queue locked */
   179 static int
   180 SDL_CutEvent(int spot)
   181 {
   182     if (spot == SDL_EventQ.head) {
   183         SDL_EventQ.head = (SDL_EventQ.head + 1) % MAXEVENTS;
   184         return (SDL_EventQ.head);
   185     } else if ((spot + 1) % MAXEVENTS == SDL_EventQ.tail) {
   186         SDL_EventQ.tail = spot;
   187         return (SDL_EventQ.tail);
   188     } else
   189         /* We cut the middle -- shift everything over */
   190     {
   191         int here, next;
   192 
   193         /* This can probably be optimized with SDL_memcpy() -- careful! */
   194         if (--SDL_EventQ.tail < 0) {
   195             SDL_EventQ.tail = MAXEVENTS - 1;
   196         }
   197         for (here = spot; here != SDL_EventQ.tail; here = next) {
   198             next = (here + 1) % MAXEVENTS;
   199             SDL_EventQ.event[here] = SDL_EventQ.event[next];
   200         }
   201         return (spot);
   202     }
   203     /* NOTREACHED */
   204 }
   205 
   206 /* Lock the event queue, take a peep at it, and unlock it */
   207 int
   208 SDL_PeepEvents(SDL_Event * events, int numevents, SDL_eventaction action,
   209                Uint32 minType, Uint32 maxType)
   210 {
   211     int i, used;
   212 
   213     /* Don't look after we've quit */
   214     if (!SDL_EventQ.active) {
   215         return (-1);
   216     }
   217     /* Lock the event queue */
   218     used = 0;
   219     if (!SDL_EventQ.lock || SDL_mutexP(SDL_EventQ.lock) == 0) {
   220         if (action == SDL_ADDEVENT) {
   221             for (i = 0; i < numevents; ++i) {
   222                 used += SDL_AddEvent(&events[i]);
   223             }
   224         } else {
   225             SDL_Event tmpevent;
   226             int spot;
   227 
   228             /* If 'events' is NULL, just see if they exist */
   229             if (events == NULL) {
   230                 action = SDL_PEEKEVENT;
   231                 numevents = 1;
   232                 events = &tmpevent;
   233             }
   234             spot = SDL_EventQ.head;
   235             while ((used < numevents) && (spot != SDL_EventQ.tail)) {
   236                 Uint32 type = SDL_EventQ.event[spot].type;
   237                 if (minType <= type && type <= maxType) {
   238                     events[used++] = SDL_EventQ.event[spot];
   239                     if (action == SDL_GETEVENT) {
   240                         spot = SDL_CutEvent(spot);
   241                     } else {
   242                         spot = (spot + 1) % MAXEVENTS;
   243                     }
   244                 } else {
   245                     spot = (spot + 1) % MAXEVENTS;
   246                 }
   247             }
   248         }
   249         SDL_mutexV(SDL_EventQ.lock);
   250     } else {
   251         SDL_SetError("Couldn't lock event queue");
   252         used = -1;
   253     }
   254     return (used);
   255 }
   256 
   257 SDL_bool
   258 SDL_HasEvent(Uint32 type)
   259 {
   260     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type) > 0);
   261 }
   262 
   263 SDL_bool
   264 SDL_HasEvents(Uint32 minType, Uint32 maxType)
   265 {
   266     return (SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, minType, maxType) > 0);
   267 }
   268 
   269 void
   270 SDL_FlushEvent(Uint32 type)
   271 {
   272     SDL_FlushEvents(type, type);
   273 }
   274 
   275 void
   276 SDL_FlushEvents(Uint32 minType, Uint32 maxType)
   277 {
   278     /* Don't look after we've quit */
   279     if (!SDL_EventQ.active) {
   280         return;
   281     }
   282 
   283     /* Make sure the events are current */
   284 #if 0
   285     /* Actually, we can't do this since we might be flushing while processing
   286        a resize event, and calling this might trigger further resize events.
   287     */
   288     SDL_PumpEvents();
   289 #endif
   290 
   291     /* Lock the event queue */
   292     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   293         int spot = SDL_EventQ.head;
   294         while (spot != SDL_EventQ.tail) {
   295             Uint32 type = SDL_EventQ.event[spot].type;
   296             if (minType <= type && type <= maxType) {
   297                 spot = SDL_CutEvent(spot);
   298             } else {
   299                 spot = (spot + 1) % MAXEVENTS;
   300             }
   301         }
   302         SDL_mutexV(SDL_EventQ.lock);
   303     }
   304 }
   305 
   306 /* Run the system dependent event loops */
   307 void
   308 SDL_PumpEvents(void)
   309 {
   310     SDL_VideoDevice *_this = SDL_GetVideoDevice();
   311 
   312     /* Get events from the video subsystem */
   313     if (_this) {
   314         _this->PumpEvents(_this);
   315     }
   316 #if !SDL_JOYSTICK_DISABLED
   317     /* Check for joystick state change */
   318     if (SDL_ShouldPollJoystick()) {
   319         SDL_JoystickUpdate();
   320     }
   321 #endif
   322 }
   323 
   324 /* Public functions */
   325 
   326 int
   327 SDL_PollEvent(SDL_Event * event)
   328 {
   329     return SDL_WaitEventTimeout(event, 0);
   330 }
   331 
   332 int
   333 SDL_WaitEvent(SDL_Event * event)
   334 {
   335     return SDL_WaitEventTimeout(event, -1);
   336 }
   337 
   338 int
   339 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
   340 {
   341     Uint32 expiration = 0;
   342 
   343     if (timeout > 0)
   344         expiration = SDL_GetTicks() + timeout;
   345 
   346     for (;;) {
   347         SDL_PumpEvents();
   348         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   349         case -1:
   350             return 0;
   351         case 1:
   352             return 1;
   353         case 0:
   354             if (timeout == 0) {
   355                 /* Polling and no events, just return */
   356                 return 0;
   357             }
   358             if (timeout > 0 && ((int) (SDL_GetTicks() - expiration) >= 0)) {
   359                 /* Timeout expired and no events */
   360                 return 0;
   361             }
   362             SDL_Delay(10);
   363             break;
   364         }
   365     }
   366 }
   367 
   368 int
   369 SDL_PushEvent(SDL_Event * event)
   370 {
   371     SDL_EventWatcher *curr;
   372     event->window.timestamp = SDL_GetTicks();
   373     if (SDL_EventOK && !SDL_EventOK(SDL_EventOKParam, event)) {
   374         return 0;
   375     }
   376 
   377     for (curr = SDL_event_watchers; curr; curr = curr->next) {
   378         curr->callback(curr->userdata, event);
   379     }
   380 
   381     if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
   382         return -1;
   383     }
   384 
   385     SDL_GestureProcessEvent(event);
   386 
   387     return 1;
   388 }
   389 
   390 void
   391 SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
   392 {
   393     SDL_Event bitbucket;
   394 
   395     /* Set filter and discard pending events */
   396     SDL_EventOK = filter;
   397     SDL_EventOKParam = userdata;
   398     while (SDL_PollEvent(&bitbucket) > 0);
   399 }
   400 
   401 SDL_bool
   402 SDL_GetEventFilter(SDL_EventFilter * filter, void **userdata)
   403 {
   404     if (filter) {
   405         *filter = SDL_EventOK;
   406     }
   407     if (userdata) {
   408         *userdata = SDL_EventOKParam;
   409     }
   410     return SDL_EventOK ? SDL_TRUE : SDL_FALSE;
   411 }
   412 
   413 /* FIXME: This is not thread-safe yet */
   414 void
   415 SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
   416 {
   417     SDL_EventWatcher *watcher;
   418 
   419     watcher = (SDL_EventWatcher *)SDL_malloc(sizeof(*watcher));
   420     if (!watcher) {
   421         /* Uh oh... */
   422         return;
   423     }
   424     watcher->callback = filter;
   425     watcher->userdata = userdata;
   426     watcher->next = SDL_event_watchers;
   427     SDL_event_watchers = watcher;
   428 }
   429 
   430 /* FIXME: This is not thread-safe yet */
   431 void
   432 SDL_DelEventWatch(SDL_EventFilter filter, void *userdata)
   433 {
   434     SDL_EventWatcher *prev = NULL;
   435     SDL_EventWatcher *curr;
   436 
   437     for (curr = SDL_event_watchers; curr; prev = curr, curr = curr->next) {
   438         if (curr->callback == filter && curr->userdata == userdata) {
   439             if (prev) {
   440                 prev->next = curr->next;
   441             } else {
   442                 SDL_event_watchers = curr->next;
   443             }
   444             SDL_free(curr);
   445             break;
   446         }
   447     }
   448 }
   449 
   450 void
   451 SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
   452 {
   453     if (SDL_mutexP(SDL_EventQ.lock) == 0) {
   454         int spot;
   455 
   456         spot = SDL_EventQ.head;
   457         while (spot != SDL_EventQ.tail) {
   458             if (filter(userdata, &SDL_EventQ.event[spot])) {
   459                 spot = (spot + 1) % MAXEVENTS;
   460             } else {
   461                 spot = SDL_CutEvent(spot);
   462             }
   463         }
   464     }
   465     SDL_mutexV(SDL_EventQ.lock);
   466 }
   467 
   468 Uint8
   469 SDL_EventState(Uint32 type, int state)
   470 {
   471     Uint8 current_state;
   472     Uint8 hi = ((type >> 8) & 0xff);
   473     Uint8 lo = (type & 0xff);
   474 
   475     if (SDL_disabled_events[hi] &&
   476         (SDL_disabled_events[hi]->bits[lo/32] & (1 << (lo&31)))) {
   477         current_state = SDL_DISABLE;
   478     } else {
   479         current_state = SDL_ENABLE;
   480     }
   481 
   482     if (state != current_state)
   483     {
   484         switch (state) {
   485         case SDL_DISABLE:
   486             /* Disable this event type and discard pending events */
   487             if (!SDL_disabled_events[hi]) {
   488                 SDL_disabled_events[hi] = (SDL_DisabledEventBlock*) SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
   489                 if (!SDL_disabled_events[hi]) {
   490                     /* Out of memory, nothing we can do... */
   491                     break;
   492                 }
   493             }
   494             SDL_disabled_events[hi]->bits[lo/32] |= (1 << (lo&31));
   495             SDL_FlushEvent(type);
   496             break;
   497         case SDL_ENABLE:
   498             SDL_disabled_events[hi]->bits[lo/32] &= ~(1 << (lo&31));
   499             break;
   500         default:
   501             /* Querying state... */
   502             break;
   503         }
   504     }
   505 
   506     return current_state;
   507 }
   508 
   509 Uint32
   510 SDL_RegisterEvents(int numevents)
   511 {
   512     Uint32 event_base;
   513 
   514     if (SDL_userevents+numevents <= SDL_LASTEVENT) {
   515         event_base = SDL_userevents;
   516         SDL_userevents += numevents;
   517     } else {
   518         event_base = (Uint32)-1;
   519     }
   520     return event_base;
   521 }
   522 
   523 /* This is a generic event handler.
   524  */
   525 int
   526 SDL_SendSysWMEvent(SDL_SysWMmsg * message)
   527 {
   528     int posted;
   529 
   530     posted = 0;
   531     if (SDL_GetEventState(SDL_SYSWMEVENT) == SDL_ENABLE) {
   532         SDL_Event event;
   533         SDL_memset(&event, 0, sizeof(event));
   534         event.type = SDL_SYSWMEVENT;
   535         event.syswm.msg = message;
   536         posted = (SDL_PushEvent(&event) > 0);
   537     }
   538     /* Update internal event state */
   539     return (posted);
   540 }
   541 
   542 /* vi: set ts=4 sw=4 expandtab: */