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