src/events/SDL_events.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 23 Nov 2005 07:29:56 +0000
changeset 1190 173c063d4f55
parent 1140 af8b0f9ac2f4
child 1312 c9b51268668f
permissions -rw-r--r--
OS/2 port!

This was mostly, if not entirely, written by "Doodle" and "Caetano":
doodle@scenergy.dfmk.hu
daniel@caetano.eng.br

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