diff src/events/SDL_events.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children e8157fcb3114
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/events/SDL_events.c	Thu Apr 26 16:45:43 2001 +0000
@@ -0,0 +1,475 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@devolution.com
+*/
+
+#ifdef SAVE_RCSID
+static char rcsid =
+ "@(#) $Id$";
+#endif
+
+/* General event handling code for SDL */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "SDL.h"
+#include "SDL_thread.h"
+#include "SDL_mutex.h"
+#include "SDL_events.h"
+#include "SDL_events_c.h"
+#include "SDL_timer_c.h"
+#ifndef DISABLE_JOYSTICK
+#include "SDL_joystick_c.h"
+#endif
+#ifndef ENABLE_X11
+#define DISABLE_X11
+#endif
+#include "SDL_syswm.h"
+#include "SDL_sysevents.h"
+
+/* Public data -- the event filter */
+SDL_EventFilter SDL_EventOK = NULL;
+Uint8 SDL_ProcessEvents[SDL_NUMEVENTS];
+static Uint32 SDL_eventstate = 0;
+
+/* Private data -- event queue */
+#define MAXEVENTS	128
+static struct {
+	SDL_mutex *lock;
+	int active;
+	int head;
+	int tail;
+	SDL_Event event[MAXEVENTS];
+	int wmmsg_next;
+	struct SDL_SysWMmsg wmmsg[MAXEVENTS];
+} SDL_EventQ;
+
+/* Private data -- event locking structure */
+static struct {
+	SDL_mutex *lock;
+	int safe;
+} SDL_EventLock;
+
+/* Thread functions */
+static SDL_Thread *SDL_EventThread = NULL;	/* Thread handle */
+static Uint32 event_thread;			/* The event thread id */
+
+void SDL_Lock_EventThread(void)
+{
+	if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
+		/* Grab lock and spin until we're sure event thread stopped */
+		SDL_mutexP(SDL_EventLock.lock);
+		while ( ! SDL_EventLock.safe ) {
+			SDL_Delay(1);
+		}
+	}
+}
+void SDL_Unlock_EventThread(void)
+{
+	if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
+		SDL_mutexV(SDL_EventLock.lock);
+	}
+}
+
+static int SDL_GobbleEvents(void *unused)
+{
+	SDL_SetTimerThreaded(2);
+	event_thread = SDL_ThreadID();
+	while ( SDL_EventQ.active ) {
+		SDL_VideoDevice *video = current_video;
+		SDL_VideoDevice *this  = current_video;
+
+		/* Get events from the video subsystem */
+		if ( video ) {
+			video->PumpEvents(this);
+		}
+
+		/* Queue pending key-repeat events */
+		SDL_CheckKeyRepeat();
+
+#ifndef DISABLE_JOYSTICK
+		/* Check for joystick state change */
+		if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) {
+			SDL_JoystickUpdate();
+		}
+#endif
+
+		/* Give up the CPU for the rest of our timeslice */
+		SDL_EventLock.safe = 1;
+		if( SDL_timer_running ) {
+			SDL_ThreadedTimerCheck();
+		}
+		SDL_Delay(1);
+
+		/* Check for event locking.
+		   On the P of the lock mutex, if the lock is held, this thread
+		   will wait until the lock is released before continuing.  The
+		   safe flag will be set, meaning that the other thread can go
+		   about it's business.  The safe flag is reset before the V,
+		   so as soon as the mutex is free, other threads can see that
+		   it's not safe to interfere with the event thread.
+		 */
+		SDL_mutexP(SDL_EventLock.lock);
+		SDL_EventLock.safe = 0;
+		SDL_mutexV(SDL_EventLock.lock);
+	}
+	SDL_SetTimerThreaded(0);
+	event_thread = 0;
+	return(0);
+}
+
+static int SDL_StartEventThread(Uint32 flags)
+{
+	/* Reset everything to zero */
+	SDL_EventThread = NULL;
+	memset(&SDL_EventLock, 0, sizeof(SDL_EventLock));
+
+	/* Create the lock and set ourselves active */
+#ifndef DISABLE_THREADS
+	SDL_EventQ.lock = SDL_CreateMutex();
+	if ( SDL_EventQ.lock == NULL ) {
+#ifdef macintosh /* On MacOS 7/8, you can't multithread, so no lock needed */
+		;
+#else
+		return(-1);
+#endif
+	}
+#endif /* !DISABLE_THREADS */
+	SDL_EventQ.active = 1;
+
+	if ( (flags&SDL_INIT_EVENTTHREAD) == SDL_INIT_EVENTTHREAD ) {
+		SDL_EventLock.lock = SDL_CreateMutex();
+		if ( SDL_EventLock.lock == NULL ) {
+			return(-1);
+		}
+		SDL_EventLock.safe = 0;
+
+		SDL_EventThread = SDL_CreateThread(SDL_GobbleEvents, NULL);
+		if ( SDL_EventThread == NULL ) {
+			return(-1);
+		}
+	} else {
+		event_thread = 0;
+	}
+	return(0);
+}
+
+static void SDL_StopEventThread(void)
+{
+	SDL_EventQ.active = 0;
+	if ( SDL_EventThread ) {
+		SDL_WaitThread(SDL_EventThread, NULL);
+		SDL_EventThread = NULL;
+		SDL_DestroyMutex(SDL_EventLock.lock);
+	}
+	SDL_DestroyMutex(SDL_EventQ.lock);
+}
+
+Uint32 SDL_EventThreadID(void)
+{
+	return(event_thread);
+}
+
+/* Public functions */
+
+void SDL_StopEventLoop(void)
+{
+	/* Halt the event thread, if running */
+	SDL_StopEventThread();
+
+	/* Clean out EventQ */
+	SDL_EventQ.head = 0;
+	SDL_EventQ.tail = 0;
+	SDL_EventQ.wmmsg_next = 0;
+}
+
+/* This function (and associated calls) may be called more than once */
+int SDL_StartEventLoop(Uint32 flags)
+{
+	int retcode;
+
+	/* Clean out the event queue */
+	SDL_EventThread = NULL;
+	SDL_EventQ.lock = NULL;
+	SDL_StopEventLoop();
+
+	/* No filter to start with, process most event types */
+	SDL_EventOK = NULL;
+	memset(SDL_ProcessEvents,SDL_ENABLE,sizeof(SDL_ProcessEvents));
+	SDL_eventstate = ~0;
+	/* It's not save to call SDL_EventState() yet */
+	SDL_eventstate &= ~(0x00000001 << SDL_SYSWMEVENT);
+	SDL_ProcessEvents[SDL_SYSWMEVENT] = SDL_IGNORE;
+
+	/* Initialize event handlers */
+	retcode = 0;
+	retcode += SDL_AppActiveInit();
+	retcode += SDL_KeyboardInit();
+	retcode += SDL_MouseInit();
+	retcode += SDL_QuitInit();
+	if ( retcode < 0 ) {
+		/* We don't expect them to fail, but... */
+		return(-1);
+	}
+
+	/* Create the lock and event thread */
+	if ( SDL_StartEventThread(flags) < 0 ) {
+		SDL_StopEventLoop();
+		return(-1);
+	}
+	return(0);
+}
+
+
+/* Add an event to the event queue -- called with the queue locked */
+static int SDL_AddEvent(SDL_Event *event)
+{
+	int tail, added;
+
+	tail = (SDL_EventQ.tail+1)%MAXEVENTS;
+	if ( tail == SDL_EventQ.head ) {
+		/* Overflow, drop event */
+		added = 0;
+	} else {
+		SDL_EventQ.event[SDL_EventQ.tail] = *event;
+		if (event->type == SDL_SYSWMEVENT) {
+			/* Note that it's possible to lose an event */
+			int next = SDL_EventQ.wmmsg_next;
+			SDL_EventQ.wmmsg[next] = *event->syswm.msg;
+		        SDL_EventQ.event[SDL_EventQ.tail].syswm.msg =
+						&SDL_EventQ.wmmsg[next];
+			SDL_EventQ.wmmsg_next = (next+1)%MAXEVENTS;
+		}
+		SDL_EventQ.tail = tail;
+		added = 1;
+	}
+	return(added);
+}
+
+/* Cut an event, and return the next valid spot, or the tail */
+/*                           -- called with the queue locked */
+static int SDL_CutEvent(int spot)
+{
+	if ( spot == SDL_EventQ.head ) {
+		SDL_EventQ.head = (SDL_EventQ.head+1)%MAXEVENTS;
+		return(SDL_EventQ.head);
+	} else
+	if ( (spot+1)%MAXEVENTS == SDL_EventQ.tail ) {
+		SDL_EventQ.tail = spot;
+		return(SDL_EventQ.tail);
+	} else
+	/* We cut the middle -- shift everything over */
+	{
+		int here, next;
+
+		/* This can probably be optimized with memcpy() -- careful! */
+		if ( --SDL_EventQ.tail < 0 ) {
+			SDL_EventQ.tail = MAXEVENTS-1;
+		}
+		for ( here=spot; here != SDL_EventQ.tail; here = next ) {
+			next = (here+1)%MAXEVENTS;
+			SDL_EventQ.event[here] = SDL_EventQ.event[next];
+		}
+		return(spot);
+	}
+	/* NOTREACHED */
+}
+
+/* Lock the event queue, take a peep at it, and unlock it */
+int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_eventaction action,
+								Uint32 mask)
+{
+	int i, used;
+
+	/* Don't look after we've quit */
+	if ( ! SDL_EventQ.active ) {
+		return(0);
+	}
+	/* Lock the event queue */
+	used = 0;
+	if ( SDL_mutexP(SDL_EventQ.lock) == 0 ) {
+		if ( action == SDL_ADDEVENT ) {
+			for ( i=0; i<numevents; ++i ) {
+				used += SDL_AddEvent(&events[i]);
+			}
+		} else {
+			SDL_Event tmpevent;
+			int spot;
+
+			/* If 'events' is NULL, just see if they exist */
+			if ( events == NULL ) {
+				action = SDL_PEEKEVENT;
+				numevents = 1;
+				events = &tmpevent;
+			}
+			spot = SDL_EventQ.head;
+			while ((used < numevents)&&(spot != SDL_EventQ.tail)) {
+				if ( mask & SDL_EVENTMASK(SDL_EventQ.event[spot].type) ) {
+					events[used++] = SDL_EventQ.event[spot];
+					if ( action == SDL_GETEVENT ) {
+						spot = SDL_CutEvent(spot);
+					} else {
+						spot = (spot+1)%MAXEVENTS;
+					}
+				} else {
+					spot = (spot+1)%MAXEVENTS;
+				}
+			}
+		}
+		SDL_mutexV(SDL_EventQ.lock);
+	} else {
+		SDL_SetError("Couldn't lock event queue");
+		used = -1;
+	}
+	return(used);
+}
+
+/* Run the system dependent event loops */
+void SDL_PumpEvents(void)
+{
+	if ( !SDL_EventThread ) {
+		SDL_VideoDevice *video = current_video;
+		SDL_VideoDevice *this  = current_video;
+
+		/* Get events from the video subsystem */
+		if ( video ) {
+			video->PumpEvents(this);
+		}
+
+		/* Queue pending key-repeat events */
+		SDL_CheckKeyRepeat();
+
+#ifndef DISABLE_JOYSTICK
+		/* Check for joystick state change */
+		if ( SDL_numjoysticks && (SDL_eventstate & SDL_JOYEVENTMASK) ) {
+			SDL_JoystickUpdate();
+		}
+#endif
+	}
+}
+
+/* Public functions */
+
+int SDL_PollEvent (SDL_Event *event)
+{
+	SDL_PumpEvents();
+
+	return(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS));
+}
+
+int SDL_WaitEvent (SDL_Event *event)
+{
+	while ( 1 ) {
+		SDL_PumpEvents();
+		switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
+		    case -1: return -1; 
+		    case 1: return 1;
+		    case 0: SDL_Delay(10);
+		}
+	}
+}
+
+int SDL_PushEvent(SDL_Event *event)
+{
+	return(SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0));
+}
+
+void SDL_SetEventFilter (SDL_EventFilter filter)
+{
+	SDL_Event bitbucket;
+
+	/* Set filter and discard pending events */
+	SDL_EventOK = filter;
+	while ( SDL_PollEvent(&bitbucket) > 0 )
+		;
+}
+
+SDL_EventFilter SDL_GetEventFilter(void)
+{
+	return(SDL_EventOK);
+}
+
+Uint8 SDL_EventState (Uint8 type, int state)
+{
+	SDL_Event bitbucket;
+	Uint8 current_state;
+
+	/* If SDL_ALLEVENTS was specified... */
+	if ( type == 0xFF ) {
+		current_state = SDL_IGNORE;
+		for ( type=0; type<SDL_NUMEVENTS; ++type ) {
+			if ( SDL_ProcessEvents[type] != SDL_IGNORE ) {
+				current_state = SDL_ENABLE;
+			}
+			SDL_ProcessEvents[type] = state;
+			if ( state == SDL_ENABLE ) {
+				SDL_eventstate |= (0x00000001 << (type));
+			} else {
+				SDL_eventstate &= ~(0x00000001 << (type));
+			}
+		}
+		while ( SDL_PollEvent(&bitbucket) > 0 )
+			;
+		return(current_state);
+	}
+
+	/* Just set the state for one event type */
+	current_state = SDL_ProcessEvents[type];
+	switch (state) {
+		case SDL_IGNORE:
+		case SDL_ENABLE:
+			/* Set state and discard pending events */
+			SDL_ProcessEvents[type] = state;
+			if ( state == SDL_ENABLE ) {
+				SDL_eventstate |= (0x00000001 << (type));
+			} else {
+				SDL_eventstate &= ~(0x00000001 << (type));
+			}
+			while ( SDL_PollEvent(&bitbucket) > 0 )
+				;
+			break;
+		default:
+			/* Querying state? */
+			break;
+	}
+	return(current_state);
+}
+
+/* This is a generic event handler.
+ */
+int SDL_PrivateSysWMEvent(SDL_SysWMmsg *message)
+{
+	int posted;
+
+	posted = 0;
+	if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
+		SDL_Event event;
+		memset(&event, 0, sizeof(event));
+		event.type = SDL_SYSWMEVENT;
+		event.syswm.msg = message;
+		if ( (SDL_EventOK == NULL) || (*SDL_EventOK)(&event) ) {
+			posted = 1;
+			SDL_PushEvent(&event);
+		}
+	}
+	/* Update internal event state */
+	return(posted);
+}