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 (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 #ifdef SDL_TIMER_UNIX 25 26 #include <stdio.h> 27 #include <sys/time.h> 28 #include <signal.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <errno.h> 32 33 #include "SDL_timer.h" 34 #include "../SDL_timer_c.h" 35 36 /* The clock_gettime provides monotonous time, so we should use it if 37 it's available. The clock_gettime function is behind ifdef 38 for __USE_POSIX199309 39 Tommi Kyntola (tommi.kyntola (at) ray.fi) 27/09/2005 40 */ 41 #if HAVE_NANOSLEEP || HAVE_CLOCK_GETTIME 42 #include <time.h> 43 #endif 44 45 #if SDL_THREAD_PTH 46 #include <pth.h> 47 #endif 48 49 #if SDL_THREADS_DISABLED 50 #define USE_ITIMER 51 #endif 52 53 /* The first ticks value of the application */ 54 #ifdef HAVE_CLOCK_GETTIME 55 static struct timespec start; 56 #else 57 static struct timeval start; 58 #endif /* HAVE_CLOCK_GETTIME */ 59 60 61 void SDL_StartTicks(void) 62 { 63 /* Set first ticks value */ 64 #if HAVE_CLOCK_GETTIME 65 clock_gettime(CLOCK_MONOTONIC,&start); 66 #else 67 gettimeofday(&start, NULL); 68 #endif 69 } 70 71 Uint32 SDL_GetTicks (void) 72 { 73 #if HAVE_CLOCK_GETTIME 74 Uint32 ticks; 75 struct timespec now; 76 clock_gettime(CLOCK_MONOTONIC,&now); 77 ticks=(now.tv_sec-start.tv_sec)*1000+(now.tv_nsec-start.tv_nsec)/1000000; 78 return(ticks); 79 #else 80 Uint32 ticks; 81 struct timeval now; 82 gettimeofday(&now, NULL); 83 ticks=(now.tv_sec-start.tv_sec)*1000+(now.tv_usec-start.tv_usec)/1000; 84 return(ticks); 85 #endif 86 } 87 88 void SDL_Delay (Uint32 ms) 89 { 90 #if SDL_THREAD_PTH 91 pth_time_t tv; 92 tv.tv_sec = ms/1000; 93 tv.tv_usec = (ms%1000)*1000; 94 pth_nap(tv); 95 #else 96 int was_error; 97 98 #if HAVE_NANOSLEEP 99 struct timespec elapsed, tv; 100 #else 101 struct timeval tv; 102 Uint32 then, now, elapsed; 103 #endif 104 105 /* Set the timeout interval */ 106 #if HAVE_NANOSLEEP 107 elapsed.tv_sec = ms/1000; 108 elapsed.tv_nsec = (ms%1000)*1000000; 109 #else 110 then = SDL_GetTicks(); 111 #endif 112 do { 113 errno = 0; 114 115 #if HAVE_NANOSLEEP 116 tv.tv_sec = elapsed.tv_sec; 117 tv.tv_nsec = elapsed.tv_nsec; 118 was_error = nanosleep(&tv, &elapsed); 119 #else 120 /* Calculate the time interval left (in case of interrupt) */ 121 now = SDL_GetTicks(); 122 elapsed = (now-then); 123 then = now; 124 if ( elapsed >= ms ) { 125 break; 126 } 127 ms -= elapsed; 128 tv.tv_sec = ms/1000; 129 tv.tv_usec = (ms%1000)*1000; 130 131 was_error = select(0, NULL, NULL, NULL, &tv); 132 #endif /* HAVE_NANOSLEEP */ 133 } while ( was_error && (errno == EINTR) ); 134 #endif /* SDL_THREAD_PTH */ 135 } 136 137 #ifdef USE_ITIMER 138 139 static void HandleAlarm(int sig) 140 { 141 Uint32 ms; 142 143 if ( SDL_alarm_callback ) { 144 ms = (*SDL_alarm_callback)(SDL_alarm_interval); 145 if ( ms != SDL_alarm_interval ) { 146 SDL_SetTimer(ms, SDL_alarm_callback); 147 } 148 } 149 } 150 151 int SDL_SYS_TimerInit(void) 152 { 153 struct sigaction action; 154 155 /* Set the alarm handler (Linux specific) */ 156 SDL_memset(&action, 0, sizeof(action)); 157 action.sa_handler = HandleAlarm; 158 action.sa_flags = SA_RESTART; 159 sigemptyset(&action.sa_mask); 160 sigaction(SIGALRM, &action, NULL); 161 return(0); 162 } 163 164 void SDL_SYS_TimerQuit(void) 165 { 166 SDL_SetTimer(0, NULL); 167 } 168 169 int SDL_SYS_StartTimer(void) 170 { 171 struct itimerval timer; 172 173 timer.it_value.tv_sec = (SDL_alarm_interval/1000); 174 timer.it_value.tv_usec = (SDL_alarm_interval%1000)*1000; 175 timer.it_interval.tv_sec = (SDL_alarm_interval/1000); 176 timer.it_interval.tv_usec = (SDL_alarm_interval%1000)*1000; 177 setitimer(ITIMER_REAL, &timer, NULL); 178 return(0); 179 } 180 181 void SDL_SYS_StopTimer(void) 182 { 183 struct itimerval timer; 184 185 SDL_memset(&timer, 0, (sizeof timer)); 186 setitimer(ITIMER_REAL, &timer, NULL); 187 } 188 189 #else /* USE_ITIMER */ 190 191 #include "SDL_thread.h" 192 193 /* Data to handle a single periodic alarm */ 194 static int timer_alive = 0; 195 static SDL_Thread *timer = NULL; 196 197 static int RunTimer(void *unused) 198 { 199 while ( timer_alive ) { 200 if ( SDL_timer_running ) { 201 SDL_ThreadedTimerCheck(); 202 } 203 SDL_Delay(1); 204 } 205 return(0); 206 } 207 208 /* This is only called if the event thread is not running */ 209 int SDL_SYS_TimerInit(void) 210 { 211 timer_alive = 1; 212 timer = SDL_CreateThread(RunTimer, NULL); 213 if ( timer == NULL ) 214 return(-1); 215 return(SDL_SetTimerThreaded(1)); 216 } 217 218 void SDL_SYS_TimerQuit(void) 219 { 220 timer_alive = 0; 221 if ( timer ) { 222 SDL_WaitThread(timer, NULL); 223 timer = NULL; 224 } 225 } 226 227 int SDL_SYS_StartTimer(void) 228 { 229 SDL_SetError("Internal logic error: Linux uses threaded timer"); 230 return(-1); 231 } 232 233 void SDL_SYS_StopTimer(void) 234 { 235 return; 236 } 237 238 #endif /* USE_ITIMER */ 239 240 #endif /* SDL_TIMER_UNIX */ 241