[email protected]
#include "SDL_config.h"
#include "SDL_timer.h"
#include "SDL_timer_c.h"
#include "SDL_mutex.h"
#include "SDL_systimer.h"
BUG
int SDL_timer_started = 0;
int SDL_timer_running = 0;
Uint32 SDL_alarm_interval = 0;
SDL_TimerCallback SDL_alarm_callback;
static int SDL_timer_threaded = 0;
struct _SDL_TimerID {
	Uint32 interval;
	SDL_NewTimerCallback cb;
	void *param;
	Uint32 last_alarm;
	struct _SDL_TimerID *next;
};
static SDL_TimerID SDL_timers = NULL;
static SDL_mutex *SDL_timer_mutex;
static volatile SDL_bool list_changed = SDL_FALSE;
int SDL_SetTimerThreaded(int value)
{
	int retval;
	if ( SDL_timer_started ) {
		SDL_SetError("Timer already initialized");
		retval = -1;
	} else {
		retval = 0;
		SDL_timer_threaded = value;
	}
	return retval;
}
int SDL_TimerInit(void)
{
	int retval;
	retval = 0;
	if ( SDL_timer_started ) {
		SDL_TimerQuit();
	}
	if ( ! SDL_timer_threaded ) {
		retval = SDL_SYS_TimerInit();
	}
	if ( SDL_timer_threaded ) {
		SDL_timer_mutex = SDL_CreateMutex();
	}
	if ( retval == 0 ) {
		SDL_timer_started = 1;
	}
	return(retval);
}
void SDL_TimerQuit(void)
{
	SDL_SetTimer(0, NULL);
	if ( SDL_timer_threaded < 2 ) {
		SDL_SYS_TimerQuit();
	}
	if ( SDL_timer_threaded ) {
		SDL_DestroyMutex(SDL_timer_mutex);
		SDL_timer_mutex = NULL;
	}
	SDL_timer_started = 0;
	SDL_timer_threaded = 0;
}
void SDL_ThreadedTimerCheck(void)
{
	Uint32 now, ms;
	SDL_TimerID t, prev, next;
	SDL_bool removed;
	SDL_mutexP(SDL_timer_mutex);
	list_changed = SDL_FALSE;
	now = SDL_GetTicks();
	for ( prev = NULL, t = SDL_timers; t; t = next ) {
		removed = SDL_FALSE;
		ms = t->interval - SDL_TIMESLICE;
		next = t->next;
		if ( (int)(now - t->last_alarm) > (int)ms ) {
			struct _SDL_TimerID timer;
			if ( (now - t->last_alarm) < t->interval ) {
				t->last_alarm += t->interval;
			} else {
				t->last_alarm = now;
			}
#ifdef DEBUG_TIMERS
			printf("Executing timer %p (thread = %d)\n",
				t, SDL_ThreadID());
#endif
			timer = *t;
			SDL_mutexV(SDL_timer_mutex);
			ms = timer.cb(timer.interval, timer.param);
			SDL_mutexP(SDL_timer_mutex);
			if ( list_changed ) {
				
				FIXME:
				break;
			}
			if ( ms != t->interval ) {
				if ( ms ) {
					t->interval = ROUND_RESOLUTION(ms);
				} else {
					
#ifdef DEBUG_TIMERS
					printf("SDL: Removing timer %p\n", t);
#endif
					if ( prev ) {
						prev->next = next;
					} else {
						SDL_timers = next;
					}
					SDL_free(t);
					--SDL_timer_running;
					removed = SDL_TRUE;
				}
			}
		}
		
		if ( ! removed ) {
			prev = t;
		}
	}
	SDL_mutexV(SDL_timer_mutex);
}
static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
{
	SDL_TimerID t;
	t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
	if ( t ) {
		t->interval = ROUND_RESOLUTION(interval);
		t->cb = callback;
		t->param = param;
		t->last_alarm = SDL_GetTicks();
		t->next = SDL_timers;
		SDL_timers = t;
		++SDL_timer_running;
		list_changed = SDL_TRUE;
	}
#ifdef DEBUG_TIMERS
	printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32)t, SDL_timer_running);
#endif
	return t;
}
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
{
	SDL_TimerID t;
	if ( ! SDL_timer_mutex ) {
		if ( SDL_timer_started ) {
			SDL_SetError("This platform doesn't support multiple timers");
		} else {
			SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
		}
		return NULL;
	}
	if ( ! SDL_timer_threaded ) {
		SDL_SetError("Multiple timers require threaded events!");
		return NULL;
	}
	SDL_mutexP(SDL_timer_mutex);
	t = SDL_AddTimerInternal(interval, callback, param);
	SDL_mutexV(SDL_timer_mutex);
	return t;
}
SDL_bool SDL_RemoveTimer(SDL_TimerID id)
{
	SDL_TimerID t, prev = NULL;
	SDL_bool removed;
	removed = SDL_FALSE;
	SDL_mutexP(SDL_timer_mutex);
	
	for (t = SDL_timers; t; prev=t, t = t->next ) {
		if ( t == id ) {
			if(prev) {
				prev->next = t->next;
			} else {
				SDL_timers = t->next;
			}
			SDL_free(t);
			--SDL_timer_running;
			removed = SDL_TRUE;
			list_changed = SDL_TRUE;
			break;
		}
	}
#ifdef DEBUG_TIMERS
	printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
#endif
	SDL_mutexV(SDL_timer_mutex);
	return removed;
}
static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param)
{
	SDL_TimerCallback func = (SDL_TimerCallback) param;
	return (*func)(ms);
}
int SDL_SetTimer(Uint32 ms, SDL_TimerCallback callback)
{
	int retval;
#ifdef DEBUG_TIMERS
	printf("SDL_SetTimer(%d)\n", ms);
#endif
	retval = 0;
	if ( SDL_timer_threaded ) {
		SDL_mutexP(SDL_timer_mutex);
	}
	if ( SDL_timer_running ) {	
		if ( SDL_timer_threaded ) {
			while ( SDL_timers ) {
				SDL_TimerID freeme = SDL_timers;
				SDL_timers = SDL_timers->next;
				SDL_free(freeme);
			}
			SDL_timer_running = 0;
			list_changed = SDL_TRUE;
		} else {
			SDL_SYS_StopTimer();
			SDL_timer_running = 0;
		}
	}
	if ( ms ) {
		if ( SDL_timer_threaded ) {
			if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
				retval = -1;
			}
		} else {
			SDL_timer_running = 1;
			SDL_alarm_interval = ms;
			SDL_alarm_callback = callback;
			retval = SDL_SYS_StartTimer();
		}
	}
	if ( SDL_timer_threaded ) {
		SDL_mutexV(SDL_timer_mutex);
	}
	return retval;
}