src/timer/SDL_timer.c
author Edgar Simo <bobbens@gmail.com>
Thu, 31 Jul 2008 09:45:27 +0000
branchgsoc2008_force_feedback
changeset 2561 3696b9ce8a37
parent 1895 c121d94672cb
child 2859 99210400e8b9
permissions -rw-r--r--
Correctness patch, it's up to the SDL_haptic.c to clean up effects, not SDL_syshaptic.c.
slouken@1
     1
/*
slouken@1
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@1
     4
slouken@1
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@1
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@1
     9
slouken@1
    10
    This library is distributed in the hope that it will be useful,
slouken@1
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@1
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@1
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@1
    18
slouken@1
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@1
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@1
    23
slouken@1
    24
#include "SDL_timer.h"
slouken@1
    25
#include "SDL_timer_c.h"
slouken@1
    26
#include "SDL_mutex.h"
slouken@1
    27
#include "SDL_systimer.h"
slouken@1
    28
slouken@1
    29
/* #define DEBUG_TIMERS */
slouken@1
    30
slouken@1
    31
int SDL_timer_started = 0;
slouken@1
    32
int SDL_timer_running = 0;
slouken@1
    33
slouken@1
    34
/* Data to handle a single periodic alarm */
slouken@1
    35
Uint32 SDL_alarm_interval = 0;
slouken@1
    36
SDL_TimerCallback SDL_alarm_callback;
slouken@1
    37
slouken@1
    38
/* Data used for a thread-based timer */
slouken@1
    39
static int SDL_timer_threaded = 0;
slouken@1
    40
slouken@1895
    41
struct _SDL_TimerID
slouken@1895
    42
{
slouken@1895
    43
    Uint32 interval;
slouken@1895
    44
    SDL_NewTimerCallback cb;
slouken@1895
    45
    void *param;
slouken@1895
    46
    Uint32 last_alarm;
slouken@1895
    47
    struct _SDL_TimerID *next;
slouken@1
    48
};
slouken@1
    49
slouken@1
    50
static SDL_TimerID SDL_timers = NULL;
slouken@1
    51
static SDL_mutex *SDL_timer_mutex;
slouken@1028
    52
static volatile SDL_bool list_changed = SDL_FALSE;
slouken@1
    53
slouken@1
    54
/* Set whether or not the timer should use a thread.
slouken@1
    55
   This should not be called while the timer subsystem is running.
slouken@1
    56
*/
slouken@1895
    57
int
slouken@1895
    58
SDL_SetTimerThreaded(int value)
slouken@1
    59
{
slouken@1895
    60
    int retval;
slouken@1
    61
slouken@1895
    62
    if (SDL_timer_started) {
slouken@1895
    63
        SDL_SetError("Timer already initialized");
slouken@1895
    64
        retval = -1;
slouken@1895
    65
    } else {
slouken@1895
    66
        retval = 0;
slouken@1895
    67
        SDL_timer_threaded = value;
slouken@1895
    68
    }
slouken@1895
    69
    return retval;
slouken@1
    70
}
slouken@1
    71
slouken@1895
    72
int
slouken@1895
    73
SDL_TimerInit(void)
slouken@1
    74
{
slouken@1895
    75
    int retval;
slouken@1
    76
slouken@1895
    77
    retval = 0;
slouken@1895
    78
    if (SDL_timer_started) {
slouken@1895
    79
        SDL_TimerQuit();
slouken@1895
    80
    }
slouken@1895
    81
    if (!SDL_timer_threaded) {
slouken@1895
    82
        retval = SDL_SYS_TimerInit();
slouken@1895
    83
    }
slouken@1895
    84
    if (SDL_timer_threaded) {
slouken@1895
    85
        SDL_timer_mutex = SDL_CreateMutex();
slouken@1895
    86
    }
slouken@1895
    87
    if (retval == 0) {
slouken@1895
    88
        SDL_timer_started = 1;
slouken@1895
    89
    }
slouken@1895
    90
    return (retval);
slouken@1
    91
}
slouken@1
    92
slouken@1895
    93
void
slouken@1895
    94
SDL_TimerQuit(void)
slouken@1
    95
{
slouken@1895
    96
    SDL_SetTimer(0, NULL);
slouken@1895
    97
    if (SDL_timer_threaded < 2) {
slouken@1895
    98
        SDL_SYS_TimerQuit();
slouken@1895
    99
    }
slouken@1895
   100
    if (SDL_timer_threaded) {
slouken@1895
   101
        SDL_DestroyMutex(SDL_timer_mutex);
slouken@1895
   102
        SDL_timer_mutex = NULL;
slouken@1895
   103
    }
slouken@1895
   104
    SDL_timer_started = 0;
slouken@1895
   105
    SDL_timer_threaded = 0;
slouken@1
   106
}
slouken@1
   107
slouken@1895
   108
void
slouken@1895
   109
