src/events/SDL_events.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 21 Aug 2005 06:18:54 +0000
changeset 1123 28ac87a38c17
parent 1028 5ba65305c954
child 1140 af8b0f9ac2f4
permissions -rw-r--r--
Date: Fri, 08 Jul 2005 22:43:48 +0200 (CEST)
From: Jiri Svoboda
Subject: [SDL] signal handling bug

I encountered the following bug:
SDL doesn't reset signal handlers for SIGTERM and SIGINT, after calling SDL_Quit these remain hooked to the handler in SDL_quit.c, being translated into SDL_QUIT events.

Consequently an application that issues a SDL_Quit and remains running will ignore any SIGTERM or SIGINT., and specifically CTRL-C presses.
     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 	/* Shutdown event handlers */
   200 	SDL_AppActiveQuit();
   201 	SDL_KeyboardQuit();
   202 	SDL_MouseQuit();
   203 	SDL_QuitQuit();
   204 
   205 	/* Clean out EventQ */
   206 	SDL_EventQ.head = 0;
   207 	SDL_EventQ.tail = 0;
   208 	SDL_EventQ.wmmsg_next = 0;
   209 }
   210 
   211 /* This function (and associated calls) may be called more than once */
   212 int SDL_StartEventLoop(Uint32 flags)
   213 {
   214 	int retcode;
   215 
   216 	/* Clean out the event queue */
   217 	SDL_EventThread = NULL;
   218 	SDL_EventQ.lock = NULL;
   219 	SDL_StopEventLoop();
   220 
   221 	/* No filter to start with, process most event types */
   222 	SDL_EventOK = NULL;
   223 	memset(SDL_ProcessEvents,SDL_ENABLE,sizeof(SDL_ProcessEvents));
   224 	SDL_eventstate = ~0;
   225 	/* It's not save to call SDL_EventState() yet */
   226 	SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
   227 	SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
   228 
   229 	/* Initialize event handlers */
   230 	retcode = 0;
   231 	retcode += SDL_AppActiveInit();
   232 	retcode += SDL_KeyboardInit();
   233 	retcode += SDL_MouseInit();
   234 	retcode += SDL_QuitInit();
   235 	if ( retcode < 0 ) {
   236 		/* We don't expect them to fail, but... */
   237 		return(-1);
   238 	}
   239 
   240 	/* Create the lock and event thread */
   241 	if ( SDL_StartEventThread(flags) < 0 ) {
   242 		SDL_StopEventLoop();
   243 		return(-1);
   244 	}
   245 	return(0);
   246 }
   247 
   248 
   249 /* Add an event to the event queue -- called with the queue locked */
   250 static int SDL_AddEvent(SDL_Event *event)
   251 {
   252 	int tail, added;
   253 
   254 	tail = (SDL_EventQ.tail+1)%MAXEVENTS;
   255 	if ( tail == SDL_EventQ.head ) {
   256 		/* Overflow, drop event */
   257 		added = 0;
   258 	} else {
   259 		SDL_EventQ.event[SDL_EventQ.tail] = *event;
   260 		if (event->type == SDL_SYSWMEVENT) {
   261 			/* Note that it's possible to lose an event */
   262 			int next = SDL_EventQ.wmmsg_next;
   263 			SDL_EventQ.wmmsg[next] = *event->syswm.msg;
   264 		        SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
   265 						&SDL_EventQ.wmmsg[next];
   266 			SDL_EventQ.wmmsg_next = (next+1)%MAXEVENTS;
   267 		}
   268 		SDL_EventQ.tail = tail;
   269 		added = 1;
   270 	}
   271 	return(added);
   272 }
   273 
   274 /* Cut an event, and return the next valid spot, or the tail */
   275 /*                           -- called with the queue locked */
   276 static int SDL_CutEvent(int spot)
   277 {
   278 	if ( spot == SDL_EventQ.head ) {
   279 		SDL_EventQ.head = (SDL_EventQ.head+1)%MAXEVENTS;
   280 		return(SDL_EventQ.head);
   281 	} else
   282 	if ( (spot+1)%MAXEVENTS == SDL_EventQ.tail ) {
   283 		SDL_EventQ.tail = spot;
   284 		return(SDL_EventQ.tail);
   285 	} else
   286 	/* We cut the middle -- shift everything over */
   287 	{
   288 		int here, next;
   289 
   290 		/* This can probably be optimized with memcpy() -- careful! */
   291 		if ( --SDL_EventQ.tail < 0 ) {
   292 			SDL_EventQ.tail = MAXEVENTS-1;
   293 		}
   294 		for ( here=spot; here != SDL_EventQ.tail; here = next ) {
   295 			next = (here+1)%MAXEVENTS;
   296 			SDL_EventQ.event[here] = SDL_EventQ.event[next];
   297 		}
   298 		return(spot);
   299 	}
   300 	/* NOTREACHED */
   301 }
   302 
   303 /* Lock the event queue, take a peep at it, and unlock it */
   304 int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action,
   305 								Uint32 mask)
   306 {
   307 	int i, used;
   308 
   309 	/* Don't look after we've quit */
   310 	if ( ! SDL_EventQ.active ) {
   311 		return(-1);
   312 	}
   313 	/* Lock the event queue */
   314 	used = 0;
   315 	if ( SDL_mutexP(SDL_EventQ.lock) == 0 ) {
   316 		if ( action == SDL_ADDEVENT ) {
   317 			for ( i=0; i<numevents; ++i ) {
   318 				used += SDL_AddEvent(&events[i]);
   319 			}
   320 		} else {
   321 			SDL_Event tmpevent;
   322 			int spot;
   323 
   324 			/* If 'events' is NULL, just see if they exist */
   325 			if ( events == NULL ) {
   326 				action = SDL_PEEKEVENT;
   327 				numevents = 1;
   328 				events = &tmpevent;
   329 			}
   330 			spot = SDL_EventQ.head;
   331 			while ((used < numevents)&&(spot != SDL_EventQ.tail)) {
   332 				if ( mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type) ) {
   333 					events[used++] = SDL_EventQ.event[spot];
   334 					if ( action == SDL_GETEVENT ) {
   335 						spot = SDL_CutEvent(spot);
   336 					} else {
   337 						spot = (spot+1)%MAXEVENTS;
   338 					}
   339 				} else {
   340 					spot = (spot+1)%MAXEVENTS;
   341 				}
   342 			}
   343 		}
   344 		SDL_mutexV(SDL_EventQ.lock);
   345 	} else {
   346 		SDL_SetError("Couldn't lock event queue");
   347 		used = -1;
   348 	}
   349 	return(used);
   350 }
   351 
   352 /* Run the system dependent event loops */
   353 void SDL_PumpEvents(void)
   354 {
   355 	if ( !SDL_EventThread ) {
   356 		SDL_VideoDevice *video = current_video;
   357 		SDL_VideoDevice *this  = current_video;
   358 
   359 		/* Get events from the video subsystem */
   360 		if ( video ) {
   361 			video->PumpEvents(this);
   362 		}
   363 
   364 		/* Queue pending key-repeat events */
   365 		SDL_CheckKeyRepeat();
   366 
   367 #ifndef DISABLE_JOYSTICK
   368 		/* Check for joystick state change */
   369 		if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) {
   370 			SDL_JoystickUpdate();
   371 		}
   372 #endif
   373 	}
   374 }
   375 
   376 /* Public functions */
   377 
   378 int SDL_PollEvent (SDL_Event *event)
   379 {
   380 	SDL_PumpEvents();
   381 
   382 	/* We can't return -1, just return 0 (no event) on error */
   383 	if ( SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS) <= 0 )
   384 		return 0;
   385 	return 1;
   386 }
   387 
   388 int SDL_WaitEvent (SDL_Event *event)
   389 {
   390 	while ( 1 ) {
   391 		SDL_PumpEvents();
   392 		switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
   393 		    case -1: return 0;
   394 		    case 1: return 1;
   395 		    case 0: SDL_Delay(10);
   396 		}
   397 	}
   398 }
   399 
   400 int SDL_PushEvent(SDL_Event *event)
   401 {
   402 	if ( SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0) <= 0 )
   403 		return -1;
   404 	return 0;
   405 }
   406 
   407 void SDL_SetEventFilter (SDL_EventFilter filter)
   408 {
   409 	SDL_Event bitbucket;
   410 
   411 	/* Set filter and discard pending events */
   412 	SDL_EventOK = filter;
   413 	while ( SDL_PollEvent(&bitbucket) > 0 )
   414 		;
   415 }
   416 
   417 SDL_EventFilter SDL_GetEventFilter(void)
   418 {
   419 	return(SDL_EventOK);
   420 }
   421 
   422 Uint8 SDL_EventState (Uint8 type, int state)
   423 {
   424 	SDL_Event bitbucket;
   425 	Uint8 current_state;
   426 
   427 	/* If SDL_ALLEVENTS was specified... */
   428 	if ( type == 0xFF ) {
   429 		current_state = SDL_IGNORE;
   430 		for ( type=0; type<SDL_NUMEVENTS; ++type ) {
   431 			if ( SDL_ProcessEvents[type] != SDL_IGNORE ) {
   432 				current_state = SDL_ENABLE;
   433 			}
   434 			SDL_ProcessEvents[type] = state;
   435 			if ( state == SDL_ENABLE ) {
   436 				SDL_eventstate |= (0x00000001 << (type));
   437 			} else {
   438 				SDL_eventstate &= ~(0x00000001 << (type));
   439 			}
   440 		}
   441 		while ( SDL_PollEvent(&bitbucket) > 0 )
   442 			;
   443 		return(current_state);
   444 	}
   445 
   446 	/* Just set the state for one event type */
   447 	current_state = SDL_ProcessEvents[type];
   448 	switch (state) {
   449 		case SDL_IGNORE:
   450 		case SDL_ENABLE:
   451 			/* Set state and discard pending events */
   452 			SDL_ProcessEvents[type] = state;
   453 			if ( state == SDL_ENABLE ) {
   454 				SDL_eventstate |= (0x00000001 << (type));
   455 			} else {
   456 				SDL_eventstate &= ~(0x00000001 << (type));
   457 			}
   458 			while ( SDL_PollEvent(&bitbucket) > 0 )
   459 				;
   460 			break;
   461 		default:
   462 			/* Querying state? */
   463 			break;
   464 	}
   465 	return(current_state);
   466 }
   467 
   468 /* This is a generic event handler.
   469  */
   470 int SDL_PrivateSysWMEvent(SDL_SysWMmsg *message)
   471 {
   472 	int posted;
   473 
   474 	posted = 0;
   475 	if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
   476 		SDL_Event event;
   477 		memset(&event, 0, sizeof(event));
   478 		event.type = SDL_SYSWMEVENT;
   479 		event.syswm.msg = message;
   480 		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
   481 			posted = 1;
   482 			SDL_PushEvent(&event);
   483 		}
   484 	}
   485 	/* Update internal event state */
   486 	return(posted);
   487 }