xref: /netbsd-src/lib/libcurses/tty.c (revision b8c616269f5ebf18ab2e35cb8099d683130a177c)
1 /*	$NetBSD: tty.c,v 1.31 2003/01/09 21:47:39 atatat Exp $	*/
2 
3 /*-
4  * Copyright (c) 1992, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)tty.c	8.6 (Berkeley) 1/10/95";
40 #else
41 __RCSID("$NetBSD: tty.c,v 1.31 2003/01/09 21:47:39 atatat Exp $");
42 #endif
43 #endif				/* not lint */
44 
45 #include <sys/types.h>
46 
47 #include <stdlib.h>
48 #include <termios.h>
49 #include <unistd.h>
50 #include <sys/fcntl.h>
51 #include <sys/ioctl.h>
52 
53 #include "curses.h"
54 #include "curses_private.h"
55 
56 /*
57  * In general, curses should leave tty hardware settings alone (speed, parity,
58  * word size).  This is most easily done in BSD by using TCSASOFT on all
59  * tcsetattr calls.  On other systems, it would be better to get and restore
60  * those attributes at each change, or at least when stopped and restarted.
61  * See also the comments in getterm().
62  */
63 #ifdef TCSASOFT
64 int	__tcaction = 1;			/* Ignore hardware settings. */
65 #else
66 int	__tcaction = 0;
67 #endif
68 
69 #ifndef	OXTABS
70 #ifdef	XTABS			/* SMI uses XTABS. */
71 #define	OXTABS	XTABS
72 #else
73 #define	OXTABS	0
74 #endif
75 #endif
76 
77 /*
78  * baudrate --
79  *	Return the current baudrate
80  */
81 int
82 baudrate(void)
83 {
84 	if (_cursesi_screen->notty == TRUE)
85 		return 0;
86 
87 	return cfgetospeed(&_cursesi_screen->baset);
88 }
89 
90 /*
91  * gettmode --
92  *	Do terminal type initialization.
93  */
94 int
95 gettmode(void)
96 {
97 	if (_cursesi_gettmode(_cursesi_screen) == ERR)
98 		return ERR;
99 
100 	__GT = _cursesi_screen->GT;
101 	__NONL = _cursesi_screen->NONL;
102 	return OK;
103 }
104 
105 /*
106  * _cursesi_gettmode --
107  *      Do the terminal type initialisation for the tty attached to the
108  *  given screen.
109  */
110 int
111 _cursesi_gettmode(SCREEN *screen)
112 {
113 	screen->useraw = 0;
114 
115 	if (tcgetattr(fileno(screen->infd), &screen->orig_termios)) {
116 		/* if the input fd is not a tty try the output */
117 		if (tcgetattr(fileno(screen->infd), &screen->orig_termios)) {
118 			/* not a tty ... we will disable tty related stuff */
119 			screen->notty = TRUE;
120 			__GT = 0;
121 			__NONL = 0;
122 			return (OK);
123 		}
124 	}
125 
126 	screen->baset = screen->orig_termios;
127 	screen->baset.c_oflag &= ~OXTABS;
128 
129 	screen->GT = 0;	/* historical. was used before we wired OXTABS off */
130 	screen->NONL = (screen->baset.c_oflag & ONLCR) == 0;
131 
132 	/*
133 	 * XXX
134 	 * System V and SMI systems overload VMIN and VTIME, such that
135 	 * VMIN is the same as the VEOF element, and VTIME is the same
136 	 * as the VEOL element.  This means that, if VEOF was ^D, the
137 	 * default VMIN is 4.  Majorly stupid.
138 	 */
139 	screen->cbreakt = screen->baset;
140 	screen->cbreakt.c_lflag &= ~(ECHO | ECHONL | ICANON);
141 	screen->cbreakt.c_cc[VMIN] = 1;
142 	screen->cbreakt.c_cc[VTIME] = 0;
143 
144 	screen->rawt = screen->cbreakt;
145 	screen->rawt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | INLCR | IGNCR |
146 				  ICRNL | IXON);
147 	screen->rawt.c_oflag &= ~OPOST;
148 	screen->rawt.c_lflag &= ~(ISIG | IEXTEN);
149 
150 	/*
151 	 * In general, curses should leave hardware-related settings alone.
152 	 * This includes parity and word size.  Older versions set the tty
153 	 * to 8 bits, no parity in raw(), but this is considered to be an
154 	 * artifact of the old tty interface.  If it's desired to change
155 	 * parity and word size, the TCSASOFT bit has to be removed from the
156 	 * calls that switch to/from "raw" mode.
157 	 */
158 	if (!__tcaction) {
159 		screen->rawt.c_iflag &= ~ISTRIP;
160 		screen->rawt.c_cflag &= ~(CSIZE | PARENB);
161 		screen->rawt.c_cflag |= CS8;
162 	}
163 
164 	screen->curt = &screen->baset;
165 	return (tcsetattr(fileno(screen->infd), __tcaction ?
166 	    TCSASOFT | TCSADRAIN : TCSADRAIN, screen->curt) ? ERR : OK);
167 }
168 
169 int
170 raw(void)
171 {
172 	/* Check if we need to restart ... */
173 	if (_cursesi_screen->endwin)
174 		__restartwin();
175 
176 	_cursesi_screen->useraw = __pfast = __rawmode = 1;
177 	_cursesi_screen->curt = &_cursesi_screen->rawt;
178 	if (_cursesi_screen->notty == TRUE)
179 		return OK;
180 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
181 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
182 			  _cursesi_screen->curt) ? ERR : OK);
183 }
184 
185 int
186 noraw(void)
187 {
188 	/* Check if we need to restart ... */
189 	if (_cursesi_screen->endwin)
190 		__restartwin();
191 
192 	_cursesi_screen->useraw = __pfast = __rawmode = 0;
193 	if (_cursesi_screen->notty == TRUE)
194 		return OK;
195 	_cursesi_screen->curt = &_cursesi_screen->baset;
196 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
197 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
198 			  _cursesi_screen->curt) ? ERR : OK);
199 }
200 
201 int
202 cbreak(void)
203 {
204 	/* Check if we need to restart ... */
205 	if (_cursesi_screen->endwin)
206 		__restartwin();
207 
208 	__rawmode = 1;
209 	if (_cursesi_screen->notty == TRUE)
210 		return OK;
211 	_cursesi_screen->curt = _cursesi_screen->useraw ?
212 		&_cursesi_screen->rawt : &_cursesi_screen->cbreakt;
213 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
214 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
215 			  _cursesi_screen->curt) ? ERR : OK);
216 }
217 
218 int
219 nocbreak(void)
220 {
221 	/* Check if we need to restart ... */
222 	if (_cursesi_screen->endwin)
223 		__restartwin();
224 
225 	__rawmode = 0;
226 	if (_cursesi_screen->notty == TRUE)
227 		return OK;
228 	  /* if we were in halfdelay mode then nuke the timeout */
229 	if ((_cursesi_screen->half_delay == TRUE) &&
230 	    (__notimeout() == ERR))
231 		return ERR;
232 
233 	_cursesi_screen->half_delay = FALSE;
234 	_cursesi_screen->curt = _cursesi_screen->useraw ?
235 		&_cursesi_screen->rawt : &_cursesi_screen->baset;
236 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
237 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
238 			  _cursesi_screen->curt) ? ERR : OK);
239 }
240 
241 /*
242  * halfdelay --
243  *    Put the terminal into cbreak mode with the specified timeout.
244  *
245  */
246 int
247 halfdelay(int duration)
248 {
249 	if ((duration < 1) || (duration > 255))
250 		return ERR;
251 
252 	if (cbreak() == ERR)
253 		return ERR;
254 
255 	if (__timeout(duration) == ERR)
256 		return ERR;
257 
258 	_cursesi_screen->half_delay = TRUE;
259 	return OK;
260 }
261 
262 int
263 __delay(void)
264  {
265 	/* Check if we need to restart ... */
266 	if (_cursesi_screen->endwin)
267 		__restartwin();
268 
269 	if (_cursesi_screen->notty == TRUE)
270 		return OK;
271 	_cursesi_screen->rawt.c_cc[VMIN] = 1;
272 	_cursesi_screen->rawt.c_cc[VTIME] = 0;
273 	_cursesi_screen->cbreakt.c_cc[VMIN] = 1;
274 	_cursesi_screen->cbreakt.c_cc[VTIME] = 0;
275 	_cursesi_screen->baset.c_cc[VMIN] = 1;
276 	_cursesi_screen->baset.c_cc[VTIME] = 0;
277 
278 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
279 		TCSASOFT : TCSANOW, _cursesi_screen->curt) ? ERR : OK);
280 }
281 
282 int
283 __nodelay(void)
284 {
285 	/* Check if we need to restart ... */
286 	if (_cursesi_screen->endwin)
287 		__restartwin();
288 
289 	if (_cursesi_screen->notty == TRUE)
290 		return OK;
291 	_cursesi_screen->rawt.c_cc[VMIN] = 0;
292 	_cursesi_screen->rawt.c_cc[VTIME] = 0;
293 	_cursesi_screen->cbreakt.c_cc[VMIN] = 0;
294 	_cursesi_screen->cbreakt.c_cc[VTIME] = 0;
295 	_cursesi_screen->baset.c_cc[VMIN] = 0;
296 	_cursesi_screen->baset.c_cc[VTIME] = 0;
297 
298 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
299 		TCSASOFT : TCSANOW, _cursesi_screen->curt) ? ERR : OK);
300 }
301 
302 void
303 __save_termios(void)
304 {
305 	/* Check if we need to restart ... */
306 	if (_cursesi_screen->endwin)
307 		__restartwin();
308 
309 	if (_cursesi_screen->notty == TRUE)
310 		return;
311 	_cursesi_screen->ovmin = _cursesi_screen->cbreakt.c_cc[VMIN];
312 	_cursesi_screen->ovtime = _cursesi_screen->cbreakt.c_cc[VTIME];
313 }
314 
315 void
316 __restore_termios(void)
317 {
318 	/* Check if we need to restart ... */
319 	if (_cursesi_screen->endwin)
320 		__restartwin();
321 
322 	if (_cursesi_screen->notty == TRUE)
323 		return;
324 	_cursesi_screen->rawt.c_cc[VMIN] = _cursesi_screen->ovmin;
325 	_cursesi_screen->rawt.c_cc[VTIME] = _cursesi_screen->ovtime;
326 	_cursesi_screen->cbreakt.c_cc[VMIN] = _cursesi_screen->ovmin;
327 	_cursesi_screen->cbreakt.c_cc[VTIME] = _cursesi_screen->ovtime;
328 	_cursesi_screen->baset.c_cc[VMIN] = _cursesi_screen->ovmin;
329 	_cursesi_screen->baset.c_cc[VTIME] = _cursesi_screen->ovtime;
330 }
331 
332 int
333 __timeout(int delay)
334 {
335 	/* Check if we need to restart ... */
336 	if (_cursesi_screen->endwin)
337 		__restartwin();
338 
339 	if (_cursesi_screen->notty == TRUE)
340 		return OK;
341 	_cursesi_screen->ovmin = _cursesi_screen->cbreakt.c_cc[VMIN];
342 	_cursesi_screen->ovtime = _cursesi_screen->cbreakt.c_cc[VTIME];
343 	_cursesi_screen->rawt.c_cc[VMIN] = 0;
344 	_cursesi_screen->rawt.c_cc[VTIME] = delay;
345 	_cursesi_screen->cbreakt.c_cc[VMIN] = 0;
346 	_cursesi_screen->cbreakt.c_cc[VTIME] = delay;
347 	_cursesi_screen->baset.c_cc[VMIN] = 0;
348 	_cursesi_screen->baset.c_cc[VTIME] = delay;
349 
350 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
351 			  TCSASOFT | TCSANOW : TCSANOW,
352 			  _cursesi_screen->curt) ? ERR : OK);
353 }
354 
355 int
356 __notimeout(void)
357 {
358 	/* Check if we need to restart ... */
359 	if (_cursesi_screen->endwin)
360 		__restartwin();
361 
362 	if (_cursesi_screen->notty == TRUE)
363 		return OK;
364 	_cursesi_screen->rawt.c_cc[VMIN] = 1;
365 	_cursesi_screen->rawt.c_cc[VTIME] = 0;
366 	_cursesi_screen->cbreakt.c_cc[VMIN] = 1;
367 	_cursesi_screen->cbreakt.c_cc[VTIME] = 0;
368 	_cursesi_screen->baset.c_cc[VMIN] = 1;
369 	_cursesi_screen->baset.c_cc[VTIME] = 0;
370 
371 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
372 			  TCSASOFT | TCSANOW : TCSANOW,
373 			  _cursesi_screen->curt) ? ERR : OK);
374 }
375 
376 int
377 echo(void)
378 {
379 	/* Check if we need to restart ... */
380 	if (_cursesi_screen->endwin)
381 		__restartwin();
382 
383 	__echoit = 1;
384 	return (OK);
385 }
386 
387 int
388 noecho(void)
389 {
390 	/* Check if we need to restart ... */
391 	if (_cursesi_screen->endwin)
392 		__restartwin();
393 
394 	__echoit = 0;
395 	return (OK);
396 }
397 
398 int
399 nl(void)
400 {
401 	/* Check if we need to restart ... */
402 	if (_cursesi_screen->endwin)
403 		__restartwin();
404 
405 	if (_cursesi_screen->notty == TRUE)
406 		return OK;
407 	_cursesi_screen->rawt.c_iflag |= ICRNL;
408 	_cursesi_screen->rawt.c_oflag |= ONLCR;
409 	_cursesi_screen->cbreakt.c_iflag |= ICRNL;
410 	_cursesi_screen->cbreakt.c_oflag |= ONLCR;
411 	_cursesi_screen->baset.c_iflag |= ICRNL;
412 	_cursesi_screen->baset.c_oflag |= ONLCR;
413 
414 	_cursesi_screen->pfast = _cursesi_screen->rawmode;
415 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
416 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
417 			  _cursesi_screen->curt) ? ERR : OK);
418 }
419 
420 int
421 nonl(void)
422 {
423 	/* Check if we need to restart ... */
424 	if (_cursesi_screen->endwin)
425 		__restartwin();
426 
427 	if (_cursesi_screen->notty == TRUE)
428 		return OK;
429 	_cursesi_screen->rawt.c_iflag &= ~ICRNL;
430 	_cursesi_screen->rawt.c_oflag &= ~ONLCR;
431 	_cursesi_screen->cbreakt.c_iflag &= ~ICRNL;
432 	_cursesi_screen->cbreakt.c_oflag &= ~ONLCR;
433 	_cursesi_screen->baset.c_iflag &= ~ICRNL;
434 	_cursesi_screen->baset.c_oflag &= ~ONLCR;
435 
436 	__pfast = 1;
437 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
438 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
439 			  _cursesi_screen->curt) ? ERR : OK);
440 }
441 
442 int
443 intrflush(WINDOW *win, bool bf)	/*ARGSUSED*/
444 {
445 	/* Check if we need to restart ... */
446 	if (_cursesi_screen->endwin)
447 		__restartwin();
448 
449 	if (_cursesi_screen->notty == TRUE)
450 		return OK;
451 	if (bf) {
452 		_cursesi_screen->rawt.c_lflag &= ~NOFLSH;
453 		_cursesi_screen->cbreakt.c_lflag &= ~NOFLSH;
454 		_cursesi_screen->baset.c_lflag &= ~NOFLSH;
455 	} else {
456 		_cursesi_screen->rawt.c_lflag |= NOFLSH;
457 		_cursesi_screen->cbreakt.c_lflag |= NOFLSH;
458 		_cursesi_screen->baset.c_lflag |= NOFLSH;
459 	}
460 
461 	__pfast = 1;
462 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
463 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
464 			  _cursesi_screen->curt) ? ERR : OK);
465 }
466 
467 void
468 __startwin(SCREEN *screen)
469 {
470 
471 	(void) fflush(screen->infd);
472 
473 	/*
474 	 * Some C libraries default to a 1K buffer when talking to a tty.
475 	 * With a larger screen, especially across a network, we'd like
476 	 * to get it to all flush in a single write.  Make it twice as big
477 	 * as just the characters (so that we have room for cursor motions
478 	 * and attribute information) but no more than 8K.
479 	 */
480 	if (screen->stdbuf == NULL) {
481 		screen->len = LINES * COLS * 2;
482 		if (screen->len > 8192)
483 			screen->len = 8192;
484 		if ((screen->stdbuf = malloc(screen->len)) == NULL)
485 			screen->len = 0;
486 	}
487 	(void) setvbuf(screen->outfd, screen->stdbuf, _IOFBF, screen->len);
488 
489 	t_puts(screen->cursesi_genbuf, __tc_ti, 0, __cputchar_args,
490 	       (void *) screen->outfd);
491 	t_puts(screen->cursesi_genbuf, __tc_vs, 0, __cputchar_args,
492 	       (void *) screen->outfd);
493 	if (screen->curscr->flags & __KEYPAD)
494 		t_puts(screen->cursesi_genbuf, __tc_ks, 0, __cputchar_args,
495 		       (void *) screen->outfd);
496 	screen->endwin = 0;
497 }
498 
499 int
500 endwin(void)
501 {
502 	return __stopwin();
503 }
504 
505 bool
506 isendwin(void)
507 {
508 	return (_cursesi_screen->endwin ? TRUE : FALSE);
509 }
510 
511 int
512 flushinp(void)
513 {
514 	(void) fpurge(_cursesi_screen->infd);
515 	return (OK);
516 }
517 
518 /*
519  * The following routines, savetty and resetty are completely useless and
520  * are left in only as stubs.  If people actually use them they will almost
521  * certainly screw up the state of the world.
522  */
523 /*static struct termios savedtty;*/
524 int
525 savetty(void)
526 {
527 	if (_cursesi_screen->notty == TRUE)
528 		return OK;
529 	return (tcgetattr(fileno(_cursesi_screen->infd),
530 			  &_cursesi_screen->savedtty) ? ERR : OK);
531 }
532 
533 int
534 resetty(void)
535 {
536 	if (_cursesi_screen->notty == TRUE)
537 		return OK;
538 	return (tcsetattr(fileno(_cursesi_screen->infd), __tcaction ?
539 			  TCSASOFT | TCSADRAIN : TCSADRAIN,
540 			  &_cursesi_screen->savedtty) ? ERR : OK);
541 }
542 
543 /*
544  * erasechar --
545  *     Return the character of the erase key.
546  *
547  */
548 char
549 erasechar(void)
550 {
551 	if (_cursesi_screen->notty == TRUE)
552 		return 0;
553 	return _cursesi_screen->baset.c_cc[VERASE];
554 }
555 
556 /*
557  * killchar --
558  *     Return the character of the kill key.
559  */
560 char
561 killchar(void)
562 {
563 	if (_cursesi_screen->notty == TRUE)
564 		return 0;
565 	return _cursesi_screen->baset.c_cc[VKILL];
566 }
567