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