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