e14743d1 |
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 | } |