Imported Upstream version 0.4
[pkg/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.27 1996/02/09 08:22:23 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,1995,1996  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           "  -S         Disable standout mode (inverse/bold) for slow terminals\n"
88           "  -H         Show distribution and warranty information\n"
89           "  -R         Show rules\n",
90           version_string, DEFAULT_PORT, DEFAULT_KEYS);
91 }
92
93 ExtFunc void DistInfo(void)
94 {
95         fprintf(stderr,
96           "Netris version %s (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>\n"
97           "\n"
98           "This program is free software; you can redistribute it and/or modify\n"
99           "it under the terms of the GNU General Public License as published by\n"
100           "the Free Software Foundation; either version 2 of the License, or\n"
101           "(at your option) any later version.\n"
102           "\n"
103           "This program is distributed in the hope that it will be useful,\n"
104           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
105           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
106           "GNU General Public License for more details.\n"
107           "\n"
108           "You should have received a copy of the GNU General Public License\n"
109           "along with this program; if not, write to the Free Software\n"
110           "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n",
111           version_string);
112 }
113
114 ExtFunc void Rules(void)
115 {
116         fprintf(stderr,
117           "Netris version %s rules\n"
118           "\n"
119           "Two player mode\n"
120           "---------------\n"
121           "It's just like normal T*tris except that when you clear more than\n"
122           "one row with a single piece, the other player's board is moved up\n"
123           "and junk rows are added to the bottom.  If you clear 2, 3 or 4\n"
124           "rows, 1, 2 or 4 junk rows are added to your opponent's board,\n"
125           "respectively.  The junk rows have exactly one empty column.\n"
126           "For each group of junk rows given, the empty columns will line\n"
127           "up.  This is intentional.\n"
128           "\n"
129           "The longest surviving player wins the game.\n"
130           "\n"
131           "One player mode\n"
132           "---------------\n"
133           "This mode is currently very boring, because there's no scoring\n"
134           "and it never gets any faster.  This will be rectified at some point.\n"
135           "I'm not very motivated to do it right now because I'm sick of one\n"
136           "player T*tris.  For now, use the \"f\" key (by default) to make the\n"
137           "game go faster.  Speedups cannot be reversed for the remainder of\n"
138           "the game.\n",
139           version_string);
140 }
141
142 /*
143  * My really crappy random number generator follows
144  * Should be more than sufficient for our purposes though
145  */
146 ExtFunc void SRandom(int seed)
147 {
148         initSeed = seed;
149         myRandSeed = seed % 31751 + 1;
150 }
151
152 ExtFunc int Random(int min, int max1)
153 {
154         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
155         return myRandSeed % (max1 - min) + min;
156 }
157
158 ExtFunc int MyRead(int fd, void *data, int len)
159 {
160         int result, left;
161
162         left = len;
163         while (left > 0) {
164                 result = read(fd, data, left);
165                 if (result > 0) {
166                         data = ((char *)data) + result;
167                         left -= result;
168                 }
169                 else if (errno != EINTR)
170                         return result;
171         }
172         return len;
173 }
174
175 ExtFunc int MyWrite(int fd, void *data, int len)
176 {
177         int result, left;
178
179         left = len;
180         while (left > 0) {
181                 result = write(fd, data, left);
182                 if (result > 0) {
183                         data = ((char *)data) + result;
184                         left -= result;
185                 }
186                 else if (errno != EINTR)
187                         return result;
188         }
189         return len;
190 }
191
192 ExtFunc void NormalizeTime(struct timeval *tv)
193 {
194         tv->tv_sec += tv->tv_usec / 1000000;
195         tv->tv_usec %= 1000000;
196         if (tv->tv_usec < 0) {
197                 tv->tv_usec += 1000000;
198                 --tv->tv_sec;
199         }
200 }
201
202 ExtFunc void CatchInt(int sig)
203 {
204         exit(0);
205 }
206
207 ExtFunc void CatchAlarm(int sig)
208 {
209         alarmGen.ready = 1;
210         signal(SIGALRM, CatchAlarm);
211 }
212
213 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
214 {
215         return E_alarm;
216 }
217
218 ExtFunc long CurTimeval(void)
219 {
220         struct timeval tv;
221
222         gettimeofday(&tv, NULL);
223         tv.tv_sec -= baseTimeval.tv_sec;
224         tv.tv_usec -= baseTimeval.tv_usec;
225         return GetTimeval(&tv);
226 }
227
228 ExtFunc void SetTimeval(struct timeval *tv, long usec)
229 {
230         tv->tv_sec = usec / 1000000;
231         tv->tv_usec = usec % 1000000;
232 }
233
234 ExtFunc long GetTimeval(struct timeval *tv)
235 {
236         return tv->tv_sec * 1000000 + tv->tv_usec;
237 }
238
239 static long SetITimer1(long interval, long value)
240 {
241         struct itimerval it, old;
242
243         SetTimeval(&it.it_interval, interval);
244         SetTimeval(&it.it_value, value);
245         if (setitimer(ITIMER_REAL, &it, &old) < 0)
246                 die("setitimer");
247         signal(SIGALRM, CatchAlarm);
248         return GetTimeval(&old.it_value);
249 }
250
251 ExtFunc long SetITimer(long interval, long value)
252 {
253         long old;
254
255         old = SetITimer1(0, 0);
256         alarmGen.ready = 0;
257         SetITimer1(interval, value);
258         return old;
259 }
260
261 ExtFunc volatile void die(char *msg)
262 {
263         perror(msg);
264         exit(1);
265 }
266
267 ExtFunc volatile void fatal(char *msg)
268 {
269         fprintf(stderr, "%s\n", msg);
270         exit(1);
271 }
272
273 ExtFunc void BlockSignals(MySigSet *saved, ...)
274 {
275         MySigSet set;
276         va_list args;
277         int sig;
278
279         va_start(args, saved);
280 #ifdef HAS_SIGPROCMASK
281         sigemptyset(&set);
282 #else
283         set = 0;
284 #endif
285         while ((sig = va_arg(args, int))) {
286 #ifdef HAS_SIGPROCMASK
287                 sigaddset(&set, sig);
288 #else
289                 sig |= sigmask(sig);
290 #endif
291         }
292 #ifdef HAS_SIGPROCMASK
293         sigprocmask(SIG_BLOCK, &set, saved);
294 #else
295         *saved = sigblock(set);
296 #endif
297         va_end(args);
298 }
299
300 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
301 {
302 #ifdef HAS_SIGPROCMASK
303         sigprocmask(SIG_SETMASK, set, saved);
304 #else
305         if (saved)
306                 *saved = sigsetmask(*set);
307         else
308                 sigsetmask(*set);
309 #endif
310 }
311
312 ExtFunc void AddEventGen(EventGenRec *gen)
313 {
314         assert(gen->next == NULL);
315         gen->next = nextGen->next;
316         nextGen->next = gen;
317 }
318
319 ExtFunc void RemoveEventGen(EventGenRec *gen)
320 {
321         assert(gen->next != NULL);
322         while (nextGen->next != gen)
323                 nextGen = nextGen->next;
324         nextGen->next = gen->next;
325         gen->next = NULL;
326 }
327
328 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
329 {
330         int i, retry = 0;
331         fd_set fds[FT_len];
332         EventGenRec *gen;
333         int result, anyReady, anySet;
334         struct timeval tv;
335
336         /* XXX In certain circumstances, this routine does polling */
337         for (;;) {
338                 for (i = 0; i < FT_len; ++i)
339                         FD_ZERO(&fds[i]);
340                 anyReady = anySet = 0;
341                 gen = nextGen;
342                 do {
343                         if (gen->mask & mask) {
344                                 if (gen->ready)
345                                         anyReady = 1;
346                                 if (gen->fd >= 0) {
347                                         FD_SET(gen->fd, &fds[gen->fdType]);
348                                         anySet = 1;
349                                 }
350                         }
351                         gen = gen->next;
352                 } while (gen != nextGen);
353                 if (anySet) {
354                         tv.tv_sec = 0;
355                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
356                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
357                                         &fds[FT_except], anyReady ? &tv : NULL);
358                 }
359                 else {
360                         if (retry && !anyReady)
361                                 sleep(1);
362                         result = 0;
363                 }
364                 gen = nextGen;
365                 do {
366                         if ((gen->mask & mask)
367                                         && (gen->ready || (result > 0 && gen->fd >= 0
368                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
369                                 gen->ready = 0;
370                                 event->type = gen->func(gen, event);
371                                 if (event->type != E_none) {
372                                         nextGen = gen->next;
373                                         return event->type;
374                                 }
375                         }
376                         gen = gen->next;
377                 } while (gen != nextGen);
378                 retry = 1;
379         }
380 }
381
382 /*
383  * vi: ts=4 ai
384  * vim: noai si
385  */