SDL_ThreadedTimerCheck(void)
slouken@1
   110
{
slouken@1895
   111
    Uint32 now, ms;
slouken@1895
   112
    SDL_TimerID t, prev, next;
slouken@1895
   113
    SDL_bool removed;
slouken@1
   114
slouken@1895
   115
    SDL_mutexP(SDL_timer_mutex);
slouken@1895
   116
    list_changed = SDL_FALSE;
slouken@1895
   117
    now = SDL_GetTicks();
slouken@1895
   118
    for (prev = NULL, t = SDL_timers; t; t = next) {
slouken@1895
   119
        removed = SDL_FALSE;
slouken@1895
   120
        ms = t->interval - SDL_TIMESLICE;
slouken@1895
   121
        next = t->next;
slouken@1895
   122
        if ((int) (now - t->last_alarm) > (int) ms) {
slouken@1895
   123
            struct _SDL_TimerID timer;
slouken@1028
   124
slouken@1895
   125
            if ((now - t->last_alarm) < t->interval) {
slouken@1895
   126
                t->last_alarm += t->interval;
slouken@1895
   127
            } else {
slouken@1895
   128
                t->last_alarm = now;
slouken@1895
   129
            }
slouken@1
   130
#ifdef DEBUG_TIMERS
slouken@1895
   131
            printf("Executing timer %p (thread = %d)\n", t, SDL_ThreadID());
slouken@1
   132
#endif
slouken@1895
   133
            timer = *t;
slouken@1895
   134
            SDL_mutexV(SDL_timer_mutex);
slouken@1895
   135
            ms = timer.cb(timer.interval, timer.param);
slouken@1895
   136
            SDL_mutexP(SDL_timer_mutex);
slouken@1895
   137
            if (list_changed) {
slouken@1895
   138
                /* Abort, list of timers modified */
slouken@1895
   139
                /* FIXME: what if ms was changed? */
slouken@1895
   140
                break;
slouken@1895
   141
            }
slouken@1895
   142
            if (ms != t->interval) {
slouken@1895
   143
                if (ms) {
slouken@1895
   144
                    t->interval = ROUND_RESOLUTION(ms);
slouken@1895
   145
                } else {
slouken@1895
   146
                    /* Remove timer from the list */
slouken@1
   147
#ifdef DEBUG_TIMERS
slouken@1895
   148
                    printf("SDL: Removing timer %p\n", t);
slouken@1
   149
#endif
slouken@1895
   150
                    if (prev) {
slouken@1895
   151
                        prev->next = next;
slouken@1895
   152
                    } else {
slouken@1895
   153
                        SDL_timers = next;
slouken@1895
   154
                    }
slouken@1895
   155
                    SDL_free(t);
slouken@1895
   156
                    --SDL_timer_running;
slouken@1895
   157
                    removed = SDL_TRUE;
slouken@1895
   158
                }
slouken@1895
   159
            }
slouken@1895
   160
        }
slouken@1895
   161
        /* Don't update prev if the timer has disappeared */
slouken@1895
   162
        if (!removed) {
slouken@1895
   163
            prev = t;
slouken@1895
   164
        }
slouken@1895
   165
    }
slouken@1895
   166
    SDL_mutexV(SDL_timer_mutex);
slouken@1
   167
}
slouken@1
   168
slouken@1895
   169
static SDL_TimerID
slouken@1895
   170
SDL_AddTimerInternal(Uint32 interval, SDL_NewTimerCallback callback,
slouken@1895
   171
                     void *param)
slouken@1028
   172
{
slouken@1895
   173
    SDL_TimerID t;
slouken@1895
   174
    t = (SDL_TimerID) SDL_malloc(sizeof(struct _SDL_TimerID));
slouken@1895
   175
    if (t) {
slouken@1895
   176
        t->interval = ROUND_RESOLUTION(interval);
slouken@1895
   177
        t->cb = callback;
slouken@1895
   178
        t->param = param;
slouken@1895
   179
        t->last_alarm = SDL_GetTicks();
slouken@1895
   180
        t->next = SDL_timers;
slouken@1895
   181
        SDL_timers = t;
slouken@1895
   182
        ++SDL_timer_running;
slouken@1895
   183
        list_changed = SDL_TRUE;
slouken@1895
   184
    }
slouken@1028
   185
#ifdef DEBUG_TIMERS
slouken@1895
   186
    printf("SDL_AddTimer(%d) = %08x num_timers = %d\n", interval, (Uint32) t,
slouken@1895
   187
           SDL_timer_running);
slouken@1028
   188
#endif
slouken@1895
   189
    return t;
slouken@1028
   190
}
slouken@1028
   191
slouken@1895
   192
SDL_TimerID
slouken@1895
   193
SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)
slouken@1
   194
{
slouken@1895
   195
    SDL_TimerID t;
slouken@1895
   196
    if (!SDL_timer_mutex) {
slouken@1895
   197
        if (SDL_timer_started) {
slouken@1895
   198
            SDL_SetError("This platform doesn't support multiple timers");
slouken@1895
   199
        } else {
slouken@1895
   200
            SDL_SetError("You must call SDL_Init(SDL_INIT_TIMER) first");
slouken@1895
   201
        }
slouken@1895
   202
        return NULL;
slouken@1895
   203
    }
slouken@1895
   204
    if (!SDL_timer_threaded) {
slouken@1895
   205
        SDL_SetError("Multiple timers require threaded events!");
slouken@1895
   206
        return NULL;
slouken@1895
   207
    }
slouken@1895
   208
    SDL_mutexP(SDL_timer_mutex);
slouken@1895
   209
    t = SDL_AddTimerInternal(interval, callback, param);
slouken@1895
   210
    SDL_mutexV(SDL_timer_mutex);
slouken@1895
   211
    return t;
slouken@1
   212
}
slouken@1
   213
