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