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