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