1 /* $OpenBSD: ttyio.c,v 1.16 2001/05/24 03:05:27 mickey Exp $ */ 2 3 /* 4 * POSIX terminal I/O. 5 * 6 * The functions in this file 7 * negotiate with the operating system for 8 * keyboard characters, and write characters to 9 * the display in a barely buffered fashion. 10 */ 11 #include "def.h" 12 13 #include <sys/types.h> 14 #include <sys/time.h> 15 #include <sys/ioctl.h> 16 #include <fcntl.h> 17 #include <termios.h> 18 #include <term.h> 19 20 #define NOBUF 512 /* Output buffer size. */ 21 22 #ifndef TCSASOFT 23 #define TCSASOFT 0 24 #endif 25 26 char obuf[NOBUF]; /* Output buffer. */ 27 int nobuf; /* Buffer count. */ 28 struct termios oldtty; /* POSIX tty settings. */ 29 struct termios newtty; 30 int nrow; /* Terminal size, rows. */ 31 int ncol; /* Terminal size, columns. */ 32 33 /* 34 * This function gets called once, to set up the terminal. 35 * On systems w/o TCSASOFT we turn off off flow control, 36 * which isn't really the right thing to do. 37 */ 38 void 39 ttopen() 40 { 41 42 if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) 43 panic("standard input and output must be a terminal"); 44 45 if (ttraw() == FALSE) 46 panic("aborting due to terminal initialize failure"); 47 } 48 49 /* 50 * This function sets the terminal to RAW mode, as defined for the current 51 * shell. This is called both by ttopen() above and by spawncli() to 52 * get the current terminal settings and then change them to what 53 * mg expects. Thus, tty changes done while spawncli() is in effect 54 * will be reflected in mg. 55 */ 56 int 57 ttraw() 58 { 59 60 if (tcgetattr(0, &oldtty) < 0) { 61 ewprintf("ttopen can't get terminal attributes"); 62 return(FALSE); 63 } 64 (void)memcpy(&newtty, &oldtty, sizeof(newtty)); 65 /* Set terminal to 'raw' mode and ignore a 'break' */ 66 newtty.c_cc[VMIN] = 1; 67 newtty.c_cc[VTIME] = 0; 68 newtty.c_iflag |= IGNBRK; 69 newtty.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON); 70 newtty.c_oflag &= ~OPOST; 71 newtty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 72 73 #if !TCSASOFT 74 /* 75 * If we don't have TCSASOFT, force terminal to 76 * 8 bits, no parity. 77 */ 78 newtty.c_iflag &= ~ISTRIP; 79 newtty.c_cflag &= ~(CSIZE | PARENB); 80 newtty.c_cflag |= CS8; 81 #endif 82 if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) < 0) { 83 ewprintf("ttopen can't tcsetattr"); 84 return(FALSE); 85 } 86 return(TRUE); 87 } 88 89 /* 90 * This function gets called just before we go back home to the shell. 91 * Put all of the terminal parameters back. 92 * Under UN*X this just calls ttcooked(), but the ttclose() hook is in 93 * because vttidy() in display.c expects it for portability reasons. 94 */ 95 void 96 ttclose() 97 { 98 99 if (ttcooked() == FALSE) 100 panic(""); /* ttcooked() already printf'd */ 101 } 102 103 /* 104 * This function restores all terminal settings to their default values, 105 * in anticipation of exiting or suspending the editor. 106 */ 107 int 108 ttcooked() 109 { 110 111 ttflush(); 112 if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) < 0) { 113 ewprintf("ttclose can't tcsetattr"); 114 return(FALSE); 115 } 116 return(TRUE); 117 } 118 119 /* 120 * Write character to the display. Characters are buffered up, 121 * to make things a little bit more efficient. 122 */ 123 int 124 ttputc(c) 125 int c; 126 { 127 128 if (nobuf >= NOBUF) 129 ttflush(); 130 obuf[nobuf++] = c; 131 return(c); 132 } 133 134 /* 135 * Flush output. 136 */ 137 void 138 ttflush() 139 { 140 141 if (nobuf != 0) { 142 if (write(1, obuf, nobuf) != nobuf) 143 panic("ttflush write failed"); 144 nobuf = 0; 145 } 146 } 147 148 /* 149 * Read character from terminal. 150 * All 8 bits are returned, so that you can use 151 * a multi-national terminal. 152 */ 153 int 154 ttgetc() 155 { 156 char c; 157 158 while (read(0, &c, 1) != 1) 159 ; 160 return ((int) c); 161 } 162 163 /* 164 * Set the tty size. 165 * XXX - belongs in tty.c since it uses terminfo vars. 166 */ 167 void 168 setttysize() 169 { 170 #ifdef TIOCGWINSZ 171 struct winsize winsize; 172 173 if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) { 174 nrow = winsize.ws_row; 175 ncol = winsize.ws_col; 176 } else nrow = 0; 177 #endif 178 if ((nrow <= 0 || ncol <= 0) && 179 ((nrow = lines) <= 0 || (ncol = columns) <= 0)) { 180 nrow = 24; 181 ncol = 80; 182 } 183 184 /* Enforce maximum screen size. */ 185 if (nrow > NROW) 186 nrow = NROW; 187 if (ncol > NCOL) 188 ncol = NCOL; 189 } 190 191 /* 192 * Returns TRUE if there are characters waiting to be read. 193 */ 194 int 195 typeahead() 196 { 197 int x; 198 199 return((ioctl(0, FIONREAD, (char *) &x) < 0) ? 0 : x); 200 } 201 202 /* 203 * panic - just exit, as quickly as we can. 204 */ 205 void 206 panic(s) 207 char *s; 208 { 209 210 (void) fputs("panic: ", stderr); 211 (void) fputs(s, stderr); 212 (void) fputc('\n', stderr); 213 exit(1); 214 } 215 216 /* 217 * This function returns FALSE if any characters have showed up on the 218 * tty before 'msec' miliseconds. 219 */ 220 int 221 ttwait(int msec) 222 { 223 fd_set readfds; 224 struct timeval tmout; 225 226 FD_ZERO(&readfds); 227 FD_SET(0, &readfds); 228 229 tmout.tv_sec = msec/1000; 230 tmout.tv_usec = msec - tmout.tv_sec * 1000; 231 232 if ((select(1, &readfds, NULL, NULL, &tmout)) == 0) 233 return (TRUE); 234 return (FALSE); 235 } 236