1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Chris Torek and Darren F. Provine.
7 *
8 * %sccs.include.redist.c%
9 *
10 * @(#)input.c 8.1 (Berkeley) 05/31/93
11 */
12
13 /*
14 * Tetris input.
15 */
16
17 #include <sys/types.h>
18 #include <sys/time.h>
19
20 #include <errno.h>
21 #include <unistd.h>
22
23 #include "input.h"
24 #include "tetris.h"
25
26 /* return true iff the given timeval is positive */
27 #define TV_POS(tv) \
28 ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0))
29
30 /* subtract timeval `sub' from `res' */
31 #define TV_SUB(res, sub) \
32 (res)->tv_sec -= (sub)->tv_sec; \
33 (res)->tv_usec -= (sub)->tv_usec; \
34 if ((res)->tv_usec < 0) { \
35 (res)->tv_usec += 1000000; \
36 (res)->tv_sec--; \
37 }
38
39 /*
40 * Do a `read wait': select for reading from stdin, with timeout *tvp.
41 * On return, modify *tvp to reflect the amount of time spent waiting.
42 * It will be positive only if input appeared before the time ran out;
43 * otherwise it will be zero or perhaps negative.
44 *
45 * If tvp is nil, wait forever, but return if select is interrupted.
46 *
47 * Return 0 => no input, 1 => can read() from stdin
48 */
49 int
rwait(tvp)50 rwait(tvp)
51 register struct timeval *tvp;
52 {
53 int i;
54 struct timeval starttv, endtv, *s;
55 extern int errno;
56 #define NILTZ ((struct timezone *)0)
57
58 /*
59 * Someday, select() will do this for us.
60 * Just in case that day is now, and no one has
61 * changed this, we use a temporary.
62 */
63 if (tvp) {
64 (void) gettimeofday(&starttv, NILTZ);
65 endtv = *tvp;
66 s = &endtv;
67 } else
68 s = 0;
69 again:
70 i = 1;
71 switch (select(1, (fd_set *)&i, (fd_set *)0, (fd_set *)0, s)) {
72
73 case -1:
74 if (tvp == 0)
75 return (-1);
76 if (errno == EINTR)
77 goto again;
78 stop("select failed, help");
79 /* NOTREACHED */
80
81 case 0: /* timed out */
82 tvp->tv_sec = 0;
83 tvp->tv_usec = 0;
84 return (0);
85 }
86 if (tvp) {
87 /* since there is input, we may not have timed out */
88 (void) gettimeofday(&endtv, NILTZ);
89 TV_SUB(&endtv, &starttv);
90 TV_SUB(tvp, &endtv); /* adjust *tvp by elapsed time */
91 }
92 return (1);
93 }
94
95 /*
96 * `sleep' for the current turn time (using select).
97 * Eat any input that might be available.
98 */
99 void
tsleep()100 tsleep()
101 {
102 struct timeval tv;
103 char c;
104
105 tv.tv_sec = 0;
106 tv.tv_usec = fallrate;
107 while (TV_POS(&tv))
108 if (rwait(&tv) && read(0, &c, 1) != 1)
109 break;
110 }
111
112 /*
113 * Eat up any input (used at end of game).
114 */
115 void
eat_input()116 eat_input()
117 {
118 struct timeval tv;
119 char c;
120
121 do {
122 tv.tv_sec = tv.tv_usec = 0;
123 } while (rwait(&tv) && read(0, &c, 1) == 1);
124 }
125
126 /*
127 * getchar with timeout.
128 */
129 int
tgetchar()130 tgetchar()
131 {
132 static struct timeval timeleft;
133 char c;
134
135 /*
136 * Reset timeleft to fallrate whenever it is not positive.
137 * In any case, wait to see if there is any input. If so,
138 * take it, and update timeleft so that the next call to
139 * tgetchar() will not wait as long. If there is no input,
140 * make timeleft zero or negative, and return -1.
141 *
142 * Most of the hard work is done by rwait().
143 */
144 if (!TV_POS(&timeleft)) {
145 faster(); /* go faster */
146 timeleft.tv_sec = 0;
147 timeleft.tv_usec = fallrate;
148 }
149 if (!rwait(&timeleft))
150 return (-1);
151 if (read(0, &c, 1) != 1)
152 stop("end of file, help");
153 return ((int)(unsigned char)c);
154 }
155