xref: /openbsd-src/usr.bin/mg/ttyio.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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