1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Unix timer module.
12  *
13  *      By Peter Wang.
14  *
15  *      _unix_rest by Elias Pschernig.
16  *
17  *      See readme.txt for copyright information.
18  */
19 
20 
21 #include "allegro.h"
22 #include "allegro/platform/aintunix.h"
23 #include <sys/time.h>
24 
25 
26 #ifndef ALLEGRO_MACOSX
27 
28 /* System drivers provide their own lists, so this is just to keep the
29  * Allegro framework happy.  */
30 _DRIVER_INFO _timer_driver_list[] = {
31    { 0, 0, 0 }
32 };
33 
34 #endif
35 
36 
37 
38 /* timeval_subtract:
39  *  Subtract the `struct timeval' values X and Y, storing the result
40  *  in RESULT.  Return 1 if the difference is negative, otherwise 0.
41  *
42  *  This function is from the glibc manual.  It handles weird platforms
43  *  where the tv_sec is unsigned.
44  */
timeval_subtract(struct timeval * result,struct timeval * x,struct timeval * y)45 static int timeval_subtract(struct timeval *result,
46 			    struct timeval *x,
47 			    struct timeval *y)
48 {
49    /* Perform the carry for the later subtraction by updating Y. */
50    if (x->tv_usec < y->tv_usec) {
51       int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
52       y->tv_usec -= 1000000 * nsec;
53       y->tv_sec += nsec;
54    }
55    if (x->tv_usec - y->tv_usec > 1000000) {
56       int nsec = (x->tv_usec - y->tv_usec) / 1000000;
57       y->tv_usec += 1000000 * nsec;
58       y->tv_sec -= nsec;
59    }
60 
61    /* Compute the time remaining to wait.
62     * `tv_usec' is certainly positive. */
63    result->tv_sec = x->tv_sec - y->tv_sec;
64    result->tv_usec = x->tv_usec - y->tv_usec;
65    /* Return 1 if result is negative. */
66    return x->tv_sec < y->tv_sec;
67 }
68 
69 
70 
_unix_rest(unsigned int ms,void (* callback)(void))71 void _unix_rest(unsigned int ms, void (*callback) (void))
72 {
73    if (callback) {
74       struct timeval tv, tv_end;
75 
76       gettimeofday (&tv_end, NULL);
77       tv_end.tv_usec += ms * 1000;
78       tv_end.tv_sec  += (tv_end.tv_usec / 1000000L);
79       tv_end.tv_usec %= 1000000L;
80 
81       while (1)
82       {
83          (*callback)();
84          gettimeofday (&tv, NULL);
85          if (tv.tv_sec > tv_end.tv_sec)
86             break;
87          if (tv.tv_sec == tv_end.tv_sec && tv.tv_usec >= tv_end.tv_usec)
88              break;
89       }
90    }
91    else {
92       struct timeval now;
93       struct timeval end;
94       struct timeval delay;
95       int result;
96 
97       gettimeofday(&now, NULL);
98 
99       end = now;
100       end.tv_usec += ms * 1000;
101       end.tv_sec  += (end.tv_usec / 1000000L);
102       end.tv_usec %= 1000000L;
103 
104       while (1) {
105 	 if (timeval_subtract(&delay, &end, &now))
106 	    break;
107 
108 #ifdef ALLEGRO_MACOSX
109 	 result = usleep((delay.tv_sec * 1000000L) + delay.tv_usec);
110 #else
111 	 result = select(0, NULL, NULL, NULL, &delay);
112 #endif
113 	 if (result == 0)	/* ok */
114 	    break;
115 	 if ((result != -1) || (errno != EINTR))
116 	    break;
117 
118 	 /* interrupted */
119 	 gettimeofday(&now, NULL);
120       }
121    }
122 }
123