1 /* $OpenBSD: ttyio.c,v 1.33 2013/01/19 21:22:28 florian Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * POSIX terminal I/O. 7 * 8 * The functions in this file negotiate with the operating system for 9 * keyboard characters, and write characters to the display in a barely 10 * buffered fashion. 11 */ 12 #include "def.h" 13 14 #include <sys/types.h> 15 #include <sys/time.h> 16 #include <sys/ioctl.h> 17 #include <fcntl.h> 18 #include <termios.h> 19 #include <term.h> 20 21 #define NOBUF 512 /* Output buffer size. */ 22 23 #ifndef TCSASOFT 24 #define TCSASOFT 0 25 #endif 26 27 int ttstarted; 28 char obuf[NOBUF]; /* Output buffer. */ 29 size_t nobuf; /* Buffer count. */ 30 struct termios oldtty; /* POSIX tty settings. */ 31 struct termios newtty; 32 int nrow; /* Terminal size, rows. */ 33 int ncol; /* Terminal size, columns. */ 34 35 /* 36 * This function gets called once, to set up the terminal. 37 * On systems w/o TCSASOFT we turn off off flow control, 38 * which isn't really the right thing to do. 39 */ 40 void 41 ttopen(void) 42 { 43 if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) 44 panic("standard input and output must be a terminal"); 45 46 if (ttraw() == FALSE) 47 panic("aborting due to terminal initialize failure"); 48 } 49 50 /* 51 * This function sets the terminal to RAW mode, as defined for the current 52 * shell. This is called both by ttopen() above and by spawncli() to 53 * get the current terminal settings and then change them to what 54 * mg expects. Thus, tty changes done while spawncli() is in effect 55 * will be reflected in mg. 56 */ 57 int 58 ttraw(void) 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 ttstarted = 1; 87 88 return (TRUE); 89 } 90 91 /* 92 * This function gets called just before we go back home to the shell. 93 * Put all of the terminal parameters back. 94 * Under UN*X this just calls ttcooked(), but the ttclose() hook is in 95 * because vttidy() in display.c expects it for portability reasons. 96 */ 97 void 98 ttclose(void) 99 { 100 if (ttstarted) { 101 if (ttcooked() == FALSE) 102 panic(""); /* ttcooked() already printf'd */ 103 ttstarted = 0; 104 } 105 } 106 107 /* 108 * This function restores all terminal settings to their default values, 109 * in anticipation of exiting or suspending the editor. 110 */ 111 int 112 ttcooked(void) 113 { 114 ttflush(); 115 if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) < 0) { 116 ewprintf("ttclose can't tcsetattr"); 117 return (FALSE); 118 } 119 return (TRUE); 120 } 121 122 /* 123 * Write character to the display. Characters are buffered up, 124 * to make things a little bit more efficient. 125 */ 126 int 127 ttputc(int c) 128 { 129 if (nobuf >= NOBUF) 130 ttflush(); 131 obuf[nobuf++] = c; 132 return (c); 133 } 134 135 /* 136 * Flush output. 137 */ 138 void 139 ttflush(void) 140 { 141 ssize_t written; 142 char *buf = obuf; 143 144 if (nobuf == 0) 145 return; 146 147 while ((written = write(fileno(stdout), buf, nobuf)) != nobuf) { 148 if (written == -1) { 149 if (errno == EINTR) 150 continue; 151 panic("ttflush write failed"); 152 } 153 buf += written; 154 nobuf -= written; 155 } 156 nobuf = 0; 157 } 158 159 /* 160 * Read character from terminal. All 8 bits are returned, so that you 161 * can use a multi-national terminal. 162 */ 163 int 164 ttgetc(void) 165 { 166 char c; 167 ssize_t ret; 168 169 do { 170 ret = read(STDIN_FILENO, &c, 1); 171 if (ret == -1 && errno == EINTR) { 172 if (winch_flag) { 173 redraw(0, 0); 174 winch_flag = 0; 175 } 176 } else if (ret == -1 && errno == EIO) 177 panic("lost stdin"); 178 else if (ret == 1) 179 break; 180 } while (1); 181 return ((int) c) & 0xFF; 182 } 183 184 /* 185 * Returns TRUE if there are characters waiting to be read. 186 */ 187 int 188 charswaiting(void) 189 { 190 int x; 191 192 return ((ioctl(0, FIONREAD, &x) < 0) ? 0 : x); 193 } 194 195 /* 196 * panic - just exit, as quickly as we can. 197 */ 198 void 199 panic(char *s) 200 { 201 static int panicking = 0; 202 203 if (panicking) 204 return; 205 else 206 panicking = 1; 207 ttclose(); 208 (void) fputs("panic: ", stderr); 209 (void) fputs(s, stderr); 210 (void) fputc('\n', stderr); 211 exit(1); 212 } 213 214 /* 215 * This function returns FALSE if any characters have showed up on the 216 * tty before 'msec' milliseconds. 217 */ 218 int 219 ttwait(int msec) 220 { 221 fd_set readfds; 222 struct timeval tmout; 223 224 FD_ZERO(&readfds); 225 FD_SET(0, &readfds); 226 227 tmout.tv_sec = msec/1000; 228 tmout.tv_usec = msec - tmout.tv_sec * 1000; 229 230 if ((select(1, &readfds, NULL, NULL, &tmout)) == 0) 231 return (TRUE); 232 return (FALSE); 233 } 234