unofficial version 0.7.1: ui improvements
[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 static EventGenRec alarmGen =
34                 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
35 static EventGenRec *nextGen = &alarmGen;
36
37 static int myRandSeed = 1;
38
39 static long baseTimeval;
40
41 ExtFunc void AtExit(void (*handler)(void))
42 {
43 #ifdef HAS_ON_EXIT
44         on_exit((void *)handler, NULL);
45 #else
46         atexit(handler);
47 #endif
48 }
49
50 ///////////// HELP MESSAGES /////////////
51
52 ExtFunc void Header(void)
53 {
54         fprintf(stderr,
55           "NETRIS %s\t(c) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>\n"
56           "          \t(c) 2002 Shiar <shiar@shiar.org>\n\n",
57           version_string);
58 } //Header
59
60 ExtFunc void Usage(void)
61 {
62         Header();
63         fprintf(stderr,
64           "Usage: netris <options>\n"
65           "\n"
66           "  -h, --help\t\tPrint this usage information\n"
67           "  -H, --info\t\tShow distribution and warranty information\n"
68           "  -R, --rules\t\tShow game rules\n"
69           "\n"
70           "  -S, --slowterm\tDisable inverse/bold/color for slow terminals\n"
71           "  -a, --ascii\t\tUse ascii characters\n"
72           "  -C, --color=0\t\tDisable color\n"
73           "\n"
74           "  -c, --connect <host>\tInitiate connection\n"
75           "  -p, --port <port>\tSet port number (default is %d)\n"
76           "\n"
77           "  -t, --team <team>\tJoin a team (don't receive lines from your teammates)\n"
78           "  -l, --level <lvl>\tBegin at a higher level (can be used as handicap)\n"
79           "  -k, --keys <keys>\tRemap keys (default is \"%s\" for cursors)\n"
80           "  -d, --dropmode\tDrops go into drop mode\n"
81           "  -D, --instadrop\tInstant drop\n"
82           "\n"
83           "  -r, --robot <cmd>\tExecute program to control the game instead of keyboard\n"
84           "  -F, --fair-robot\tUse fair robot interface\n"
85           "\n", DEFAULT_PORT, DEFAULT_KEYS);
86 }
87
88 ExtFunc void DistInfo(void)
89 {
90         Header();
91         fprintf(stderr,
92           "This program is free software; you can redistribute it and/or modify\n"
93           "it under the terms of the GNU General Public License as published by\n"
94           "the Free Software Foundation; either version 2 of the License, or\n"
95           "(at your option) any later version.\n"
96           "\n"
97           "This program is distributed in the hope that it will be useful,\n"
98           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
99           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
100           "GNU General Public License for more details.\n"
101           "\n"
102           "You should have received a copy of the GNU General Public License\n"
103           "along with this program; if not, write to the Free Software\n"
104           "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
105           "\n");
106 } //DistInfo
107
108 ExtFunc void Rules(void)
109 {
110         Header();
111         fprintf(stderr,
112           "One player mode\n"
113           "---------------\n"
114           "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
115           "remove as many lines at once as you can). After removing ten lines\n"
116           "you go to the next level, which will be faster thus making the game\n"
117           "harder to play.\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 receives penalty lines\n"
123           "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
124           "be added to the bottom of your opponent's board respectively.\n"
125           "The junk rows have exactly one empty column, which will line up for\n"
126           "multiple rows.\n"
127           "\n"
128           "The longest surviving player wins the game.\n"
129           "\n");
130 } //Rules
131
132 ///////////// RANDOM /////////////
133
134 /*
135  * My really crappy random number generator follows
136  * Should be more than sufficient for our purposes though
137  */
138 ExtFunc void SRandom(int seed)
139 {
140         Game.seed = seed;
141         myRandSeed = seed % 31751 + 1;
142 } //SRandom
143
144 ExtFunc int Random(int min, int max1)
145 { //return a random value
146         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
147         return myRandSeed % (max1 - min) + min;
148 } //Random
149
150 ///////////// I/O /////////////
151
152 ExtFunc int MyRead(int fd, void *data, int len)
153 {
154         int result, left;
155
156         left = len;
157         while (left > 0) {
158                 result = read(fd, data, left);
159                 if (result > 0) {
160                         data = ((char *)data) + result;
161                         left -= result;
162                 }
163                 else if (errno != EINTR)
164                         return result;
165         }
166         return len;
167 } //MyRead
168
169 ExtFunc int MyWrite(int fd, void *data, int len)
170 {
171         int result, left;
172
173         left = len;
174         while (left > 0) {
175                 result = write(fd, data, left);
176                 if (result > 0) {
177                         data = ((char *)data) + result;
178                         left -= result;
179                 }
180                 else if (errno != EINTR)
181                         return result;
182         }
183         return len;
184 } //MyWrite
185
186 ///////////// TIME /////////////
187
188 ExtFunc void NormalizeTime(struct timeval *tv)
189 {
190         tv->tv_sec += tv->tv_usec / 1000000;
191         tv->tv_usec %= 1000000;
192         if (tv->tv_usec < 0) {
193                 tv->tv_usec += 1000000;
194                 --tv->tv_sec;
195         }
196 }
197
198 ExtFunc void CatchAlarm(int sig)
199 {
200         alarmGen.ready = 1;
201         signal(SIGALRM, CatchAlarm);
202 }
203
204 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
205 {
206         return E_alarm;
207 }
208
209 ExtFunc void SetTimeval(struct timeval *tv, long usec)
210 {
211         tv->tv_sec = usec / 1000000;
212         tv->tv_usec = usec % 1000000;
213 } //SetTimeval
214
215 ExtFunc long GetTimeval(struct timeval *tv)
216 {
217         return tv->tv_sec * 1000000 + tv->tv_usec;
218 } //GetTimeval
219
220 ExtFunc long AbsTimeval(void)
221 {
222         struct timeval tv;
223
224         gettimeofday(&tv, NULL);
225         return GetTimeval(&tv);
226 } //CurTimeval
227
228 ExtFunc void ResetBaseTime(void)
229 {
230         baseTimeval = AbsTimeval();
231 } //ResetBaseTime
232
233 ExtFunc void PauseTime(void)
234 {
235         baseTimeval -= AbsTimeval();
236 } //PauseTime
237
238 ExtFunc void ResumeTime(void)
239 {
240         baseTimeval += AbsTimeval();
241 } //ResumeTime
242
243 ExtFunc long CurTimeval(void)
244 {
245         struct timeval tv;
246
247         gettimeofday(&tv, NULL);
248         return GetTimeval(&tv) - baseTimeval;
249 } //CurTimeval
250
251 static long SetITimer1(long interval, long value)
252 {
253         struct itimerval it, old;
254
255         SetTimeval(&it.it_interval, interval);
256         SetTimeval(&it.it_value, value);
257         if (setitimer(ITIMER_REAL, &it, &old) < 0)
258                 die("setitimer");
259         signal(SIGALRM, CatchAlarm);
260         return GetTimeval(&old.it_value);
261 }
262
263 ExtFunc long SetITimer(long interval, long value)
264 {
265         long old;
266
267         old = SetITimer1(0, 0);
268         alarmGen.ready = 0;
269         SetITimer1(interval, value);
270         return old;
271 }
272
273 ///////////// ... /////////////
274
275 ExtFunc volatile void die(char *msg)
276 {
277         perror(msg);
278         exit(1);
279 }
280
281 ExtFunc volatile void fatal(char *msg)
282 {
283         fprintf(stderr, "%s\n", msg);
284         exit(1);
285 } //fatal
286
287 ExtFunc void BlockSignals(MySigSet *saved, ...)
288 {
289         MySigSet set;
290         va_list args;
291         int sig;
292
293         va_start(args, saved);
294 #ifdef HAS_SIGPROCMASK
295         sigemptyset(&set);
296 #else
297         set = 0;
298 #endif
299         while ((sig = va_arg(args, int))) {
300 #ifdef HAS_SIGPROCMASK
301                 sigaddset(&set, sig);
302 #else
303                 sig |= sigmask(sig);
304 #endif
305         }
306 #ifdef HAS_SIGPROCMASK
307         sigprocmask(SIG_BLOCK, &set, saved);
308 #else
309         *saved = sigblock(set);
310 #endif
311         va_end(args);
312 } //BlockSignals
313
314 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
315 {
316 #ifdef HAS_SIGPROCMASK
317         sigprocmask(SIG_SETMASK, set, saved);
318 #else
319         if (saved)
320                 *saved = sigsetmask(*set);
321         else
322                 sigsetmask(*set);
323 #endif
324 } //RestoreSignals
325
326 ///////////// EVENTS /////////////
327
328 ExtFunc void AddEventGen(EventGenRec *gen)
329 {
330         assert(gen->next == NULL);
331         gen->next = nextGen->next;
332         nextGen->next = gen;
333 } //AddEventGen
334
335 ExtFunc void RemoveEventGen(EventGenRec *gen)
336 {
337         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
338         if (gen->next) {
339                 while (nextGen->next != gen)
340                         nextGen = nextGen->next;
341                 nextGen->next = gen->next;
342                 gen->next = NULL;
343         }
344 } //RemoveEventGen
345
346 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
347 { //poll
348         int i, retry = 0;
349         fd_set fds[FT_len];
350         EventGenRec *gen;
351         int result, anyReady, anySet;
352         struct timeval tv;
353
354         for (;;) {
355                 for (i = 0; i < FT_len; ++i)
356                         FD_ZERO(&fds[i]);
357                 anyReady = anySet = 0;
358                 gen = nextGen;
359                 do {
360                         if (gen->mask & mask) {
361                                 if (gen->ready)
362                                         anyReady = 1;
363                                 if (gen->fd >= 0) {
364                                         FD_SET(gen->fd, &fds[gen->fdType]);
365                                         anySet = 1;
366                                 }
367                         }
368                         gen = gen->next;
369                 } while (gen != nextGen);
370                 if (anySet) {
371                         tv.tv_sec = 0;
372                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
373                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
374                                         &fds[FT_except], anyReady ? &tv : NULL);
375                 }
376                 else {
377                         if (retry && !anyReady)
378                                 sleep(1);
379                         result = 0;
380                 }
381                 gen = nextGen;
382                 do {
383                         if ((gen->mask & mask)
384                                         && (gen->ready || (result > 0 && gen->fd >= 0
385                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
386                                 gen->ready = 0;
387                                 event->type = gen->func(gen, event);
388                                 if (event->type != E_none) {
389                                         nextGen = gen->next;
390                                         return event->type;
391                                 }
392                         }
393                         gen = gen->next;
394                 } while (gen != nextGen);
395                 retry = 1;
396         }
397 } //WaitMyEvent
398
399 /*
400  * vi: ts=4 ai
401  * vim: noai si
402  */