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