1 /* queue.c - a queue of events to happen
2  *
3  * Copyright 1999, 2000  Jochen Voss  */
4 
5 static const  char  rcsid[] = "$Id: queue.c 4839 2003-04-13 16:50:02Z voss $";
6 
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #ifdef _XOPEN_SOURCE
12 #define _XOPEN_SOURCE_EXTENDED 1
13 #endif
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #ifdef HAVE_SYS_SELECT_H
21 #include <sys/select.h>
22 #endif
23 #include <unistd.h>
24 #include <math.h>
25 #ifdef HAVE_ERRNO_H
26 #include <errno.h>
27 #else
28 extern  int  errno;
29 #endif
30 #include <assert.h>
31 
32 #if defined(__hp9000s800)
33 #include <stdarg.h>
34 #endif
35 
36 #include "moon-buggy.h"
37 
38 
39 /* The queue of events */
40 struct event {
41   struct event *next;
42   game_time  t;			/* time, when the event should happen */
43   callback_fn  callback;	/* function to call */
44   void *client_data;		/* argument to pass */
45 };
46 static  struct event *queue;
47 
48 /**********************************************************************
49  * convert between game time and real time
50  */
51 
52 typedef  double  real_time;
53 
54 /* The type `game_time' is relative to `time_base'.  */
55 static  double  time_base;
56 
57 static real_time
to_real(game_time t)58 to_real (game_time t)
59 {
60   return  t + time_base;
61 }
62 
63 static game_time
to_game(real_time t)64 to_game (real_time t)
65 {
66   return  t - time_base;
67 }
68 
69 game_time
current_time(void)70 current_time (void)
71 {
72   return  to_game (vclock ());
73 }
74 
75 /**********************************************************************
76  * wait for timeouts or keyboard input
77  */
78 
79 static int
my_select(struct timeval * timeout)80 my_select (struct timeval *timeout)
81 /* Wait until input is ready on stdin or a timeout is reached.
82  * TIMEOUT has the same meaning, as is has for the `select' system
83  * call.  The return value is 0 if we return because of a timeout,
84  * positive if input is ready, and negative if a signal occured.  In
85  * the latter case we must calculate a new TIMEOUT value and call
86  * `my_select' again.  */
87 {
88   fd_set  rfds;
89   int  res;
90 
91   /* Watch stdin (fd 0) to see when it has input. */
92   FD_ZERO (&rfds);
93   FD_SET (0, &rfds);
94 
95   if (handle_signals ())  return -1;
96   res = select (FD_SETSIZE, &rfds, NULL, NULL, timeout);
97   if (res < 0) {
98     if (errno == EINTR) {
99       handle_signals ();
100     } else {
101       fatal ("Select failed: %s", strerror (errno));
102     }
103   }
104   return  res;
105 }
106 
107 static int
key_ready(void)108 key_ready (void)
109 /* Return a positive value iff keyboard input is ready.  */
110 {
111   int  res;
112 
113   do {
114     struct timeval  ancient_time;
115 
116     ancient_time.tv_sec = 0;
117     ancient_time.tv_usec = 0;
118     res = my_select (&ancient_time);
119   } while (res < 0);
120   return  res;
121 }
122 
123 static void
wait_for_key(void)124 wait_for_key (void)
125 {
126   int  res;
127 
128   do {
129     res = my_select (NULL);
130   } while (res < 0);
131 }
132 
133 static int
wait_until(game_time t,real_time * t_return)134 wait_until (game_time t, real_time *t_return)
135 /* Wait for time *T or the next key press, whichever comes first.
136  * Return a positive value, if a key was pressed, and 0 else.
137  * Set *T_RETURN to the return time.  */
138 {
139   double  start;
140   int  res;
141 
142   do {
143     double  dt, sec, usec;
144     struct timeval  tv;
145 
146     start = vclock ();
147     dt = to_real(t) - start;
148     if (dt <= 0) {
149       *t_return = start;
150       return  key_ready ();
151     }
152 
153     usec = 1e6 * modf (dt, &sec) + 0.5;
154     tv.tv_sec = sec + 0.5;
155     tv.tv_usec = usec + 0.5;
156     res = my_select (&tv);
157   } while (res < 0);
158   *t_return = vclock ();
159 
160   return  res;
161 }
162 
163 static void
drain_input(void)164 drain_input (void)
165 /* Discard all data from the input queue.  */
166 {
167   char  buffer [16];
168   int  oldflags;
169 
170   oldflags = fcntl (0, F_GETFL, 0);
171   if (oldflags < 0) {
172     fatal ("Cannot get file status flags (%s)", strerror (errno));
173   }
174   fcntl (0, F_SETFL, oldflags|O_NONBLOCK);
175   while (read (0, buffer, 16) == 16)
176     ;
177   fcntl (0, F_SETFL, oldflags);
178 }
179 
180 void
clock_reset(void)181 clock_reset (void)
182 /* Adjust the clock to make 0 the current time.  */
183 {
184   time_base = vclock ();
185 }
186 
187 static void
dummy_h(game_time t,void * client_data)188 dummy_h (game_time t, void *client_data)
189 /* This function is a possible callback argument to `add_event'.
190  * It does nothing.  */
191 {
192   return;
193 }
194 
195 void
clock_freeze(void)196 clock_freeze (void)
197 /* Prepare to freeze the game's clock.
198  * This function should be called before the game is resumed.
199  * Afterwards you should use `clock_thaw ()' to restart the
200  * game in the current state.  If the next event is less then 0.1
201  * seconds in the future, we add some gap to allow for 0.1 seconds
202  * pause after the restart.  */
203 {
204   game_time  t;
205 
206   remove_event (dummy_h);
207   t = current_time ();
208   if (queue && t >= queue->t - 0.1)  t = queue->t - 0.1;
209   add_event (t, dummy_h, NULL);
210 }
211 
212 void
clock_thaw(void)213 clock_thaw (void)
214 /* Adjust the clock to make the next event occur immediately.
215  * The queue must contain at least one element.  */
216 {
217   assert (queue);
218   time_base = vclock () - queue->t;
219 }
220 
221 void
clear_queue(void)222 clear_queue (void)
223 /* Remove all events from the queue.  */
224 {
225   struct event *ev;
226 
227   ev = queue, queue = NULL;
228   while (ev) {
229     struct event *old = ev;
230     ev = old->next;
231     free (old);
232   }
233   drain_input ();
234 }
235 
236 void
add_event(game_time t,callback_fn callback,void * client_data)237 add_event (game_time t, callback_fn callback, void *client_data)
238 /* Add a new event for time T to the queue.
239  * The event calls function CALLBACK with argument CLIENT_DATA.  */
240 {
241   struct event **evp;
242   struct event *ev;
243 
244   evp = &queue;
245   while (*evp && (*evp)->t <= t)  evp = &((*evp)->next);
246 
247   ev = xmalloc (sizeof (struct event));
248   ev->next = *evp;
249   ev->t = t;
250   ev->callback = callback;
251   ev->client_data = client_data;
252 
253   *evp = ev;
254 }
255 
256 void
remove_event(callback_fn callback)257 remove_event (callback_fn callback)
258 /* Remove all events from the queue, which would call CALLBACK.  */
259 {
260   struct event **evp;
261 
262   evp = &queue;
263   while (*evp) {
264     struct event *ev = *evp;
265     if (ev->callback == callback) {
266       *evp = (*evp)->next;
267       free (ev);
268     } else {
269       evp = &((*evp)->next);
270     }
271   }
272 }
273 
274 void
remove_client_data(void * client_data)275 remove_client_data (void *client_data)
276 /* Remove all events from the queue, which refer to CLIENT_DATA.  */
277 {
278   struct event **evp;
279 
280   evp = &queue;
281   while (*evp) {
282     struct event *ev = *evp;
283     if (ev->client_data == client_data) {
284       *evp = (*evp)->next;
285       free (ev);
286     } else {
287       evp = &((*evp)->next);
288     }
289   }
290 }
291 
292 /**********************************************************************
293  * the main loop
294  */
295 
296 static  int  exit_flag;
297 
298 void
quit_main_loop(void)299 quit_main_loop (void)
300 {
301   exit_flag = 1;
302 }
303 
304 void
main_loop(void)305 main_loop (void)
306 {
307   clock_reset ();
308   exit_flag = 0;
309 
310   while (! exit_flag) {
311     int  retval;
312     double  t;
313 
314     mode_update ();
315 
316     if (queue) {
317       retval = wait_until (queue->t, &t);
318     } else {
319       wait_for_key ();
320       t = vclock ();
321       retval = 1;
322     }
323 
324     if (retval>0) {
325       int  meaning = read_key ();
326       if (meaning != -1) {
327 	if (! mode_keypress (to_game (t), meaning))  beep ();
328       }
329     }
330 
331     while (queue && queue->t <= current_time ()) {
332       struct event *ev = queue;
333       queue = queue->next;
334 
335       ev->callback (ev->t, ev->client_data);
336       free (ev);
337     }
338   }
339 }
340 
341 /**********************************************************************
342  * some handlers for the main loop above
343  */
344 
345 void
print_hint_h(game_time t,void * client_data)346 print_hint_h (game_time t, void *client_data)
347 /* This function is a possible callback argument to `add_event'.
348  * It causes the level hint (const char *)CLIENT_DATA to be
349  * displayed for 4 seconds.  */
350 {
351   print_hint (client_data);
352   remove_event (clear_hint_h);
353   add_event (t+4, clear_hint_h, NULL);
354 }
355 
356 void
clear_hint_h(game_time t,void * client_data)357 clear_hint_h (game_time t, void *client_data)
358 /* This function is a possible callback argument to `add_event'.
359  * It causes the screen's hint area to be cleared.
360  * The arguments T and CLIENT_DATA are ignored.  */
361 {
362   wmove (moon, LINES-11, 0);
363   wclrtoeol (moon);
364   wnoutrefresh (moon);
365 }
366