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