Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5131 | clevermous | 1 | /* |
2 | SDL - Simple DirectMedia Layer |
||
3 | Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga |
||
4 | |||
5 | This library is free software; you can redistribute it and/or |
||
6 | modify it under the terms of the GNU Library General Public |
||
7 | License as published by the Free Software Foundation; either |
||
8 | version 2 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 | Library General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU Library General Public |
||
16 | License along with this library; if not, write to the Free |
||
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
18 | |||
19 | Sam Lantinga |
||
20 | slouken@devolution.com |
||
21 | */ |
||
22 | |||
23 | |||
24 | /* General event handling code for SDL */ |
||
25 | |||
26 | #include |
||
27 | #include |
||
28 | |||
29 | #include "SDL.h" |
||
30 | #include "SDL_thread.h" |
||
31 | #include "SDL_mutex.h" |
||
32 | #include "SDL_events.h" |
||
33 | #include "SDL_events_c.h" |
||
34 | #include "SDL_timer_c.h" |
||
35 | #ifndef DISABLE_JOYSTICK |
||
36 | #include "SDL_joystick_c.h" |
||
37 | #endif |
||
38 | #ifndef ENABLE_X11 |
||
39 | #define DISABLE_X11 |
||
40 | #endif |
||
41 | #include "SDL_syswm.h" |
||
42 | #include "SDL_sysevents.h" |
||
43 | |||
44 | /* Public data -- the event filter */ |
||
45 | SDL_EventFilter SDL_EventOK = NULL; |
||
46 | Uint8 SDL_ProcessEvents[SDL_NUMEVENTS]; |
||
47 | static Uint32 SDL_eventstate = 0; |
||
48 | |||
49 | /* Private data -- event queue */ |
||
50 | #define MAXEVENTS 128 |
||
51 | static struct { |
||
52 | SDL_mutex *lock; |
||
53 | int active; |
||
54 | int head; |
||
55 | int tail; |
||
56 | SDL_Event event[MAXEVENTS]; |
||
57 | int wmmsg_next; |
||
58 | struct SDL_SysWMmsg wmmsg[MAXEVENTS]; |
||
59 | } SDL_EventQ; |
||
60 | |||
61 | /* Private data -- event locking structure */ |
||
62 | static struct { |
||
63 | SDL_mutex *lock; |
||
64 | int safe; |
||
65 | } SDL_EventLock; |
||
66 | |||
67 | /* Thread functions */ |
||
68 | static SDL_Thread *SDL_EventThread = NULL; /* Thread handle */ |
||
69 | static Uint32 event_thread; /* The event thread id */ |
||
70 | |||
71 | void SDL_Lock_EventThread(void) |
||
72 | { |
||
73 | if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) { |
||
74 | /* Grab lock and spin until we're sure event thread stopped */ |
||
75 | SDL_mutexP(SDL_EventLock.lock); |
||
76 | while ( ! SDL_EventLock.safe ) { |
||
77 | SDL_Delay(1); |
||
78 | } |
||
79 | } |
||
80 | } |
||
81 | void SDL_Unlock_EventThread(void) |
||
82 | { |
||
83 | if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) { |
||
84 | SDL_mutexV(SDL_EventLock.lock); |
||
85 | } |
||
86 | } |
||
87 | |||
88 | static int SDL_GobbleEvents(void *unused) |
||
89 | { |
||
90 | SDL_SetTimerThreaded(2); |
||
91 | event_thread = SDL_ThreadID(); |
||
92 | while ( SDL_EventQ.active ) { |
||
93 | SDL_VideoDevice *video = current_video; |
||
94 | SDL_VideoDevice *this = current_video; |
||
95 | |||
96 | /* Get events from the video subsystem */ |
||
97 | if ( video ) { |
||
98 | video->PumpEvents(this); |
||
99 | } |
||
100 | |||
101 | /* Queue pending key-repeat events */ |
||
102 | SDL_CheckKeyRepeat(); |
||
103 | |||
104 | #ifndef DISABLE_JOYSTICK |
||
105 | /* Check for joystick state change */ |
||
106 | if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) { |
||
107 | SDL_JoystickUpdate(); |
||
108 | } |
||
109 | #endif |
||
110 | |||
111 | /* Give up the CPU for the rest of our timeslice */ |
||
112 | SDL_EventLock.safe = 1; |
||
113 | if( SDL_timer_running ) { |
||
114 | SDL_ThreadedTimerCheck(); |
||
115 | } |
||
116 | SDL_Delay(1); |
||
117 | |||
118 | /* Check for event locking. |
||
119 | On the P of the lock mutex, if the lock is held, this thread |
||
120 | will wait until the lock is released before continuing. The |
||
121 | safe flag will be set, meaning that the other thread can go |
||
122 | about it's business. The safe flag is reset before the V, |
||
123 | so as soon as the mutex is free, other threads can see that |
||
124 | it's not safe to interfere with the event thread. |
||
125 | */ |
||
126 | SDL_mutexP(SDL_EventLock.lock); |
||
127 | SDL_EventLock.safe = 0; |
||
128 | SDL_mutexV(SDL_EventLock.lock); |
||
129 | } |
||
130 | SDL_SetTimerThreaded(0); |
||
131 | event_thread = 0; |
||
132 | return(0); |
||
133 | } |
||
134 | |||
135 | static int SDL_StartEventThread(Uint32 flags) |
||
136 | { |
||
137 | /* Reset everything to zero */ |
||
138 | SDL_EventThread = NULL; |
||
139 | memset(&SDL_EventLock, 0, sizeof(SDL_EventLock)); |
||
140 | |||
141 | /* Create the lock and set ourselves active */ |
||
142 | #ifndef DISABLE_THREADS |
||
143 | SDL_EventQ.lock = SDL_CreateMutex(); |
||
144 | if ( SDL_EventQ.lock == NULL ) { |
||
145 | #ifdef macintosh /* On MacOS 7/8, you can't multithread, so no lock needed */ |
||
146 | ; |
||
147 | #else |
||
148 | return(-1); |
||
149 | #endif |
||
150 | } |
||
151 | #endif /* !DISABLE_THREADS */ |
||
152 | SDL_EventQ.active = 1; |
||
153 | |||
154 | if ( (flags&SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD ) { |
||
155 | SDL_EventLock.lock = SDL_CreateMutex(); |
||
156 | if ( SDL_EventLock.lock == NULL ) { |
||
157 | return(-1); |
||
158 | } |
||
159 | SDL_EventLock.safe = 0; |
||
160 | |||
161 | SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL); |
||
162 | if ( SDL_EventThread == NULL ) { |
||
163 | return(-1); |
||
164 | } |
||
165 | } else { |
||
166 | event_thread = 0; |
||
167 | } |
||
168 | return(0); |
||
169 | } |
||
170 | |||
171 | static void SDL_StopEventThread(void) |
||
172 | { |
||
173 | SDL_EventQ.active = 0; |
||
174 | if ( SDL_EventThread ) { |
||
175 | SDL_WaitThread(SDL_EventThread, NULL); |
||
176 | SDL_EventThread = NULL; |
||
177 | SDL_DestroyMutex(SDL_EventLock.lock); |
||
178 | } |
||
179 | SDL_DestroyMutex(SDL_EventQ.lock); |
||
180 | } |
||
181 | |||
182 | Uint32 SDL_EventThreadID(void) |
||
183 | { |
||
184 | return(event_thread); |
||
185 | } |
||
186 | |||
187 | /* Public functions */ |
||
188 | |||
189 | void SDL_StopEventLoop(void) |
||
190 | { |
||
191 | /* Halt the event thread, if running */ |
||
192 | SDL_StopEventThread(); |
||
193 | |||
194 | /* Clean out EventQ */ |
||
195 | SDL_EventQ.head = 0; |
||
196 | SDL_EventQ.tail = 0; |
||
197 | SDL_EventQ.wmmsg_next = 0; |
||
198 | } |
||
199 | |||
200 | /* This function (and associated calls) may be called more than once */ |
||
201 | int SDL_StartEventLoop(Uint32 flags) |
||
202 | { |
||
203 | int retcode; |
||
204 | |||
205 | /* Clean out the event queue */ |
||
206 | SDL_EventThread = NULL; |
||
207 | SDL_EventQ.lock = NULL; |
||
208 | SDL_StopEventLoop(); |
||
209 | |||
210 | /* No filter to start with, process most event types */ |
||
211 | SDL_EventOK = NULL; |
||
212 | memset(SDL_ProcessEvents,SDL_ENABLE,sizeof(SDL_ProcessEvents)); |
||
213 | SDL_eventstate = ~0; |
||
214 | /* It's not save to call SDL_EventState() yet */ |
||
215 | SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT); |
||
216 | SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE; |
||
217 | |||
218 | /* Initialize event handlers */ |
||
219 | retcode = 0; |
||
220 | retcode += SDL_AppActiveInit(); |
||
221 | retcode += SDL_KeyboardInit(); |
||
222 | retcode += SDL_MouseInit(); |
||
223 | retcode += SDL_QuitInit(); |
||
224 | if ( retcode < 0 ) { |
||
225 | /* We don't expect them to fail, but... */ |
||
226 | return(-1); |
||
227 | } |
||
228 | |||
229 | /* Create the lock and event thread */ |
||
230 | if ( SDL_StartEventThread(flags) < 0 ) { |
||
231 | SDL_StopEventLoop(); |
||
232 | return(-1); |
||
233 | } |
||
234 | return(0); |
||
235 | } |
||
236 | |||
237 | |||
238 | /* Add an event to the event queue -- called with the queue locked */ |
||
239 | static int SDL_AddEvent(SDL_Event *event) |
||
240 | { |
||
241 | int tail, added; |
||
242 | |||
243 | tail = (SDL_EventQ.tail+1)%MAXEVENTS; |
||
244 | if ( tail == SDL_EventQ.head ) { |
||
245 | /* Overflow, drop event */ |
||
246 | added = 0; |
||
247 | } else { |
||
248 | SDL_EventQ.event[SDL_EventQ.tail] = *event; |
||
249 | if (event->type == SDL_SYSWMEVENT) { |
||
250 | /* Note that it's possible to lose an event */ |
||
251 | int next = SDL_EventQ.wmmsg_next; |
||
252 | SDL_EventQ.wmmsg[next] = *event->syswm.msg; |
||
253 | SDL_EventQ.event[SDL_EventQ.tail].syswm.msg = |
||
254 | &SDL_EventQ.wmmsg[next]; |
||
255 | SDL_EventQ.wmmsg_next = (next+1)%MAXEVENTS; |
||
256 | } |
||
257 | SDL_EventQ.tail = tail; |
||
258 | added = 1; |
||
259 | } |
||
260 | return(added); |
||
261 | } |
||
262 | |||
263 | /* Cut an event, and return the next valid spot, or the tail */ |
||
264 | /* -- called with the queue locked */ |
||
265 | static int SDL_CutEvent(int spot) |
||
266 | { |
||
267 | if ( spot == SDL_EventQ.head ) { |
||
268 | SDL_EventQ.head = (SDL_EventQ.head+1)%MAXEVENTS; |
||
269 | return(SDL_EventQ.head); |
||
270 | } else |
||
271 | if ( (spot+1)%MAXEVENTS == SDL_EventQ.tail ) { |
||
272 | SDL_EventQ.tail = spot; |
||
273 | return(SDL_EventQ.tail); |
||
274 | } else |
||
275 | /* We cut the middle -- shift everything over */ |
||
276 | { |
||
277 | int here, next; |
||
278 | |||
279 | /* This can probably be optimized with memcpy() -- careful! */ |
||
280 | if ( --SDL_EventQ.tail < 0 ) { |
||
281 | SDL_EventQ.tail = MAXEVENTS-1; |
||
282 | } |
||
283 | for ( here=spot; here != SDL_EventQ.tail; here = next ) { |
||
284 | next = (here+1)%MAXEVENTS; |
||
285 | SDL_EventQ.event[here] = SDL_EventQ.event[next]; |
||
286 | } |
||
287 | return(spot); |
||
288 | } |
||
289 | /* NOTREACHED */ |
||
290 | } |
||
291 | |||
292 | /* Lock the event queue, take a peep at it, and unlock it */ |
||
293 | int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action, |
||
294 | Uint32 mask) |
||
295 | { |
||
296 | int i, used; |
||
297 | |||
298 | /* Don't look after we've quit */ |
||
299 | if ( ! SDL_EventQ.active ) { |
||
300 | return(0); |
||
301 | } |
||
302 | /* Lock the event queue */ |
||
303 | used = 0; |
||
304 | if ( SDL_mutexP(SDL_EventQ.lock) == 0 ) { |
||
305 | if ( action == SDL_ADDEVENT ) { |
||
306 | for ( i=0; i |
||
307 | used += SDL_AddEvent(&events[i]); |
||
308 | } |
||
309 | } else { |
||
310 | SDL_Event tmpevent; |
||
311 | int spot; |
||
312 | |||
313 | /* If 'events' is NULL, just see if they exist */ |
||
314 | if ( events == NULL ) { |
||
315 | action = SDL_PEEKEVENT; |
||
316 | numevents = 1; |
||
317 | events = &tmpevent; |
||
318 | } |
||
319 | spot = SDL_EventQ.head; |
||
320 | while ((used < numevents)&&(spot != SDL_EventQ.tail)) { |
||
321 | if ( mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type) ) { |
||
322 | events[used++] = SDL_EventQ.event[spot]; |
||
323 | if ( action == SDL_GETEVENT ) { |
||
324 | spot = SDL_CutEvent(spot); |
||
325 | } else { |
||
326 | spot = (spot+1)%MAXEVENTS; |
||
327 | } |
||
328 | } else { |
||
329 | spot = (spot+1)%MAXEVENTS; |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | SDL_mutexV(SDL_EventQ.lock); |
||
334 | } else { |
||
335 | SDL_SetError("Couldn't lock event queue"); |
||
336 | used = -1; |
||
337 | } |
||
338 | return(used); |
||
339 | } |
||
340 | |||
341 | /* Run the system dependent event loops */ |
||
342 | void SDL_PumpEvents(void) |
||
343 | { |
||
344 | if ( !SDL_EventThread ) { |
||
345 | SDL_VideoDevice *video = current_video; |
||
346 | SDL_VideoDevice *this = current_video; |
||
347 | |||
348 | /* Get events from the video subsystem */ |
||
349 | if ( video ) { |
||
350 | video->PumpEvents(this); |
||
351 | } |
||
352 | |||
353 | /* Queue pending key-repeat events */ |
||
354 | SDL_CheckKeyRepeat(); |
||
355 | |||
356 | #ifndef DISABLE_JOYSTICK |
||
357 | /* Check for joystick state change */ |
||
358 | if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) { |
||
359 | SDL_JoystickUpdate(); |
||
360 | } |
||
361 | #endif |
||
362 | } |
||
363 | } |
||
364 | |||
365 | /* Public functions */ |
||
366 | |||
367 | int SDL_PollEvent (SDL_Event *event) |
||
368 | { |
||
369 | SDL_PumpEvents(); |
||
370 | |||
371 | return(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)); |
||
372 | } |
||
373 | |||
374 | int SDL_WaitEvent (SDL_Event *event) |
||
375 | { |
||
376 | while ( 1 ) { |
||
377 | SDL_PumpEvents(); |
||
378 | switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) { |
||
379 | case -1: return -1; |
||
380 | case 1: return 1; |
||
381 | case 0: SDL_Delay(10); |
||
382 | } |
||
383 | } |
||
384 | } |
||
385 | |||
386 | int SDL_PushEvent(SDL_Event *event) |
||
387 | { |
||
388 | return(SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0)); |
||
389 | } |
||
390 | |||
391 | void SDL_SetEventFilter (SDL_EventFilter filter) |
||
392 | { |
||
393 | SDL_Event bitbucket; |
||
394 | |||
395 | /* Set filter and discard pending events */ |
||
396 | SDL_EventOK = filter; |
||
397 | while ( SDL_PollEvent(&bitbucket) > 0 ) |
||
398 | ; |
||
399 | } |
||
400 | |||
401 | SDL_EventFilter SDL_GetEventFilter(void) |
||
402 | { |
||
403 | return(SDL_EventOK); |
||
404 | } |
||
405 | |||
406 | Uint8 SDL_EventState (Uint8 type, int state) |
||
407 | { |
||
408 | SDL_Event bitbucket; |
||
409 | Uint8 current_state; |
||
410 | |||
411 | /* If SDL_ALLEVENTS was specified... */ |
||
412 | if ( type == 0xFF ) { |
||
413 | current_state = SDL_IGNORE; |
||
414 | for ( type=0; type |
||
415 | if ( SDL_ProcessEvents[type] != SDL_IGNORE ) { |
||
416 | current_state = SDL_ENABLE; |
||
417 | } |
||
418 | SDL_ProcessEvents[type] = state; |
||
419 | if ( state == SDL_ENABLE ) { |
||
420 | SDL_eventstate |= (0x00000001 << (type)); |
||
421 | } else { |
||
422 | SDL_eventstate &= ~(0x00000001 << (type)); |
||
423 | } |
||
424 | } |
||
425 | while ( SDL_PollEvent(&bitbucket) > 0 ) |
||
426 | ; |
||
427 | return(current_state); |
||
428 | } |
||
429 | |||
430 | /* Just set the state for one event type */ |
||
431 | current_state = SDL_ProcessEvents[type]; |
||
432 | switch (state) { |
||
433 | case SDL_IGNORE: |
||
434 | case SDL_ENABLE: |
||
435 | /* Set state and discard pending events */ |
||
436 | SDL_ProcessEvents[type] = state; |
||
437 | if ( state == SDL_ENABLE ) { |
||
438 | SDL_eventstate |= (0x00000001 << (type)); |
||
439 | } else { |
||
440 | SDL_eventstate &= ~(0x00000001 << (type)); |
||
441 | } |
||
442 | while ( SDL_PollEvent(&bitbucket) > 0 ) |
||
443 | ; |
||
444 | break; |
||
445 | default: |
||
446 | /* Querying state? */ |
||
447 | break; |
||
448 | } |
||
449 | return(current_state); |
||
450 | } |
||
451 | |||
452 | /* This is a generic event handler. |
||
453 | */ |
||
454 | int SDL_PrivateSysWMEvent(SDL_SysWMmsg *message) |
||
455 | { |
||
456 | int posted; |
||
457 | |||
458 | posted = 0; |
||
459 | if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) { |
||
460 | SDL_Event event; |
||
461 | memset(&event, 0, sizeof(event)); |
||
462 | event.type = SDL_SYSWMEVENT; |
||
463 | event.syswm.msg = message; |
||
464 | if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) { |
||
465 | posted = 1; |
||
466 | SDL_PushEvent(&event); |
||
467 | } |
||
468 | } |
||
469 | /* Update internal event state */ |
||
470 | return(posted); |
||
471 | }><>><>><>><>>>>>><> |