xref: /original-bsd/games/tetris/input.c (revision 431be598)
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
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
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
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
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