1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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 (at) 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 } 503