slouken@1895
   214
SDL_bool
slouken@1895
   215
SDL_RemoveTimer(SDL_TimerID id)
slouken@1
   216
{
slouken@1895
   217
    SDL_TimerID t, prev = NULL;
slouken@1895
   218
    SDL_bool removed;
slouken@1
   219
slouken@1895
   220
    removed = SDL_FALSE;
slouken@1895
   221
    SDL_mutexP(SDL_timer_mutex);
slouken@1895
   222
    /* Look for id in the linked list of timers */
slouken@1895
   223
    for (t = SDL_timers; t; prev = t, t = t->next) {
slouken@1895
   224
        if (t == id) {
slouken@1895
   225
            if (prev) {
slouken@1895
   226
                prev->next = t->next;
slouken@1895
   227
            } else {
slouken@1895
   228
                SDL_timers = t->next;
slouken@1895
   229
            }
slouken@1895
   230
            SDL_free(t);
slouken@1895
   231
            --SDL_timer_running;
slouken@1895
   232
            removed = SDL_TRUE;
slouken@1895
   233
            list_changed = SDL_TRUE;
slouken@1895
   234
            break;
slouken@1895
   235
        }
slouken@1895
   236
    }
slouken@1
   237
#ifdef DEBUG_TIMERS
slouken@1895
   238
    printf("SDL_RemoveTimer(%08x) = %d num_timers = %d thread = %d\n",
slouken@1895
   239
           (Uint32) id, removed, SDL_timer_running, SDL_ThreadID());
slouken@1
   240
#endif
slouken@1895
   241
    SDL_mutexV(SDL_timer_mutex);
slouken@1895
   242
    return removed;
slouken@1
   243
}
slouken@1
   244
slouken@1
   245
/* Old style callback functions are wrapped through this */
slouken@1895
   246
static Uint32 SDLCALL
slouken@1895
   247
callback_wrapper(Uint32 ms, void *param)
slouken@1
   248
{
slouken@1895
   249
    SDL_TimerCallback func = (SDL_TimerCallback) param;
slouken@1895
   250
    return (*func) (ms);
slouken@1
   251
}
slouken@1
   252
slouken@1895
   253
int
slouken@1895
   254
SDL_SetTimer(Uint32 ms, SDL_TimerCallback callback)
slouken@1
   255
{
slouken@1895
   256
    int retval;
slouken@1
   257
slouken@1
   258
#ifdef DEBUG_TIMERS
slouken@1895
   259
    printf("SDL_SetTimer(%d)\n", ms);
slouken@1
   260
#endif
slouken@1895
   261
    retval = 0;
slouken@1028
   262
slouken@1895
   263
    if (SDL_timer_threaded) {
slouken@1895
   264
        SDL_mutexP(SDL_timer_mutex);
slouken@1895
   265
    }
slouken@1895
   266
    if (SDL_timer_running) {    /* Stop any currently running timer */
slouken@1895
   267
        if (SDL_timer_threaded) {
slouken@1895
   268
            while (SDL_timers) {
slouken@1895
   269
                SDL_TimerID freeme = SDL_timers;
slouken@1895
   270
                SDL_timers = SDL_timers->next;
slouken@1895
   271
                SDL_free(freeme);
slouken@1895
   272
            }
slouken@1895
   273
            SDL_timer_running = 0;
slouken@1895
   274
            list_changed = SDL_TRUE;
slouken@1895
   275
        } else {
slouken@1895
   276
            SDL_SYS_StopTimer();
slouken@1895
   277
            SDL_timer_running = 0;
slouken@1895
   278
        }
slouken@1895
   279
    }
slouken@1895
   280
    if (ms) {
slouken@1895
   281
        if (SDL_timer_threaded) {
slouken@1895
   282
            if (SDL_AddTimerInternal
slouken@1895
   283
                (ms, callback_wrapper, (void *) callback) == NULL) {
slouken@1895
   284
                retval = -1;
slouken@1895
   285
            }
slouken@1895
   286
        } else {
slouken@1895
   287
            SDL_timer_running = 1;
slouken@1895
   288
            SDL_alarm_interval = ms;
slouken@1895
   289
            SDL_alarm_callback = callback;
slouken@1895
   290
            retval = SDL_SYS_StartTimer();
slouken@1895
   291
        }
slouken@1895
   292
    }
slouken@1895
   293
    if (SDL_timer_threaded) {
slouken@1895
   294
        SDL_mutexV(SDL_timer_mutex);
slouken@1895
   295
    }
slouken@1028
   296
slouken@1895
   297
    return retval;
slouken@1
   298
}
slouken@1895
   299
slouken@1895
   300
/* vi: set ts=4 sw=4 expandtab: */