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