/
SDL_timer.c
285 lines (257 loc) · 6.52 KB
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
11
12
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
13
Lesser General Public License for more details.
14
15
16
17
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
18
19
Sam Lantinga
20
slouken@libsdl.org
21
*/
22
#include "SDL_config.h"
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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;
51
static volatile SDL_bool list_changed = SDL_FALSE;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* 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;
75
76
77
if ( SDL_timer_started ) {
SDL_TimerQuit();
}
78
79
80
81
82
83
if ( ! SDL_timer_threaded ) {
retval = SDL_SYS_TimerInit();
}
if ( SDL_timer_threaded ) {
SDL_timer_mutex = SDL_CreateMutex();
}
84
85
86
if ( retval == 0 ) {
SDL_timer_started = 1;
}
87
88
89
90
91
92
93
94
95
96
97
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);
98
SDL_timer_mutex = NULL;
99
100
101
102
103
104
105
106
107
}
SDL_timer_started = 0;
SDL_timer_threaded = 0;
}
void SDL_ThreadedTimerCheck(void)
{
Uint32 now, ms;
SDL_TimerID t, prev, next;
108
SDL_bool removed;
109
110
SDL_mutexP(SDL_timer_mutex);
111
112
list_changed = SDL_FALSE;
now = SDL_GetTicks();
113
for ( prev = NULL, t = SDL_timers; t; t = next ) {
114
removed = SDL_FALSE;
115
116
ms = t->interval - SDL_TIMESLICE;
next = t->next;
117
118
119
if ( (int)(now - t->last_alarm) > (int)ms ) {
struct _SDL_TimerID timer;
120
121
122
123
124
125
126
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",
127
t, SDL_ThreadID());
128
#endif
129
timer = *t;
130
SDL_mutexV(SDL_timer_mutex);
131
ms = timer.cb(timer.interval, timer.param);
132
133
SDL_mutexP(SDL_timer_mutex);
if ( list_changed ) {
134
135
/* Abort, list of timers modified */
/* FIXME: what if ms was changed? */
136
137
138
139
140
break;
}
if ( ms != t->interval ) {
if ( ms ) {
t->interval = ROUND_RESOLUTION(ms);
141
142
} else {
/* Remove timer from the list */
143
144
145
146
147
148
149
150
#ifdef DEBUG_TIMERS
printf("SDL: Removing timer %p\n", t);
#endif
if ( prev ) {
prev->next = next;
} else {
SDL_timers = next;
}
151
SDL_free(t);
152
153
--SDL_timer_running;
removed = SDL_TRUE;
154
155
156
157
158
159
160
161
162
163
164
}
}
}
/* Don't update prev if the timer has disappeared */
if ( ! removed ) {
prev = t;
}
}
SDL_mutexV(SDL_timer_mutex);
}
165
166
167
static SDL_TimerID SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback, void *param)
{
SDL_TimerID t;
168
t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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;
}
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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);
201
t = SDL_AddTimerInternal(interval, callback, param);
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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;
}
221
SDL_free(t);
222
--SDL_timer_running;
223
224
225
226
227
228
removed = SDL_TRUE;
list_changed = SDL_TRUE;
break;
}
}
#ifdef DEBUG_TIMERS
229
printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n", (Uint32)id, removed, SDL_timer_running, SDL_ThreadID());
230
231
232
233
234
235
#endif
SDL_mutexV(SDL_timer_mutex);
return removed;
}
/* Old style callback functions are wrapped through this */
236
static Uint32 SDLCALL callback_wrapper(Uint32 ms, void *param)
237
238
239
240
241
242
243
244
245
246
247
248
249
{
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;
250
251
252
253
if ( SDL_timer_threaded ) {
SDL_mutexP(SDL_timer_mutex);
}
254
255
if ( SDL_timer_running ) { /* Stop any currently running timer */
if ( SDL_timer_threaded ) {
256
257
258
while ( SDL_timers ) {
SDL_TimerID freeme = SDL_timers;
SDL_timers = SDL_timers->next;
259
SDL_free(freeme);
260
261
262
}
SDL_timer_running = 0;
list_changed = SDL_TRUE;
263
264
} else {
SDL_SYS_StopTimer();
265
SDL_timer_running = 0;
266
267
268
269
}
}
if ( ms ) {
if ( SDL_timer_threaded ) {
270
271
272
if ( SDL_AddTimerInternal(ms, callback_wrapper, (void *)callback) == NULL ) {
retval = -1;
}
273
274
275
276
277
278
279
} else {
SDL_timer_running = 1;
SDL_alarm_interval = ms;
SDL_alarm_callback = callback;
retval = SDL_SYS_StartTimer();
}
}
280
if ( SDL_timer_threaded ) {
281
SDL_mutexV(SDL_timer_mutex);
282
283
}
284
285
return retval;
}