1 /* $NetBSD: tty.c,v 1.51 2024/12/23 02:58:04 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. 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.51 2024/12/23 02:58:04 blymn 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->outfd), &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 if (__predict_false(win == NULL)) 501 return ERR; 502 503 /* Check if we need to restart ... */ 504 if (_cursesi_screen->endwin) 505 __restartwin(); 506 507 if (_cursesi_screen->notty == TRUE) 508 return OK; 509 if (bf) { 510 _cursesi_screen->rawt.c_lflag &= ~NOFLSH; 511 _cursesi_screen->cbreakt.c_lflag &= ~NOFLSH; 512 _cursesi_screen->baset.c_lflag &= ~NOFLSH; 513 } else { 514 _cursesi_screen->rawt.c_lflag |= NOFLSH; 515 _cursesi_screen->cbreakt.c_lflag |= NOFLSH; 516 _cursesi_screen->baset.c_lflag |= NOFLSH; 517 } 518 519 __pfast = 1; 520 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 521 _cursesi_screen->curt) ? ERR : OK; 522 } 523 524 void 525 __startwin(SCREEN *screen) 526 { 527 528 (void)fflush(screen->infd); 529 530 #ifdef BSD 531 /* 532 * Some C libraries default to a 1K buffer when talking to a tty. 533 * With a larger screen, especially across a network, we'd like 534 * to get it to all flush in a single write. Make it twice as big 535 * as just the characters (so that we have room for cursor motions 536 * and attribute information) but no more than 8K. 537 * 538 * However, setvbuf may only be used after opening a stream and 539 * before any operations have been performed on it. 540 * This means we cannot work portably if an application wants 541 * to stop curses and start curses after a resize. 542 * Curses resizing is not standard, and thus not strictly portable 543 * even though all curses today support it. 544 * The BSD systems do not suffer from this limitation on setvbuf. 545 */ 546 if (screen->stdbuf == NULL) { 547 screen->len = LINES * COLS * 2; 548 if (screen->len > 8192) 549 screen->len = 8192; 550 if ((screen->stdbuf = malloc(screen->len)) == NULL) 551 screen->len = 0; 552 } 553 (void)setvbuf(screen->outfd, screen->stdbuf, _IOFBF, screen->len); 554 #endif 555 556 ti_puts(screen->term, t_enter_ca_mode(screen->term), 0, 557 __cputchar_args, (void *) screen->outfd); 558 ti_puts(screen->term, t_cursor_normal(screen->term), 0, 559 __cputchar_args, (void *) screen->outfd); 560 if (screen->curscr->flags & __KEYPAD) 561 ti_puts(screen->term, t_keypad_xmit(screen->term), 0, 562 __cputchar_args, (void *) screen->outfd); 563 screen->endwin = 0; 564 } 565 566 int 567 endwin(void) 568 { 569 __CTRACE(__CTRACE_MISC, "endwin\n"); 570 return __stopwin(); 571 } 572 573 bool 574 isendwin(void) 575 { 576 577 return _cursesi_screen->endwin ? TRUE : FALSE; 578 } 579 580 int 581 flushinp(void) 582 { 583 584 (void)fpurge(_cursesi_screen->infd); 585 return OK; 586 } 587 588 /* 589 * The following routines, savetty and resetty are completely useless and 590 * are left in only as stubs. If people actually use them they will almost 591 * certainly screw up the state of the world. 592 */ 593 /*static struct termios savedtty;*/ 594 int 595 savetty(void) 596 { 597 598 if (_cursesi_screen->notty == TRUE) 599 return OK; 600 return tcgetattr(fileno(_cursesi_screen->infd), 601 &_cursesi_screen->savedtty) ? ERR : OK; 602 } 603 604 int 605 resetty(void) 606 { 607 608 if (_cursesi_screen->notty == TRUE) 609 return OK; 610 return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN, 611 &_cursesi_screen->savedtty) ? ERR : OK; 612 } 613 614 /* 615 * erasechar -- 616 * Return the character of the erase key. 617 */ 618 char 619 erasechar(void) 620 { 621 622 if (_cursesi_screen->notty == TRUE) 623 return 0; 624 return _cursesi_screen->baset.c_cc[VERASE]; 625 } 626 627 /* 628 * killchar -- 629 * Return the character of the kill key. 630 */ 631 char 632 killchar(void) 633 { 634 635 if (_cursesi_screen->notty == TRUE) 636 return 0; 637 return _cursesi_screen->baset.c_cc[VKILL]; 638 } 639 640 /* 641 * erasewchar -- 642 * Return the wide character of the erase key. 643 */ 644 int 645 erasewchar(wchar_t *ch) 646 { 647 648 #ifndef HAVE_WCHAR 649 return ERR; 650 #else 651 if (_cursesi_screen->notty == TRUE) 652 return ERR; 653 *ch = _cursesi_screen->baset.c_cc[VERASE]; 654 return OK; 655 #endif /* HAVE_WCHAR */ 656 } 657 658 /* 659 * killwchar -- 660 * Return the wide character of the kill key. 661 */ 662 int 663 killwchar( wchar_t *ch ) 664 { 665 666 #ifndef HAVE_WCHAR 667 return ERR; 668 #else 669 if (_cursesi_screen->notty == TRUE) 670 return 0; 671 *ch = _cursesi_screen->baset.c_cc[VKILL]; 672 return OK; 673 #endif /* HAVE_WCHAR */ 674 } 675 676 int 677 typeahead(int filedes) 678 { 679 680 _cursesi_screen->checkfd = filedes; 681 return OK; 682 } 683