1 /* $NetBSD: tty.c,v 1.49 2021/09/06 07:03:50 rin 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)tty.c 8.6 (Berkeley) 1/10/95"; 36 #else 37 __RCSID("$NetBSD: tty.c,v 1.49 2021/09/06 07:03:50 rin Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/fcntl.h> 42 #include <sys/ioctl.h> 43 #include <sys/param.h> 44 #include <sys/types.h> 45 46 #include <stdlib.h> 47 #include <termios.h> 48 #include <unistd.h> 49 50 #include "curses.h" 51 #include "curses_private.h" 52 53 /* 54 * In general, curses should leave tty hardware settings alone (speed, parity, 55 * word size). This is most easily done in BSD by using TCSASOFT on all 56 * tcsetattr calls. On other systems, it would be better to get and restore 57 * those attributes at each change, or at least when stopped and restarted. 58 * See also the comments in getterm(). 59 */ 60 #ifndef TCSASOFT 61 #define TCSASOFT 0 62 #endif 63 64 int __tcaction = TCSASOFT != 0; /* Ignore hardware settings */ 65 66 #ifndef OXTABS 67 #ifdef XTABS /* SMI uses XTABS. */ 68 #define OXTABS XTABS 69 #else 70 #define OXTABS 0 71 #endif 72 #endif 73 74 /* 75 * baudrate -- 76 * Return the current baudrate 77 */ 78 int 79 baudrate(void) 80 { 81 82 if (_cursesi_screen->notty == TRUE) 83 return 0; 84 85 return cfgetospeed(&_cursesi_screen->baset); 86 } 87 88 /* 89 * gettmode -- 90 * Do terminal type initialization. 91 */ 92 int 93 gettmode(void) 94 { 95 96 if (_cursesi_gettmode(_cursesi_screen) == ERR) 97 return ERR; 98 99 __GT = _cursesi_screen->GT; 100 __NONL = _cursesi_screen->NONL; 101 return OK; 102 } 103 104 /* 105 * _cursesi_gettmode -- 106 * Do the terminal type initialisation for the tty attached to the 107 * given screen. 108 */ 109 int 110 _cursesi_gettmode(SCREEN *screen) 111 { 112 screen->useraw = 0; 113 114 if (tcgetattr(fileno(screen->infd), &screen->orig_termios)) { 115 /* if the input fd is not a tty try the output */ 116 if (tcgetattr(fileno(screen->infd), &screen->orig_termios)) { 117 /* not a tty ... we will disable tty related stuff */ 118 screen->notty = TRUE; 119 __GT = 0; 120 __NONL = 0; 121 return OK; 122 } 123 } 124 125 screen->baset = screen->orig_termios; 126 screen->baset.c_oflag &= ~OXTABS; 127 128 screen->GT = 0; /* historical. was used before we wired OXTABS off */ 129 screen->NONL = (screen->baset.c_oflag & ONLCR) == 0; 130 131 /* 132 * XXX 133 * System V and SMI systems overload VMIN and VTIME, such that 134 * VMIN is the same as the VEOF element, and VTIME is the same 135 * as the VEOL element. This means that, if VEOF was ^D, the 136 * default VMIN is 4. Majorly stupid. 137 */ 138 screen->cbreakt = screen->baset; 139 screen->cbreakt.c_lflag &= ~(ECHO | ECHONL | ICANON); 140 screen->cbreakt.c_cc[VMIN] = 1; 141 screen->cbreakt.c_cc[VTIME] = 0; 142 143 screen->rawt = screen->cbreakt; 144 screen->rawt.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | INLCR | IGNCR | 145 ICRNL | IXON); 146 screen->rawt.c_oflag &= ~OPOST; 147 screen->rawt.c_lflag &= ~(ISIG | IEXTEN); 148 149 #if TCSASOFT == 0 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 screen->rawt.c_iflag &= ~ISTRIP; 159 screen->rawt.c_cflag &= ~(CSIZE | PARENB); 160 screen->rawt.c_cflag |= CS8; 161 #endif 162 163 screen->curt = &screen->baset; 164 return tcsetattr(fileno(screen->infd), TCSASOFT | TCSADRAIN, 165 screen->curt) ? ERR : OK; 166 } 167 168 /* 169 * raw -- 170 * Put the terminal into raw mode 171 */ 172 int 173 raw(void) 174 { 175 __CTRACE(__CTRACE_MISC, "raw()\n"); 176 /* Check if we need to restart ... */ 177 if (_cursesi_screen->endwin) 178 __restartwin(); 179 180 _cursesi_screen->useraw = __pfast = __rawmode = 1; 181 _cursesi_screen->curt = &_cursesi_screen->rawt; 182 if (_cursesi_screen->notty == TRUE) 183 return OK; 184 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 185 _cursesi_screen->curt) ? ERR : OK; 186 } 187 188 /* 189 * noraw -- 190 * Put the terminal into cooked mode 191 */ 192 int 193 noraw(void) 194 { 195 __CTRACE(__CTRACE_MISC, "noraw()\n"); 196 /* Check if we need to restart ... */ 197 if (_cursesi_screen->endwin) 198 __restartwin(); 199 200 _cursesi_screen->useraw = __pfast = __rawmode = 0; 201 if (_cursesi_screen->notty == TRUE) 202 return OK; 203 _cursesi_screen->curt = &_cursesi_screen->baset; 204 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 205 _cursesi_screen->curt) ? ERR : OK; 206 } 207 208 /* 209 * cbreak -- 210 * Enable cbreak mode 211 */ 212 int 213 cbreak(void) 214 { 215 __CTRACE(__CTRACE_MISC, "cbreak()\n"); 216 /* Check if we need to restart ... */ 217 if (_cursesi_screen->endwin) 218 __restartwin(); 219 220 __rawmode = 1; 221 if (_cursesi_screen->notty == TRUE) 222 return OK; 223 _cursesi_screen->curt = _cursesi_screen->useraw ? 224 &_cursesi_screen->rawt : &_cursesi_screen->cbreakt; 225 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 226 _cursesi_screen->curt) ? ERR : OK; 227 } 228 229 /* 230 * nocbreak -- 231 * Disable cbreak mode 232 */ 233 int 234 nocbreak(void) 235 { 236 __CTRACE(__CTRACE_MISC, "nocbreak()\n"); 237 /* Check if we need to restart ... */ 238 if (_cursesi_screen->endwin) 239 __restartwin(); 240 241 __rawmode = 0; 242 if (_cursesi_screen->notty == TRUE) 243 return OK; 244 /* if we were in halfdelay mode then nuke the timeout */ 245 if ((stdscr->flags & __HALFDELAY) && 246 (__notimeout() == ERR)) 247 return ERR; 248 249 stdscr->flags &= ~__HALFDELAY; 250 _cursesi_screen->curt = _cursesi_screen->useraw ? 251 &_cursesi_screen->rawt : &_cursesi_screen->baset; 252 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 253 _cursesi_screen->curt) ? ERR : OK; 254 } 255 256 /* 257 * halfdelay -- 258 * Put the terminal into cbreak mode with the specified timeout. 259 * 260 */ 261 int 262 halfdelay(int duration) 263 { 264 if ((duration < 1) || (duration > 255)) 265 return ERR; 266 267 if (cbreak() == ERR) 268 return ERR; 269 270 if (duration > 255) 271 stdscr->delay = 255; 272 else 273 stdscr->delay = duration; 274 275 stdscr->flags |= __HALFDELAY; 276 return OK; 277 } 278 279 int 280 __delay(void) 281 { 282 __CTRACE(__CTRACE_MISC, "__delay()\n"); 283 /* Check if we need to restart ... */ 284 if (_cursesi_screen->endwin) 285 __restartwin(); 286 287 if (_cursesi_screen->notty == TRUE) 288 return OK; 289 _cursesi_screen->rawt.c_cc[VMIN] = 1; 290 _cursesi_screen->rawt.c_cc[VTIME] = 0; 291 _cursesi_screen->cbreakt.c_cc[VMIN] = 1; 292 _cursesi_screen->cbreakt.c_cc[VTIME] = 0; 293 _cursesi_screen->baset.c_cc[VMIN] = 1; 294 _cursesi_screen->baset.c_cc[VTIME] = 0; 295 296 if (tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSANOW, 297 _cursesi_screen->curt)) { 298 __restore_termios(); 299 return ERR; 300 } 301 302 return OK; 303 } 304 305 int 306 __nodelay(void) 307 { 308 __CTRACE(__CTRACE_MISC, "__nodelay()\n"); 309 /* Check if we need to restart ... */ 310 if (_cursesi_screen->endwin) 311 __restartwin(); 312 313 if (_cursesi_screen->notty == TRUE) 314 return OK; 315 _cursesi_screen->rawt.c_cc[VMIN] = 0; 316 _cursesi_screen->rawt.c_cc[VTIME] = 0; 317 _cursesi_screen->cbreakt.c_cc[VMIN] = 0; 318 _cursesi_screen->cbreakt.c_cc[VTIME] = 0; 319 _cursesi_screen->baset.c_cc[VMIN] = 0; 320 _cursesi_screen->baset.c_cc[VTIME] = 0; 321 322 if (tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSANOW, 323 _cursesi_screen->curt)) { 324 __restore_termios(); 325 return ERR; 326 } 327 328 return OK; 329 } 330 331 void 332 __save_termios(void) 333 { 334 /* Check if we need to restart ... */ 335 if (_cursesi_screen->endwin) 336 __restartwin(); 337 338 if (_cursesi_screen->notty == TRUE) 339 return; 340 _cursesi_screen->ovmin = _cursesi_screen->cbreakt.c_cc[VMIN]; 341 _cursesi_screen->ovtime = _cursesi_screen->cbreakt.c_cc[VTIME]; 342 } 343 344 void 345 __restore_termios(void) 346 { 347 /* Check if we need to restart ... */ 348 if (_cursesi_screen->endwin) 349 __restartwin(); 350 351 if (_cursesi_screen->notty == TRUE) 352 return; 353 _cursesi_screen->rawt.c_cc[VMIN] = _cursesi_screen->ovmin; 354 _cursesi_screen->rawt.c_cc[VTIME] = _cursesi_screen->ovtime; 355 _cursesi_screen->cbreakt.c_cc[VMIN] = _cursesi_screen->ovmin; 356 _cursesi_screen->cbreakt.c_cc[VTIME] = _cursesi_screen->ovtime; 357 _cursesi_screen->baset.c_cc[VMIN] = _cursesi_screen->ovmin; 358 _cursesi_screen->baset.c_cc[VTIME] = _cursesi_screen->ovtime; 359 } 360 361 int 362 __timeout(int delay) 363 { 364 __CTRACE(__CTRACE_MISC, "__timeout()\n"); 365 /* Check if we need to restart ... */ 366 if (_cursesi_screen->endwin) 367 __restartwin(); 368 369 if (_cursesi_screen->notty == TRUE) 370 return OK; 371 _cursesi_screen->ovmin = _cursesi_screen->cbreakt.c_cc[VMIN]; 372 _cursesi_screen->ovtime = _cursesi_screen->cbreakt.c_cc[VTIME]; 373 _cursesi_screen->rawt.c_cc[VMIN] = 0; 374 _cursesi_screen->rawt.c_cc[VTIME] = delay; 375 _cursesi_screen->cbreakt.c_cc[VMIN] = 0; 376 _cursesi_screen->cbreakt.c_cc[VTIME] = delay; 377 _cursesi_screen->baset.c_cc[VMIN] = 0; 378 _cursesi_screen->baset.c_cc[VTIME] = delay; 379 380 if (tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSANOW, 381 _cursesi_screen->curt)) { 382 __restore_termios(); 383 return ERR; 384 } 385 386 return OK; 387 } 388 389 int 390 __notimeout(void) 391 { 392 __CTRACE(__CTRACE_MISC, "__notimeout()\n"); 393 /* Check if we need to restart ... */ 394 if (_cursesi_screen->endwin) 395 __restartwin(); 396 397 if (_cursesi_screen->notty == TRUE) 398 return OK; 399 _cursesi_screen->rawt.c_cc[VMIN] = 1; 400 _cursesi_screen->rawt.c_cc[VTIME] = 0; 401 _cursesi_screen->cbreakt.c_cc[VMIN] = 1; 402 _cursesi_screen->cbreakt.c_cc[VTIME] = 0; 403 _cursesi_screen->baset.c_cc[VMIN] = 1; 404 _cursesi_screen->baset.c_cc[VTIME] = 0; 405 406 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSANOW, 407 _cursesi_screen->curt) ? ERR : OK; 408 } 409 410 int 411 echo(void) 412 { 413 __CTRACE(__CTRACE_MISC, "echo()\n"); 414 /* Check if we need to restart ... */ 415 if (_cursesi_screen->endwin) 416 __restartwin(); 417 418 __echoit = 1; 419 return OK; 420 } 421 422 int 423 noecho(void) 424 { 425 __CTRACE(__CTRACE_MISC, "noecho()\n"); 426 /* Check if we need to restart ... */ 427 if (_cursesi_screen->endwin) 428 __restartwin(); 429 430 __echoit = 0; 431 return OK; 432 } 433 434 int 435 nl(void) 436 { 437 __CTRACE(__CTRACE_MISC, "nl()\n"); 438 /* Check if we need to restart ... */ 439 if (_cursesi_screen->endwin) 440 __restartwin(); 441 442 if (_cursesi_screen->notty == TRUE) 443 return OK; 444 _cursesi_screen->rawt.c_iflag |= ICRNL; 445 _cursesi_screen->rawt.c_oflag |= ONLCR; 446 _cursesi_screen->cbreakt.c_iflag |= ICRNL; 447 _cursesi_screen->cbreakt.c_oflag |= ONLCR; 448 _cursesi_screen->baset.c_iflag |= ICRNL; 449 _cursesi_screen->baset.c_oflag |= ONLCR; 450 451 _cursesi_screen->nl = 1; 452 _cursesi_screen->pfast = _cursesi_screen->rawmode; 453 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 454 _cursesi_screen->curt) ? ERR : OK; 455 } 456 457 int 458 nonl(void) 459 { 460 __CTRACE(__CTRACE_MISC, "nonl()\n"); 461 /* Check if we need to restart ... */ 462 if (_cursesi_screen->endwin) 463 __restartwin(); 464 465 if (_cursesi_screen->notty == TRUE) 466 return OK; 467 _cursesi_screen->rawt.c_iflag &= ~ICRNL; 468 _cursesi_screen->rawt.c_oflag &= ~ONLCR; 469 _cursesi_screen->cbreakt.c_iflag &= ~ICRNL; 470 _cursesi_screen->cbreakt.c_oflag &= ~ONLCR; 471 _cursesi_screen->baset.c_iflag &= ~ICRNL; 472 _cursesi_screen->baset.c_oflag &= ~ONLCR; 473 474 _cursesi_screen->nl = 0; 475 __pfast = 1; 476 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 477 _cursesi_screen->curt) ? ERR : OK; 478 } 479 480 #ifndef _CURSES_USE_MACROS 481 void 482 noqiflush(void) 483 { 484 485 (void)intrflush(stdscr, FALSE); 486 } 487 488 void 489 qiflush(void) 490 { 491 492 (void)intrflush(stdscr, TRUE); 493 } 494 #endif /* _CURSES_USE_MACROS */ 495 496 /*ARGSUSED*/ 497 int 498 intrflush(WINDOW *win, bool bf) 499 { 500 /* Check if we need to restart ... */ 501 if (_cursesi_screen->endwin) 502 __restartwin(); 503 504 if (_cursesi_screen->notty == TRUE) 505 return OK; 506 if (bf) { 507 _cursesi_screen->rawt.c_lflag &= ~NOFLSH; 508 _cursesi_screen->cbreakt.c_lflag &= ~NOFLSH; 509 _cursesi_screen->baset.c_lflag &= ~NOFLSH; 510 } else { 511 _cursesi_screen->rawt.c_lflag |= NOFLSH; 512 _cursesi_screen->cbreakt.c_lflag |= NOFLSH; 513 _cursesi_screen->baset.c_lflag |= NOFLSH; 514 } 515 516 __pfast = 1; 517 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 518 _cursesi_screen->curt) ? ERR : OK; 519 } 520 521 void 522 __startwin(SCREEN *screen) 523 { 524 525 (void)fflush(screen->infd); 526 527 #ifdef BSD 528 /* 529 * Some C libraries default to a 1K buffer when talking to a tty. 530 * With a larger screen, especially across a network, we'd like 531 * to get it to all flush in a single write. Make it twice as big 532 * as just the characters (so that we have room for cursor motions 533 * and attribute information) but no more than 8K. 534 * 535 * However, setvbuf may only be used after opening a stream and 536 * before any operations have been performed on it. 537 * This means we cannot work portably if an application wants 538 * to stop curses and start curses after a resize. 539 * Curses resizing is not standard, and thus not strictly portable 540 * even though all curses today support it. 541 * The BSD systems do not suffer from this limitation on setvbuf. 542 */ 543 if (screen->stdbuf == NULL) { 544 screen->len = LINES * COLS * 2; 545 if (screen->len > 8192) 546 screen->len = 8192; 547 if ((screen->stdbuf = malloc(screen->len)) == NULL) 548 screen->len = 0; 549 } 550 (void)setvbuf(screen->outfd, screen->stdbuf, _IOFBF, screen->len); 551 #endif 552 553 ti_puts(screen->term, t_enter_ca_mode(screen->term), 0, 554 __cputchar_args, (void *) screen->outfd); 555 ti_puts(screen->term, t_cursor_normal(screen->term), 0, 556 __cputchar_args, (void *) screen->outfd); 557 if (screen->curscr->flags & __KEYPAD) 558 ti_puts(screen->term, t_keypad_xmit(screen->term), 0, 559 __cputchar_args, (void *) screen->outfd); 560 screen->endwin = 0; 561 } 562 563 int 564 endwin(void) 565 { 566 __CTRACE(__CTRACE_MISC, "endwin\n"); 567 return __stopwin(); 568 } 569 570 bool 571 isendwin(void) 572 { 573 574 return _cursesi_screen->endwin ? TRUE : FALSE; 575 } 576 577 int 578 flushinp(void) 579 { 580 581 (void)fpurge(_cursesi_screen->infd); 582 return OK; 583 } 584 585 /* 586 * The following routines, savetty and resetty are completely useless and 587 * are left in only as stubs. If people actually use them they will almost 588 * certainly screw up the state of the world. 589 */ 590 /*static struct termios savedtty;*/ 591 int 592 savetty(void) 593 { 594 595 if (_cursesi_screen->notty == TRUE) 596 return OK; 597 return tcgetattr(fileno(_cursesi_screen->infd), 598 &_cursesi_screen->savedtty) ? ERR : OK; 599 } 600 601 int 602 resetty(void) 603 { 604 605 if (_cursesi_screen->notty == TRUE) 606 return OK; 607 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 608 &_cursesi_screen->savedtty) ? ERR : OK; 609 } 610 611 /* 612 * erasechar -- 613 * Return the character of the erase key. 614 */ 615 char 616 erasechar(void) 617 { 618 619 if (_cursesi_screen->notty == TRUE) 620 return 0; 621 return _cursesi_screen->baset.c_cc[VERASE]; 622 } 623 624 /* 625 * killchar -- 626 * Return the character of the kill key. 627 */ 628 char 629 killchar(void) 630 { 631 632 if (_cursesi_screen->notty == TRUE) 633 return 0; 634 return _cursesi_screen->baset.c_cc[VKILL]; 635 } 636 637 /* 638 * erasewchar -- 639 * Return the wide character of the erase key. 640 */ 641 int 642 erasewchar(wchar_t *ch) 643 { 644 645 #ifndef HAVE_WCHAR 646 return ERR; 647 #else 648 if (_cursesi_screen->notty == TRUE) 649 return ERR; 650 *ch = _cursesi_screen->baset.c_cc[VERASE]; 651 return OK; 652 #endif /* HAVE_WCHAR */ 653 } 654 655 /* 656 * killwchar -- 657 * Return the wide character of the kill key. 658 */ 659 int 660 killwchar( wchar_t *ch ) 661 { 662 663 #ifndef HAVE_WCHAR 664 return ERR; 665 #else 666 if (_cursesi_screen->notty == TRUE) 667 return 0; 668 *ch = _cursesi_screen->baset.c_cc[VKILL]; 669 return OK; 670 #endif /* HAVE_WCHAR */ 671 } 672 673 int 674 typeahead(int filedes) 675 { 676 677 _cursesi_screen->checkfd = filedes; 678 return OK; 679 } 680