SDL-1.2.14
[sdl_omap.git] / src / events / SDL_events.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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         SDL_mutex *lock;
44         int active;
45         int head;
46         int tail;
47         SDL_Event event[MAXEVENTS];
48         int wmmsg_next;
49         struct SDL_SysWMmsg wmmsg[MAXEVENTS];
50 } SDL_EventQ;
51
52 /* Private data -- event locking structure */
53 static struct {
54         SDL_mutex *lock;
55         int safe;
56 } SDL_EventLock;
57
58 /* Thread functions */
59 static SDL_Thread *SDL_EventThread = NULL;      /* Thread handle */
60 static Uint32 event_thread;                     /* The event thread id */
61
62 void SDL_Lock_EventThread(void)
63 {
64         if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
65                 /* Grab lock and spin until we're sure event thread stopped */
66                 SDL_mutexP(SDL_EventLock.lock);
67                 while ( ! SDL_EventLock.safe ) {
68                         SDL_Delay(1);
69                 }
70         }
71 }
72 void SDL_Unlock_EventThread(void)
73 {
74         if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
75                 SDL_mutexV(SDL_EventLock.lock);
76         }
77 }
78
79 #ifdef __OS2__
80 /*
81  * We'll increase the priority of GobbleEvents thread, so it will process
82  *  events in time for sure! For this, we need the DosSetPriority() API
83  *  from the os2.h include file.
84  */
85 #define INCL_DOSPROCESS
86 #include <os2.h>
87 #include <time.h>
88 #endif
89
90 static int SDLCALL SDL_GobbleEvents(void *unused)
91 {
92         event_thread = SDL_ThreadID();
93
94 #ifdef __OS2__
95 #ifdef USE_DOSSETPRIORITY
96         /* Increase thread priority, so it will process events in time for sure! */
97         DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, +16, 0);
98 #endif
99 #endif
100
101         while ( SDL_EventQ.active ) {
102                 SDL_VideoDevice *video = current_video;
103                 SDL_VideoDevice *this  = current_video;
104
105                 /* Get events from the video subsystem */
106                 if ( video ) {
107                         video->PumpEvents(this);
108                 }
109
110                 /* Queue pending key-repeat events */
111                 SDL_CheckKeyRepeat();
112
113 #if !SDL_JOYSTICK_DISABLED
114                 /* Check for joystick state change */
115                 if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) {
116                         SDL_JoystickUpdate();
117                 }
118 #endif
119
120                 /* Give up the CPU for the rest of our timeslice */
121                 SDL_EventLock.safe = 1;
122                 if ( SDL_timer_running ) {
123                         SDL_ThreadedTimerCheck();
124                 }
125                 SDL_Delay(1);
126
127                 /* Check for event locking.
128                    On the P of the lock mutex, if the lock is held, this thread
129                    will wait until the lock is released before continuing.  The
130                    safe flag will be set, meaning that the other thread can go
131                    about it's business.  The safe flag is reset before the V,
132                    so as soon as the mutex is free, other threads can see that
133                    it's not safe to interfere with the event thread.
134                  */
135                 SDL_mutexP(SDL_EventLock.lock);
136                 SDL_EventLock.safe = 0;
137                 SDL_mutexV(SDL_EventLock.lock);
138         }
139         SDL_SetTimerThreaded(0);
140         event_thread = 0;
141         return(0);
142 }
143
144 static int SDL_StartEventThread(Uint32 flags)
145 {
146         /* Reset everything to zero */
147         SDL_EventThread = NULL;
148         SDL_memset(&SDL_EventLock, 0, sizeof(SDL_EventLock));
149
150         /* Create the lock and set ourselves active */
151 #if !SDL_THREADS_DISABLED
152         SDL_EventQ.lock = SDL_CreateMutex();
153         if ( SDL_EventQ.lock == NULL ) {
154 #ifdef __MACOS__ /* MacOS classic you can't multithread, so no lock needed */
155                 ;
156 #else
157                 return(-1);
158 #endif
159         }
160 #endif /* !SDL_THREADS_DISABLED */
161         SDL_EventQ.active = 1;
162
163         if ( (flags&SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD ) {
164                 SDL_EventLock.lock = SDL_CreateMutex();
165                 if ( SDL_EventLock.lock == NULL ) {
166                         return(-1);
167                 }
168                 SDL_EventLock.safe = 0;
169
170                 /* The event thread will handle timers too */
171                 SDL_SetTimerThreaded(2);
172 #if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) && !defined(__SYMBIAN32__)
173 #undef SDL_CreateThread
174                 SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL, NULL, NULL);
175 #else
176                 SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
177 #endif
178                 if ( SDL_EventThread == NULL ) {
179                         return(-1);
180                 }
181         } else {
182                 event_thread = 0;
183         }
184         return(0);
185 }
186
187 static void 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                 SDL_EventLock.lock = NULL;
195         }
196 #ifndef IPOD
197         SDL_DestroyMutex(SDL_EventQ.lock);
198         SDL_EventQ.lock = NULL;
199 #endif
200 }
201
202 Uint32 SDL_EventThreadID(void)
203 {
204         return(event_thread);
205 }
206
207 /* Public functions */
208
209 void SDL_StopEventLoop(void)
210 {
211         /* Halt the event thread, if running */
212         SDL_StopEventThread();
213
214         /* Shutdown event handlers */
215         SDL_AppActiveQuit();
216         SDL_KeyboardQuit();
217         SDL_MouseQuit();
218         SDL_QuitQuit();
219
220         /* Clean out EventQ */
221         SDL_EventQ.head = 0;
222         SDL_EventQ.tail = 0;
223         SDL_EventQ.wmmsg_next = 0;
224 }
225
226 /* This function (and associated calls) may be called more than once */
227 int 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_AppActiveInit();
247         retcode += SDL_KeyboardInit();
248         retcode += SDL_MouseInit();
249         retcode += SDL_QuitInit();
250         if ( retcode < 0 ) {
251                 /* We don't expect them to fail, but... */
252                 return(-1);
253         }
254
255         /* Create the lock and event thread */
256         if ( SDL_StartEventThread(flags) < 0 ) {
257                 SDL_StopEventLoop();
258                 return(-1);
259         }
260         return(0);
261 }
262
263
264 /* Add an event to the event queue -- called with the queue locked */
265 static int 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 SDL_CutEvent(int spot)
292 {
293         if ( spot == SDL_EventQ.head ) {
294                 SDL_EventQ.head = (SDL_EventQ.head+1)%MAXEVENTS;
295                 return(SDL_EventQ.head);
296         } else
297         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 SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action,
320                                                                 Uint32 mask)
321 {
322         int i, used;
323
324         /* Don't look after we've quit */
325         if ( ! SDL_EventQ.active ) {
326                 return(-1);
327         }
328         /* Lock the event queue */
329         used = 0;
330         if ( SDL_mutexP(SDL_EventQ.lock) == 0 ) {
331                 if ( action == SDL_ADDEVENT ) {
332                         for ( i=0; i<numevents; ++i ) {
333                                 used += SDL_AddEvent(&events[i]);
334                         }
335                 } else {
336                         SDL_Event tmpevent;
337                         int spot;
338
339                         /* If 'events' is NULL, just see if they exist */
340                         if ( events == NULL ) {
341                                 action = SDL_PEEKEVENT;
342                                 numevents = 1;
343                                 events = &tmpevent;
344                         }
345                         spot = SDL_EventQ.head;
346                         while ((used < numevents)&&(spot != SDL_EventQ.tail)) {
347                                 if ( mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type) ) {
348                                         events[used++] = SDL_EventQ.event[spot];
349                                         if ( action == SDL_GETEVENT ) {
350                                                 spot = SDL_CutEvent(spot);
351                                         } else {
352                                                 spot = (spot+1)%MAXEVENTS;
353                                         }
354                                 } else {
355                                         spot = (spot+1)%MAXEVENTS;
356                                 }
357                         }
358                 }
359                 SDL_mutexV(SDL_EventQ.lock);
360         } else {
361                 SDL_SetError("Couldn't lock event queue");
362                 used = -1;
363         }
364         return(used);
365 }
366
367 /* Run the system dependent event loops */
368 void SDL_PumpEvents(void)
369 {
370         if ( !SDL_EventThread ) {
371                 SDL_VideoDevice *video = current_video;
372                 SDL_VideoDevice *this  = current_video;
373
374                 /* Get events from the video subsystem */
375                 if ( video ) {
376                         video->PumpEvents(this);
377                 }
378
379                 /* Queue pending key-repeat events */
380                 SDL_CheckKeyRepeat();
381
382 #if !SDL_JOYSTICK_DISABLED
383                 /* Check for joystick state change */
384                 if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) {
385                         SDL_JoystickUpdate();
386                 }
387 #endif
388         }
389 }
390
391 /* Public functions */
392
393 int SDL_PollEvent (SDL_Event *event)
394 {
395         SDL_PumpEvents();
396
397         /* We can't return -1, just return 0 (no event) on error */
398         if ( SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0 )
399                 return 0;
400         return 1;
401 }
402
403 int SDL_WaitEvent (SDL_Event *event)
404 {
405         while ( 1 ) {
406                 SDL_PumpEvents();
407                 switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
408                     case -1: return 0;
409                     case 1: return 1;
410                     case 0: SDL_Delay(10);
411                 }
412         }
413 }
414
415 int SDL_PushEvent(SDL_Event *event)
416 {
417         if ( SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0 )
418                 return -1;
419         return 0;
420 }
421
422 void SDL_SetEventFilter (SDL_EventFilter filter)
423 {
424         SDL_Event bitbucket;
425
426         /* Set filter and discard pending events */
427         SDL_EventOK = filter;
428         while ( SDL_PollEvent(&bitbucket) > 0 )
429                 ;
430 }
431
432 SDL_EventFilter SDL_GetEventFilter(void)
433 {
434         return(SDL_EventOK);
435 }
436
437 Uint8 SDL_EventState (Uint8 type, int state)
438 {
439         SDL_Event bitbucket;
440         Uint8 current_state;
441
442         /* If SDL_ALLEVENTS was specified... */
443         if ( type == 0xFF ) {
444                 current_state = SDL_IGNORE;
445                 for ( type=0; type<SDL_NUMEVENTS; ++type ) {
446                         if ( SDL_ProcessEvents[type] != SDL_IGNORE ) {
447                                 current_state = SDL_ENABLE;
448                         }
449                         SDL_ProcessEvents[type] = state;
450                         if ( state == SDL_ENABLE ) {
451                                 SDL_eventstate |= (0x00000001 << (type));
452                         } else {
453                                 SDL_eventstate &= ~(0x00000001 << (type));
454                         }
455                 }
456                 while ( SDL_PollEvent(&bitbucket) > 0 )
457                         ;
458                 return(current_state);
459         }
460
461         /* Just set the state for one event type */
462         current_state = SDL_ProcessEvents[type];
463         switch (state) {
464                 case SDL_IGNORE:
465                 case SDL_ENABLE:
466                         /* Set state and discard pending events */
467                         SDL_ProcessEvents[type] = state;
468                         if ( state == SDL_ENABLE ) {
469                                 SDL_eventstate |= (0x00000001 << (type));
470                         } else {
471                                 SDL_eventstate &= ~(0x00000001 << (type));
472                         }
473                         while ( SDL_PollEvent(&bitbucket) > 0 )
474                                 ;
475                         break;
476                 default:
477                         /* Querying state? */
478                         break;
479         }
480         return(current_state);
481 }
482
483 /* This is a generic event handler.
484  */
485 int SDL_PrivateSysWMEvent(SDL_SysWMmsg *message)
486 {
487         int posted;
488
489         posted = 0;
490         if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
491                 SDL_Event event;
492                 SDL_memset(&event, 0, sizeof(event));
493                 event.type = SDL_SYSWMEVENT;
494                 event.syswm.msg = message;
495                 if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
496                         posted = 1;
497                         SDL_PushEvent(&event);
498                 }
499         }
500         /* Update internal event state */
501         return(posted);
502 }