|
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 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 } |
|
195 #ifndef IPOD |
|
196 SDL_DestroyMutex(SDL_EventQ.lock); |
|
197 #endif |
|
198 } |
|
199 |
|
200 Uint32 SDL_EventThreadID(void) |
|
201 { |
|
202 return(event_thread); |
|
203 } |
|
204 |
|
205 /* Public functions */ |
|
206 |
|
207 void SDL_StopEventLoop(void) |
|
208 { |
|
209 /* Halt the event thread, if running */ |
|
210 SDL_StopEventThread(); |
|
211 |
|
212 /* Shutdown event handlers */ |
|
213 SDL_AppActiveQuit(); |
|
214 SDL_KeyboardQuit(); |
|
215 SDL_MouseQuit(); |
|
216 SDL_QuitQuit(); |
|
217 |
|
218 /* Clean out EventQ */ |
|
219 SDL_EventQ.head = 0; |
|
220 SDL_EventQ.tail = 0; |
|
221 SDL_EventQ.wmmsg_next = 0; |
|
222 } |
|
223 |
|
224 /* This function (and associated calls) may be called more than once */ |
|
225 int SDL_StartEventLoop(Uint32 flags) |
|
226 { |
|
227 int retcode; |
|
228 |
|
229 /* Clean out the event queue */ |
|
230 SDL_EventThread = NULL; |
|
231 SDL_EventQ.lock = NULL; |
|
232 SDL_StopEventLoop(); |
|
233 |
|
234 /* No filter to start with, process most event types */ |
|
235 SDL_EventOK = NULL; |
|
236 SDL_memset(SDL_ProcessEvents,SDL_ENABLE,sizeof(SDL_ProcessEvents)); |
|
237 SDL_eventstate = ~0; |
|
238 /* It's not save to call SDL_EventState() yet */ |
|
239 SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT); |
|
240 SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE; |
|
241 |
|
242 /* Initialize event handlers */ |
|
243 retcode = 0; |
|
244 retcode += SDL_AppActiveInit(); |
|
245 retcode += SDL_KeyboardInit(); |
|
246 retcode += SDL_MouseInit(); |
|
247 retcode += SDL_QuitInit(); |
|
248 if ( retcode < 0 ) { |
|
249 /* We don't expect them to fail, but... */ |
|
250 return(-1); |
|
251 } |
|
252 |
|
253 /* Create the lock and event thread */ |
|
254 if ( SDL_StartEventThread(flags) < 0 ) { |
|
255 SDL_StopEventLoop(); |
|
256 return(-1); |
|
257 } |
|
258 return(0); |
|
259 } |
|
260 |
|
261 |
|
262 /* Add an event to the event queue -- called with the queue locked */ |
|
263 static int SDL_AddEvent(SDL_Event *event) |
|
264 { |
|
265 int tail, added; |
|
266 |
|
267 tail = (SDL_EventQ.tail+1)%MAXEVENTS; |
|
268 if ( tail == SDL_EventQ.head ) { |
|
269 /* Overflow, drop event */ |
|
270 added = 0; |
|
271 } else { |
|
272 SDL_EventQ.event[SDL_EventQ.tail] = *event; |
|
273 if (event->type == SDL_SYSWMEVENT) { |
|
274 /* Note that it's possible to lose an event */ |
|
275 int next = SDL_EventQ.wmmsg_next; |
|
276 SDL_EventQ.wmmsg[next] = *event->syswm.msg; |
|
277 SDL_EventQ.event[SDL_EventQ.tail].syswm.msg = |
|
278 &SDL_EventQ.wmmsg[next]; |
|
279 SDL_EventQ.wmmsg_next = (next+1)%MAXEVENTS; |
|
280 } |
|
281 SDL_EventQ.tail = tail; |
|
282 added = 1; |
|
283 } |
|
284 return(added); |
|
285 } |
|
286 |
|
287 /* Cut an event, and return the next valid spot, or the tail */ |
|
288 /* -- called with the queue locked */ |
|
289 static int SDL_CutEvent(int spot) |
|
290 { |
|
291 if ( spot == SDL_EventQ.head ) { |
|
292 SDL_EventQ.head = (SDL_EventQ.head+1)%MAXEVENTS; |
|
293 return(SDL_EventQ.head); |
|
294 } else |
|
295 if ( (spot+1)%MAXEVENTS == SDL_EventQ.tail ) { |
|
296 SDL_EventQ.tail = spot; |
|
297 return(SDL_EventQ.tail); |
|
298 } else |
|
299 /* We cut the middle -- shift everything over */ |
|
300 { |
|
301 int here, next; |
|
302 |
|
303 /* This can probably be optimized with SDL_memcpy() -- careful! */ |
|
304 if ( --SDL_EventQ.tail < 0 ) { |
|
305 SDL_EventQ.tail = MAXEVENTS-1; |
|
306 } |
|
307 for ( here=spot; here != SDL_EventQ.tail; here = next ) { |
|
308 next = (here+1)%MAXEVENTS; |
|
309 SDL_EventQ.event[here] = SDL_EventQ.event[next]; |
|
310 } |
|
311 return(spot); |
|
312 } |
|
313 /* NOTREACHED */ |
|
314 } |
|
315 |
|
316 /* Lock the event queue, take a peep at it, and unlock it */ |
|
317 int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action, |
|
318 Uint32 mask) |
|
319 { |
|
320 int i, used; |
|
321 |
|
322 /* Don't look after we've quit */ |
|
323 if ( ! SDL_EventQ.active ) { |
|
324 return(-1); |
|
325 } |
|
326 /* Lock the event queue */ |
|
327 used = 0; |
|
328 if ( SDL_mutexP(SDL_EventQ.lock) == 0 ) { |
|
329 if ( action == SDL_ADDEVENT ) { |
|
330 for ( i=0; i<numevents; ++i ) { |
|
331 used += SDL_AddEvent(&events[i]); |
|
332 } |
|
333 } else { |
|
334 SDL_Event tmpevent; |
|
335 int spot; |
|
336 |
|
337 /* If 'events' is NULL, just see if they exist */ |
|
338 if ( events == NULL ) { |
|
339 action = SDL_PEEKEVENT; |
|
340 numevents = 1; |
|
341 events = &tmpevent; |
|
342 } |
|
343 spot = SDL_EventQ.head; |
|
344 while ((used < numevents)&&(spot != SDL_EventQ.tail)) { |
|
345 if ( mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type) ) { |
|
346 events[used++] = SDL_EventQ.event[spot]; |
|
347 if ( action == SDL_GETEVENT ) { |
|
348 spot = SDL_CutEvent(spot); |
|
349 } else { |
|
350 spot = (spot+1)%MAXEVENTS; |
|
351 } |
|
352 } else { |
|
353 spot = (spot+1)%MAXEVENTS; |
|
354 } |
|
355 } |
|
356 } |
|
357 SDL_mutexV(SDL_EventQ.lock); |
|
358 } else { |
|
359 SDL_SetError("Couldn't lock event queue"); |
|
360 used = -1; |
|
361 } |
|
362 return(used); |
|
363 } |
|
364 |
|
365 /* Run the system dependent event loops */ |
|
366 void SDL_PumpEvents(void) |
|
367 { |
|
368 if ( !SDL_EventThread ) { |
|
369 SDL_VideoDevice *video = current_video; |
|
370 SDL_VideoDevice *this = current_video; |
|
371 |
|
372 /* Get events from the video subsystem */ |
|
373 if ( video ) { |
|
374 video->PumpEvents(this); |
|
375 } |
|
376 |
|
377 /* Queue pending key-repeat events */ |
|
378 SDL_CheckKeyRepeat(); |
|
379 |
|
380 #if !SDL_JOYSTICK_DISABLED |
|
381 /* Check for joystick state change */ |
|
382 if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) { |
|
383 SDL_JoystickUpdate(); |
|
384 } |
|
385 #endif |
|
386 } |
|
387 } |
|
388 |
|
389 /* Public functions */ |
|
390 |
|
391 int SDL_PollEvent (SDL_Event *event) |
|
392 { |
|
393 SDL_PumpEvents(); |
|
394 |
|
395 /* We can't return -1, just return 0 (no event) on error */ |
|
396 if ( SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0 ) |
|
397 return 0; |
|
398 return 1; |
|
399 } |
|
400 |
|
401 int SDL_WaitEvent (SDL_Event *event) |
|
402 { |
|
403 while ( 1 ) { |
|
404 SDL_PumpEvents(); |
|
405 switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) { |
|
406 case -1: return 0; |
|
407 case 1: return 1; |
|
408 case 0: SDL_Delay(10); |
|
409 } |
|
410 } |
|
411 } |
|
412 |
|
413 int SDL_PushEvent(SDL_Event *event) |
|
414 { |
|
415 if ( SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0 ) |
|
416 return -1; |
|
417 return 0; |
|
418 } |
|
419 |
|
420 void SDL_SetEventFilter (SDL_EventFilter filter) |
|
421 { |
|
422 SDL_Event bitbucket; |
|
423 |
|
424 /* Set filter and discard pending events */ |
|
425 SDL_EventOK = filter; |
|
426 while ( SDL_PollEvent(&bitbucket) > 0 ) |
|
427 ; |
|
428 } |
|
429 |
|
430 SDL_EventFilter SDL_GetEventFilter(void) |
|
431 { |
|
432 return(SDL_EventOK); |
|
433 } |
|
434 |
|
435 Uint8 SDL_EventState (Uint8 type, int state) |
|
436 { |
|
437 SDL_Event bitbucket; |
|
438 Uint8 current_state; |
|
439 |
|
440 /* If SDL_ALLEVENTS was specified... */ |
|
441 if ( type == 0xFF ) { |
|
442 current_state = SDL_IGNORE; |
|
443 for ( type=0; type<SDL_NUMEVENTS; ++type ) { |
|
444 if ( SDL_ProcessEvents[type] != SDL_IGNORE ) { |
|
445 current_state = SDL_ENABLE; |
|
446 } |
|
447 SDL_ProcessEvents[type] = state; |
|
448 if ( state == SDL_ENABLE ) { |
|
449 SDL_eventstate |= (0x00000001 << (type)); |
|
450 } else { |
|
451 SDL_eventstate &= ~(0x00000001 << (type)); |
|
452 } |
|
453 } |
|
454 while ( SDL_PollEvent(&bitbucket) > 0 ) |
|
455 ; |
|
456 return(current_state); |
|
457 } |
|
458 |
|
459 /* Just set the state for one event type */ |
|
460 current_state = SDL_ProcessEvents[type]; |
|
461 switch (state) { |
|
462 case SDL_IGNORE: |
|
463 case SDL_ENABLE: |
|
464 /* Set state and discard pending events */ |
|
465 SDL_ProcessEvents[type] = state; |
|
466 if ( state == SDL_ENABLE ) { |
|
467 SDL_eventstate |= (0x00000001 << (type)); |
|
468 } else { |
|
469 SDL_eventstate &= ~(0x00000001 << (type)); |
|
470 } |
|
471 while ( SDL_PollEvent(&bitbucket) > 0 ) |
|
472 ; |
|
473 break; |
|
474 default: |
|
475 /* Querying state? */ |
|
476 break; |
|
477 } |
|
478 return(current_state); |
|
479 } |
|
480 |
|
481 /* This is a generic event handler. |
|
482 */ |
|
483 int SDL_PrivateSysWMEvent(SDL_SysWMmsg *message) |
|
484 { |
|
485 int posted; |
|
486 |
|
487 posted = 0; |
|
488 if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) { |
|
489 SDL_Event event; |
|
490 SDL_memset(&event, 0, sizeof(event)); |
|
491 event.type = SDL_SYSWMEVENT; |
|
492 event.syswm.msg = message; |
|
493 if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) { |
|
494 posted = 1; |
|
495 SDL_PushEvent(&event); |
|
496 } |
|
497 } |
|
498 /* Update internal event state */ |
|
499 return(posted); |
|
500 } |