initial interface requests
[netris.git] / util.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program 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
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: util.c,v 1.29 1999/05/16 06:56:33 mhw Exp $
20  */
21
22 #include "netris.h"
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <netdb.h>
30 #include <errno.h>
31
32 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
33
34 static EventGenRec alarmGen =
35                 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
36 static EventGenRec *nextGen = &alarmGen;
37
38 static myRandSeed = 1;
39
40 static struct timeval baseTimeval;
41
42 ExtFunc void InitUtil(void)
43 {
44         if (initSeed)
45                 SRandom(initSeed);
46         else
47                 SRandom(time(0));
48         signal(SIGINT, CatchInt);
49         ResetBaseTime();
50 }
51
52 ExtFunc void ResetBaseTime(void)
53 {
54         gettimeofday(&baseTimeval, NULL);
55 }
56
57 ExtFunc void AtExit(void (*handler)(void))
58 {
59 #ifdef HAS_ON_EXIT
60         on_exit((void *)handler, NULL);
61 #else
62         atexit(handler);
63 #endif
64 }
65
66 ExtFunc void Usage(void)
67 {
68         fprintf(stderr,
69           "Netris version %s (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>\n"
70           "Usage: netris <options>\n"
71           "  -h         Print usage information\n"
72           "  -w         Wait for connection\n"
73           "  -c <host>  Initiate connection\n"
74           "  -p <port>  Set port number (default is %d)\n"
75           "  -k <keys>  Remap keys.  The argument is a prefix of the string\n"
76           "               containing the keys in order: left, rotate, right, drop,\n"
77           "               down-faster, toggle-spying, pause, faster, redraw.\n"
78           "               \"^\" prefixes controls.  (default is \"%s\")\n"
79           "  -i <sec>   Set the step-down interval, in seconds\n"
80           "  -r <robot> Execute <robot> (a command) as a robot controlling\n"
81           "               the game instead of the keyboard\n"
82           "  -F         Use fair robot interface\n"
83           "  -s <seed>  Start with given random seed\n"
84           "  -d         Drops go into drop mode\n"
85           "               This means that sliding off a cliff after a drop causes\n"
86           "               another drop automatically\n"
87           "  -D         Instant drop\n"
88           "  -S         Disable inverse/bold/color for slow terminals\n"
89           "  -C         Disable color\n"
90           "  -H         Show distribution and warranty information\n"
91           "  -R         Show rules\n",
92           version_string, DEFAULT_PORT, DEFAULT_KEYS);
93 }
94
95 ExtFunc void DistInfo(void)
96 {
97         fprintf(stderr,
98           "Netris version %s (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>\n"
99           "\n"
100           "This program is free software; you can redistribute it and/or modify\n"
101           "it under the terms of the GNU General Public License as published by\n"
102           "the Free Software Foundation; either version 2 of the License, or\n"
103           "(at your option) any later version.\n"
104           "\n"
105           "This program is distributed in the hope that it will be useful,\n"
106           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
107           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
108           "GNU General Public License for more details.\n"
109           "\n"
110           "You should have received a copy of the GNU General Public License\n"
111           "along with this program; if not, write to the Free Software\n"
112           "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n",
113           version_string);
114 }
115
116 ExtFunc void Rules(void)
117 {
118         fprintf(stderr,
119           "Netris version %s rules\n"
120           "\n"
121           "Two player mode\n"
122           "---------------\n"
123           "It's just like normal T*tris except that when you clear more than\n"
124           "one row with a single piece, the other player's board is moved up\n"
125           "and junk rows are added to the bottom.  If you clear 2, 3 or 4\n"
126           "rows, 1, 2 or 4 junk rows are added to your opponent's board,\n"
127           "respectively.  The junk rows have exactly one empty column.\n"
128           "For each group of junk rows given, the empty columns will line\n"
129           "up.  This is intentional.\n"
130           "\n"
131           "The longest surviving player wins the game.\n"
132           "\n"
133           "One player mode\n"
134           "---------------\n"
135           "This mode is currently very boring, because there's no scoring\n"
136           "and it never gets any faster.  This will be rectified at some point.\n"
137           "I'm not very motivated to do it right now because I'm sick of one\n"
138           "player T*tris.  For now, use the \"f\" key (by default) to make the\n"
139           "game go faster.  Speedups cannot be reversed for the remainder of\n"
140           "the game.\n",
141           version_string);
142 }
143
144 /*
145  * My really crappy random number generator follows
146  * Should be more than sufficient for our purposes though
147  */
148 ExtFunc void SRandom(int seed)
149 {
150         initSeed = seed;
151         myRandSeed = seed % 31751 + 1;
152 }
153
154 ExtFunc int Random(int min, int max1)
155 {
156         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
157         return myRandSeed % (max1 - min) + min;
158 }
159
160 ExtFunc int MyRead(int fd, void *data, int len)
161 {
162         int result, left;
163
164         left = len;
165         while (left > 0) {
166                 result = read(fd, data, left);
167                 if (result > 0) {
168                         data = ((char *)data) + result;
169                         left -= result;
170                 }
171                 else if (errno != EINTR)
172                         return result;
173         }
174         return len;
175 }
176
177 ExtFunc int MyWrite(int fd, void *data, int len)
178 {
179         int result, left;
180
181         left = len;
182         while (left > 0) {
183                 result = write(fd, data, left);
184                 if (result > 0) {
185                         data = ((char *)data) + result;
186                         left -= result;
187                 }
188                 else if (errno != EINTR)
189                         return result;
190         }
191         return len;
192 }
193
194 ExtFunc void NormalizeTime(struct timeval *tv)
195 {
196         tv->tv_sec += tv->tv_usec / 1000000;
197         tv->tv_usec %= 1000000;
198         if (tv->tv_usec < 0) {
199                 tv->tv_usec += 1000000;
200                 --tv->tv_sec;
201         }
202 }
203
204 ExtFunc void CatchInt(int sig)
205 {
206         exit(0);
207 }
208
209 ExtFunc void CatchAlarm(int sig)
210 {
211         alarmGen.ready = 1;
212         signal(SIGALRM, CatchAlarm);
213 }
214
215 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
216 {
217         return E_alarm;
218 }
219
220 ExtFunc long CurTimeval(void)
221 {
222         struct timeval tv;
223
224         gettimeofday(&tv, NULL);
225         tv.tv_sec -= baseTimeval.tv_sec;
226         tv.tv_usec -= baseTimeval.tv_usec;
227         return GetTimeval(&tv);
228 }
229
230 ExtFunc void SetTimeval(struct timeval *tv, long usec)
231 {
232         tv->tv_sec = usec / 1000000;
233         tv->tv_usec = usec % 1000000;
234 }
235
236 ExtFunc long GetTimeval(struct timeval *tv)
237 {
238         return tv->tv_sec * 1000000 + tv->tv_usec;
239 }
240
241 static long SetITimer1(long interval, long value)
242 {
243         struct itimerval it, old;
244
245         SetTimeval(&it.it_interval, interval);
246         SetTimeval(&it.it_value, value);
247         if (setitimer(ITIMER_REAL, &it, &old) < 0)
248                 die("setitimer");
249         signal(SIGALRM, CatchAlarm);
250         return GetTimeval(&old.it_value);
251 }
252
253 ExtFunc long SetITimer(long interval, long value)
254 {
255         long old;
256
257         old = SetITimer1(0, 0);
258         alarmGen.ready = 0;
259         SetITimer1(interval, value);
260         return old;
261 }
262
263 ExtFunc volatile void die(char *msg)
264 {
265         perror(msg);
266         exit(1);
267 }
268
269 ExtFunc volatile void fatal(char *msg)
270 {
271         fprintf(stderr, "%s\n", msg);
272         exit(1);
273 }
274
275 ExtFunc void BlockSignals(MySigSet *saved, ...)
276 {
277         MySigSet set;
278         va_list args;
279         int sig;
280
281         va_start(args, saved);
282 #ifdef HAS_SIGPROCMASK
283         sigemptyset(&set);
284 #else
285         set = 0;
286 #endif
287         while ((sig = va_arg(args, int))) {
288 #ifdef HAS_SIGPROCMASK
289                 sigaddset(&set, sig);
290 #else
291                 sig |= sigmask(sig);
292 #endif
293         }
294 #ifdef HAS_SIGPROCMASK
295         sigprocmask(SIG_BLOCK, &set, saved);
296 #else
297         *saved = sigblock(set);
298 #endif
299         va_end(args);
300 }
301
302 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
303 {
304 #ifdef HAS_SIGPROCMASK
305         sigprocmask(SIG_SETMASK, set, saved);
306 #else
307         if (saved)
308                 *saved = sigsetmask(*set);
309         else
310                 sigsetmask(*set);
311 #endif
312 }
313
314 ExtFunc void AddEventGen(EventGenRec *gen)
315 {
316         assert(gen->next == NULL);
317         gen->next = nextGen->next;
318         nextGen->next = gen;
319 }
320
321 ExtFunc void RemoveEventGen(EventGenRec *gen)
322 {
323         /* assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
324         if (gen->next) {
325                 while (nextGen->next != gen)
326                         nextGen = nextGen->next;
327                 nextGen->next = gen->next;
328                 gen->next = NULL;
329         }
330 }
331
332 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
333 {
334         int i, retry = 0;
335         fd_set fds[FT_len];
336         EventGenRec *gen;
337         int result, anyReady, anySet;
338         struct timeval tv;
339
340         /* XXX In certain circumstances, this routine does polling */
341         for (;;) {
342                 for (i = 0; i < FT_len; ++i)
343                         FD_ZERO(&fds[i]);
344                 anyReady = anySet = 0;
345                 gen = nextGen;
346                 do {
347                         if (gen->mask & mask) {
348                                 if (gen->ready)
349                                         anyReady = 1;
350                                 if (gen->fd >= 0) {
351                                         FD_SET(gen->fd, &fds[gen->fdType]);
352                                         anySet = 1;
353                                 }
354                         }
355                         gen = gen->next;
356                 } while (gen != nextGen);
357                 if (anySet) {
358                         tv.tv_sec = 0;
359                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
360                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
361                                         &fds[FT_except], anyReady ? &tv : NULL);
362                 }
363                 else {
364                         if (retry && !anyReady)
365                                 sleep(1);
366                         result = 0;
367                 }
368                 gen = nextGen;
369                 do {
370                         if ((gen->mask & mask)
371                                         && (gen->ready || (result > 0 && gen->fd >= 0
372                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
373                                 gen->ready = 0;
374                                 event->type = gen->func(gen, event);
375                                 if (event->type != E_none) {
376                                         nextGen = gen->next;
377                                         return event->type;
378                                 }
379                         }
380                         gen = gen->next;
381                 } while (gen != nextGen);
382                 retry = 1;
383         }
384 }
385
386 /*
387  * vi: ts=4 ai
388  * vim: noai si
389  */