| /* |
| SDL - Simple DirectMedia Layer |
| Copyright (C) 1997-2012 Sam Lantinga |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| Sam Lantinga |
| slouken@libsdl.org |
| */ |
| #include "SDL_config.h" |
| |
| #include "SDL_timer.h" |
| #include "SDL_timer_c.h" |
| #include "SDL_mutex.h" |
| #include "SDL_systimer.h" |
| |
| /* #define DEBUG_TIMERS */ |
| |
| int SDL_timer_started = 0; |
| int SDL_timer_running = 0; |
| |
| /* Data to handle a single periodic alarm */ |
| Uint32 SDL_alarm_interval = 0; |
| SDL_TimerCallback SDL_alarm_callback; |
| |
| /* Data used for a thread-based timer */ |
| 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; |
| |
| /* Set whether or not the timer should use a thread. |
| This should not be called while the timer subsystem is running. |
| */ |
| 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 ) { |
| /* Abort, list of timers modified */ |
| /* FIXME: what if ms was changed? */ |
| break; |
| } |
| if ( ms != t->interval ) { |
| if ( ms ) { |
| t->interval = ROUND_RESOLUTION(ms); |
| } else { |
| /* Remove timer from the list */ |
| #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; |
| } |
| } |
| } |
| /* Don't update prev if the timer has disappeared */ |
| 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); |
| /* Look for id in the linked list of timers */ |
| 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; |
| } |
| |
| /* Old style callback functions are wrapped through this */ |
| 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 ) { /* Stop any currently running timer */ |
| 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; |
| } |