tty.c revision 1.30 1 /* $NetBSD: tty.c,v 1.30 2003/01/09 12:48:06 blymn 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.30 2003/01/09 12:48:06 blymn 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 timeout)
248 {
249 if ((timeout < 1) || (timeout > 255))
250 return ERR;
251
252 if (cbreak() == ERR)
253 return ERR;
254
255 if (__timeout(timeout) == 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