1 /* 2 * Copyright (C) 1984-2011 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * Routines which deal with the characteristics of the terminal. 14 * Uses termcap to be as terminal-independent as possible. 15 */ 16 17 #include "less.h" 18 #include "cmd.h" 19 20 #if MSDOS_COMPILER 21 #include "pckeys.h" 22 #if MSDOS_COMPILER==MSOFTC 23 #include <graph.h> 24 #else 25 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 26 #include <conio.h> 27 #if MSDOS_COMPILER==DJGPPC 28 #include <pc.h> 29 extern int fd0; 30 #endif 31 #else 32 #if MSDOS_COMPILER==WIN32C 33 #include <windows.h> 34 #endif 35 #endif 36 #endif 37 #include <time.h> 38 39 #else 40 41 #if HAVE_SYS_IOCTL_H 42 #include <sys/ioctl.h> 43 #endif 44 45 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 46 #include <termios.h> 47 #else 48 #if HAVE_TERMIO_H 49 #include <termio.h> 50 #else 51 #if HAVE_SGSTAT_H 52 #include <sgstat.h> 53 #else 54 #include <sgtty.h> 55 #endif 56 #endif 57 #endif 58 59 #if HAVE_TERMCAP_H 60 #include <termcap.h> 61 #endif 62 #ifdef _OSK 63 #include <signal.h> 64 #endif 65 #if OS2 66 #include <sys/signal.h> 67 #include "pckeys.h" 68 #endif 69 #if HAVE_SYS_STREAM_H 70 #include <sys/stream.h> 71 #endif 72 #if HAVE_SYS_PTEM_H 73 #include <sys/ptem.h> 74 #endif 75 76 #endif /* MSDOS_COMPILER */ 77 78 /* 79 * Check for broken termios package that forces you to manually 80 * set the line discipline. 81 */ 82 #ifdef __ultrix__ 83 #define MUST_SET_LINE_DISCIPLINE 1 84 #else 85 #define MUST_SET_LINE_DISCIPLINE 0 86 #endif 87 88 #if OS2 89 #define DEFAULT_TERM "ansi" 90 static char *windowid; 91 #else 92 #define DEFAULT_TERM "unknown" 93 #endif 94 95 #if MSDOS_COMPILER==MSOFTC 96 static int videopages; 97 static long msec_loops; 98 static int flash_created = 0; 99 #define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } 100 #endif 101 102 #if MSDOS_COMPILER==BORLANDC 103 static unsigned short *whitescreen; 104 static int flash_created = 0; 105 #endif 106 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 107 #define _settextposition(y,x) gotoxy(x,y) 108 #define _clearscreen(m) clrscr() 109 #define _outtext(s) cputs(s) 110 #define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } 111 extern int sc_height; 112 #endif 113 114 #if MSDOS_COMPILER==WIN32C 115 struct keyRecord 116 { 117 int ascii; 118 int scan; 119 } currentKey; 120 121 static int keyCount = 0; 122 static WORD curr_attr; 123 static int pending_scancode = 0; 124 static WORD *whitescreen; 125 126 static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 127 static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 128 HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 129 130 extern int quitting; 131 static void win32_init_term(); 132 static void win32_deinit_term(); 133 134 #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) 135 #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) 136 #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 137 #define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ 138 if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ 139 error("SETCOLORS failed"); } 140 #endif 141 142 #if MSDOS_COMPILER 143 public int nm_fg_color; /* Color of normal text */ 144 public int nm_bg_color; 145 public int bo_fg_color; /* Color of bold text */ 146 public int bo_bg_color; 147 public int ul_fg_color; /* Color of underlined text */ 148 public int ul_bg_color; 149 public int so_fg_color; /* Color of standout text */ 150 public int so_bg_color; 151 public int bl_fg_color; /* Color of blinking text */ 152 public int bl_bg_color; 153 static int sy_fg_color; /* Color of system text (before less) */ 154 static int sy_bg_color; 155 156 #else 157 158 /* 159 * Strings passed to tputs() to do various terminal functions. 160 */ 161 static char 162 *sc_pad, /* Pad string */ 163 *sc_home, /* Cursor home */ 164 *sc_addline, /* Add line, scroll down following lines */ 165 *sc_lower_left, /* Cursor to last line, first column */ 166 *sc_return, /* Cursor to beginning of current line */ 167 *sc_move, /* General cursor positioning */ 168 *sc_clear, /* Clear screen */ 169 *sc_eol_clear, /* Clear to end of line */ 170 *sc_eos_clear, /* Clear to end of screen */ 171 *sc_s_in, /* Enter standout (highlighted) mode */ 172 *sc_s_out, /* Exit standout mode */ 173 *sc_u_in, /* Enter underline mode */ 174 *sc_u_out, /* Exit underline mode */ 175 *sc_b_in, /* Enter bold mode */ 176 *sc_b_out, /* Exit bold mode */ 177 *sc_bl_in, /* Enter blink mode */ 178 *sc_bl_out, /* Exit blink mode */ 179 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 180 *sc_backspace, /* Backspace cursor */ 181 *sc_s_keypad, /* Start keypad mode */ 182 *sc_e_keypad, /* End keypad mode */ 183 *sc_init, /* Startup terminal initialization */ 184 *sc_deinit; /* Exit terminal de-initialization */ 185 #endif 186 187 static int init_done = 0; 188 189 public int auto_wrap; /* Terminal does \r\n when write past margin */ 190 public int ignaw; /* Terminal ignores \n immediately after wrap */ 191 public int erase_char; /* The user's erase char */ 192 public int erase2_char; /* The user's other erase char */ 193 public int kill_char; /* The user's line-kill char */ 194 public int werase_char; /* The user's word-erase char */ 195 public int sc_width, sc_height; /* Height & width of screen */ 196 public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 197 public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 198 public int so_s_width, so_e_width; /* Printing width of standout seq */ 199 public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 200 public int above_mem, below_mem; /* Memory retained above/below screen */ 201 public int can_goto_line; /* Can move cursor to any line */ 202 public int clear_bg; /* Clear fills with background color */ 203 public int missing_cap = 0; /* Some capability is missing */ 204 205 static int attrmode = AT_NORMAL; 206 extern int binattr; 207 208 #if !MSDOS_COMPILER 209 static char *cheaper(); 210 static void tmodes(); 211 #endif 212 213 /* 214 * These two variables are sometimes defined in, 215 * and needed by, the termcap library. 216 */ 217 #if MUST_DEFINE_OSPEED 218 extern short ospeed; /* Terminal output baud rate */ 219 extern char PC; /* Pad character */ 220 #endif 221 #ifdef _OSK 222 short ospeed; 223 char PC_, *UP, *BC; 224 #endif 225 226 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 227 extern int no_back_scroll; 228 extern int swindow; 229 extern int no_init; 230 extern int no_keypad; 231 extern volatile sig_atomic_t sigs; 232 extern int wscroll; 233 extern int screen_trashed; 234 extern int tty; 235 extern int top_scroll; 236 extern int oldbot; 237 #if HILITE_SEARCH 238 extern int hilite_search; 239 #endif 240 241 extern char *tgetstr(); 242 extern char *tgoto(); 243 244 245 /* 246 * Change terminal to "raw mode", or restore to "normal" mode. 247 * "Raw mode" means 248 * 1. An outstanding read will complete on receipt of a single keystroke. 249 * 2. Input is not echoed. 250 * 3. On output, \n is mapped to \r\n. 251 * 4. \t is NOT expanded into spaces. 252 * 5. Signal-causing characters such as ctrl-C (interrupt), 253 * etc. are NOT disabled. 254 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 255 */ 256 public void 257 raw_mode(on) 258 int on; 259 { 260 static int curr_on = 0; 261 262 if (on == curr_on) 263 return; 264 erase2_char = '\b'; /* in case OS doesn't know about erase2 */ 265 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 266 { 267 struct termios s; 268 static struct termios save_term; 269 static int saved_term = 0; 270 271 if (on) 272 { 273 /* 274 * Get terminal modes. 275 */ 276 tcgetattr(tty, &s); 277 278 /* 279 * Save modes and set certain variables dependent on modes. 280 */ 281 if (!saved_term) 282 { 283 save_term = s; 284 saved_term = 1; 285 } 286 #if HAVE_OSPEED 287 switch (cfgetospeed(&s)) 288 { 289 #ifdef B0 290 case B0: ospeed = 0; break; 291 #endif 292 #ifdef B50 293 case B50: ospeed = 1; break; 294 #endif 295 #ifdef B75 296 case B75: ospeed = 2; break; 297 #endif 298 #ifdef B110 299 case B110: ospeed = 3; break; 300 #endif 301 #ifdef B134 302 case B134: ospeed = 4; break; 303 #endif 304 #ifdef B150 305 case B150: ospeed = 5; break; 306 #endif 307 #ifdef B200 308 case B200: ospeed = 6; break; 309 #endif 310 #ifdef B300 311 case B300: ospeed = 7; break; 312 #endif 313 #ifdef B600 314 case B600: ospeed = 8; break; 315 #endif 316 #ifdef B1200 317 case B1200: ospeed = 9; break; 318 #endif 319 #ifdef B1800 320 case B1800: ospeed = 10; break; 321 #endif 322 #ifdef B2400 323 case B2400: ospeed = 11; break; 324 #endif 325 #ifdef B4800 326 case B4800: ospeed = 12; break; 327 #endif 328 #ifdef B9600 329 case B9600: ospeed = 13; break; 330 #endif 331 #ifdef EXTA 332 case EXTA: ospeed = 14; break; 333 #endif 334 #ifdef EXTB 335 case EXTB: ospeed = 15; break; 336 #endif 337 #ifdef B57600 338 case B57600: ospeed = 16; break; 339 #endif 340 #ifdef B115200 341 case B115200: ospeed = 17; break; 342 #endif 343 default: ; 344 } 345 #endif 346 erase_char = s.c_cc[VERASE]; 347 #ifdef VERASE2 348 erase2_char = s.c_cc[VERASE2]; 349 #endif 350 kill_char = s.c_cc[VKILL]; 351 #ifdef VWERASE 352 werase_char = s.c_cc[VWERASE]; 353 #else 354 werase_char = CONTROL('W'); 355 #endif 356 357 /* 358 * Set the modes to the way we want them. 359 */ 360 s.c_lflag &= ~(0 361 #ifdef ICANON 362 | ICANON 363 #endif 364 #ifdef ECHO 365 | ECHO 366 #endif 367 #ifdef ECHOE 368 | ECHOE 369 #endif 370 #ifdef ECHOK 371 | ECHOK 372 #endif 373 #if ECHONL 374 | ECHONL 375 #endif 376 ); 377 378 s.c_oflag |= (0 379 #ifdef OXTABS 380 | OXTABS 381 #else 382 #ifdef TAB3 383 | TAB3 384 #else 385 #ifdef XTABS 386 | XTABS 387 #endif 388 #endif 389 #endif 390 #ifdef OPOST 391 | OPOST 392 #endif 393 #ifdef ONLCR 394 | ONLCR 395 #endif 396 ); 397 398 s.c_oflag &= ~(0 399 #ifdef ONOEOT 400 | ONOEOT 401 #endif 402 #ifdef OCRNL 403 | OCRNL 404 #endif 405 #ifdef ONOCR 406 | ONOCR 407 #endif 408 #ifdef ONLRET 409 | ONLRET 410 #endif 411 ); 412 s.c_cc[VMIN] = 1; 413 s.c_cc[VTIME] = 0; 414 #ifdef VLNEXT 415 s.c_cc[VLNEXT] = 0; 416 #endif 417 #ifdef VDSUSP 418 s.c_cc[VDSUSP] = 0; 419 #endif 420 #if MUST_SET_LINE_DISCIPLINE 421 /* 422 * System's termios is broken; need to explicitly 423 * request TERMIODISC line discipline. 424 */ 425 s.c_line = TERMIODISC; 426 #endif 427 } else 428 { 429 /* 430 * Restore saved modes. 431 */ 432 s = save_term; 433 } 434 tcsetattr(tty, TCSASOFT | TCSADRAIN, &s); 435 #if HAVE_FSYNC 436 fsync(tty); 437 #endif 438 #if MUST_SET_LINE_DISCIPLINE 439 if (!on) 440 { 441 /* 442 * Broken termios *ignores* any line discipline 443 * except TERMIODISC. A different old line discipline 444 * is therefore not restored, yet. Restore the old 445 * line discipline by hand. 446 */ 447 ioctl(tty, TIOCSETD, &save_term.c_line); 448 } 449 #endif 450 } 451 #else 452 #ifdef TCGETA 453 { 454 struct termio s; 455 static struct termio save_term; 456 static int saved_term = 0; 457 458 if (on) 459 { 460 /* 461 * Get terminal modes. 462 */ 463 ioctl(tty, TCGETA, &s); 464 465 /* 466 * Save modes and set certain variables dependent on modes. 467 */ 468 if (!saved_term) 469 { 470 save_term = s; 471 saved_term = 1; 472 } 473 #if HAVE_OSPEED 474 ospeed = s.c_cflag & CBAUD; 475 #endif 476 erase_char = s.c_cc[VERASE]; 477 kill_char = s.c_cc[VKILL]; 478 #ifdef VWERASE 479 werase_char = s.c_cc[VWERASE]; 480 #else 481 werase_char = CONTROL('W'); 482 #endif 483 484 /* 485 * Set the modes to the way we want them. 486 */ 487 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); 488 s.c_oflag |= (OPOST|ONLCR|TAB3); 489 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); 490 s.c_cc[VMIN] = 1; 491 s.c_cc[VTIME] = 0; 492 } else 493 { 494 /* 495 * Restore saved modes. 496 */ 497 s = save_term; 498 } 499 ioctl(tty, TCSETAW, &s); 500 } 501 #else 502 #ifdef TIOCGETP 503 { 504 struct sgttyb s; 505 static struct sgttyb save_term; 506 static int saved_term = 0; 507 508 if (on) 509 { 510 /* 511 * Get terminal modes. 512 */ 513 ioctl(tty, TIOCGETP, &s); 514 515 /* 516 * Save modes and set certain variables dependent on modes. 517 */ 518 if (!saved_term) 519 { 520 save_term = s; 521 saved_term = 1; 522 } 523 #if HAVE_OSPEED 524 ospeed = s.sg_ospeed; 525 #endif 526 erase_char = s.sg_erase; 527 kill_char = s.sg_kill; 528 werase_char = CONTROL('W'); 529 530 /* 531 * Set the modes to the way we want them. 532 */ 533 s.sg_flags |= CBREAK; 534 s.sg_flags &= ~(ECHO|XTABS); 535 } else 536 { 537 /* 538 * Restore saved modes. 539 */ 540 s = save_term; 541 } 542 ioctl(tty, TIOCSETN, &s); 543 } 544 #else 545 #ifdef _OSK 546 { 547 struct sgbuf s; 548 static struct sgbuf save_term; 549 static int saved_term = 0; 550 551 if (on) 552 { 553 /* 554 * Get terminal modes. 555 */ 556 _gs_opt(tty, &s); 557 558 /* 559 * Save modes and set certain variables dependent on modes. 560 */ 561 if (!saved_term) 562 { 563 save_term = s; 564 saved_term = 1; 565 } 566 erase_char = s.sg_bspch; 567 kill_char = s.sg_dlnch; 568 werase_char = CONTROL('W'); 569 570 /* 571 * Set the modes to the way we want them. 572 */ 573 s.sg_echo = 0; 574 s.sg_eofch = 0; 575 s.sg_pause = 0; 576 s.sg_psch = 0; 577 } else 578 { 579 /* 580 * Restore saved modes. 581 */ 582 s = save_term; 583 } 584 _ss_opt(tty, &s); 585 } 586 #else 587 /* MS-DOS, Windows, or OS2 */ 588 #if OS2 589 /* OS2 */ 590 LSIGNAL(SIGINT, SIG_IGN); 591 #endif 592 erase_char = '\b'; 593 #if MSDOS_COMPILER==DJGPPC 594 kill_char = CONTROL('U'); 595 /* 596 * So that when we shell out or run another program, its 597 * stdin is in cooked mode. We do not switch stdin to binary 598 * mode if fd0 is zero, since that means we were called before 599 * tty was reopened in open_getchr, in which case we would be 600 * changing the original stdin device outside less. 601 */ 602 if (fd0 != 0) 603 setmode(0, on ? O_BINARY : O_TEXT); 604 #else 605 kill_char = ESC; 606 #endif 607 werase_char = CONTROL('W'); 608 #endif 609 #endif 610 #endif 611 #endif 612 curr_on = on; 613 } 614 615 #if !MSDOS_COMPILER 616 /* 617 * Some glue to prevent calling termcap functions if tgetent() failed. 618 */ 619 static int hardcopy; 620 621 static char * 622 ltget_env(capname) 623 char *capname; 624 { 625 char name[16]; 626 char *s; 627 628 s = lgetenv("LESS_TERMCAP_DEBUG"); 629 if (s != NULL && *s != '\0') 630 { 631 struct env { struct env *next; char *name; char *value; }; 632 static struct env *envs = NULL; 633 struct env *p; 634 size_t len; 635 for (p = envs; p != NULL; p = p->next) 636 if (strcmp(p->name, capname) == 0) 637 return p->value; 638 p = (struct env *) ecalloc(1, sizeof(struct env)); 639 p->name = save(capname); 640 len = strlen(capname) + 3; 641 p->value = (char *) ecalloc(len, sizeof(char)); 642 snprintf(p->value, len, "<%s>", capname); 643 p->next = envs; 644 envs = p; 645 return p->value; 646 } 647 strlcpy(name, "LESS_TERMCAP_", sizeof(name)); 648 strlcat(name, capname, sizeof(name)); 649 return (lgetenv(name)); 650 } 651 652 static int 653 ltgetflag(capname) 654 char *capname; 655 { 656 char *s; 657 658 if ((s = ltget_env(capname)) != NULL) 659 return (*s != '\0' && *s != '0'); 660 if (hardcopy) 661 return (0); 662 return (tgetflag(capname)); 663 } 664 665 static int 666 ltgetnum(capname) 667 char *capname; 668 { 669 char *s; 670 671 if ((s = ltget_env(capname)) != NULL) 672 return (atoi(s)); 673 if (hardcopy) 674 return (-1); 675 return (tgetnum(capname)); 676 } 677 678 static char * 679 ltgetstr(capname, pp) 680 char *capname; 681 char **pp; 682 { 683 char *s; 684 685 if ((s = ltget_env(capname)) != NULL) 686 return (s); 687 if (hardcopy) 688 return (NULL); 689 return (tgetstr(capname, pp)); 690 } 691 #endif /* MSDOS_COMPILER */ 692 693 /* 694 * Get size of the output screen. 695 */ 696 public void 697 scrsize() 698 { 699 register char *s; 700 int sys_height; 701 int sys_width; 702 #if !MSDOS_COMPILER 703 int n; 704 #endif 705 706 #define DEF_SC_WIDTH 80 707 #if MSDOS_COMPILER 708 #define DEF_SC_HEIGHT 25 709 #else 710 #define DEF_SC_HEIGHT 24 711 #endif 712 713 714 sys_width = sys_height = 0; 715 716 #if MSDOS_COMPILER==MSOFTC 717 { 718 struct videoconfig w; 719 _getvideoconfig(&w); 720 sys_height = w.numtextrows; 721 sys_width = w.numtextcols; 722 } 723 #else 724 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 725 { 726 struct text_info w; 727 gettextinfo(&w); 728 sys_height = w.screenheight; 729 sys_width = w.screenwidth; 730 } 731 #else 732 #if MSDOS_COMPILER==WIN32C 733 { 734 CONSOLE_SCREEN_BUFFER_INFO scr; 735 GetConsoleScreenBufferInfo(con_out, &scr); 736 sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; 737 sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; 738 } 739 #else 740 #if OS2 741 { 742 int s[2]; 743 _scrsize(s); 744 sys_width = s[0]; 745 sys_height = s[1]; 746 /* 747 * When using terminal emulators for XFree86/OS2, the 748 * _scrsize function does not work well. 749 * Call the scrsize.exe program to get the window size. 750 */ 751 windowid = getenv("WINDOWID"); 752 if (windowid != NULL) 753 { 754 FILE *fd = popen("scrsize", "rt"); 755 if (fd != NULL) 756 { 757 int w, h; 758 fscanf(fd, "%i %i", &w, &h); 759 if (w > 0 && h > 0) 760 { 761 sys_width = w; 762 sys_height = h; 763 } 764 pclose(fd); 765 } 766 } 767 } 768 #else 769 #ifdef TIOCGWINSZ 770 { 771 struct winsize w; 772 if (ioctl(2, TIOCGWINSZ, &w) == 0) 773 { 774 if (w.ws_row > 0) 775 sys_height = w.ws_row; 776 if (w.ws_col > 0) 777 sys_width = w.ws_col; 778 } 779 } 780 #else 781 #ifdef WIOCGETD 782 { 783 struct uwdata w; 784 if (ioctl(2, WIOCGETD, &w) == 0) 785 { 786 if (w.uw_height > 0) 787 sys_height = w.uw_height / w.uw_vs; 788 if (w.uw_width > 0) 789 sys_width = w.uw_width / w.uw_hs; 790 } 791 } 792 #endif 793 #endif 794 #endif 795 #endif 796 #endif 797 #endif 798 799 if (sys_height > 0) 800 sc_height = sys_height; 801 else if ((s = lgetenv("LINES")) != NULL) 802 sc_height = atoi(s); 803 #if !MSDOS_COMPILER 804 else if ((n = ltgetnum("li")) > 0) 805 sc_height = n; 806 #endif 807 else 808 sc_height = DEF_SC_HEIGHT; 809 810 if (sys_width > 0) 811 sc_width = sys_width; 812 else if ((s = lgetenv("COLUMNS")) != NULL) 813 sc_width = atoi(s); 814 #if !MSDOS_COMPILER 815 else if ((n = ltgetnum("co")) > 0) 816 sc_width = n; 817 #endif 818 else 819 sc_width = DEF_SC_WIDTH; 820 } 821 822 #if MSDOS_COMPILER==MSOFTC 823 /* 824 * Figure out how many empty loops it takes to delay a millisecond. 825 */ 826 static void 827 get_clock() 828 { 829 clock_t start; 830 831 /* 832 * Get synchronized at the start of a tick. 833 */ 834 start = clock(); 835 while (clock() == start) 836 ; 837 /* 838 * Now count loops till the next tick. 839 */ 840 start = clock(); 841 msec_loops = 0; 842 while (clock() == start) 843 msec_loops++; 844 /* 845 * Convert from (loops per clock) to (loops per millisecond). 846 */ 847 msec_loops *= CLOCKS_PER_SEC; 848 msec_loops /= 1000; 849 } 850 851 /* 852 * Delay for a specified number of milliseconds. 853 */ 854 static void 855 dummy_func() 856 { 857 static long delay_dummy = 0; 858 delay_dummy++; 859 } 860 861 static void 862 delay(msec) 863 int msec; 864 { 865 long i; 866 867 while (msec-- > 0) 868 { 869 for (i = 0; i < msec_loops; i++) 870 { 871 /* 872 * Make it look like we're doing something here, 873 * so the optimizer doesn't remove the whole loop. 874 */ 875 dummy_func(); 876 } 877 } 878 } 879 #endif 880 881 /* 882 * Return the characters actually input by a "special" key. 883 */ 884 public char * 885 special_key_str(key) 886 int key; 887 { 888 static char tbuf[40]; 889 char *s; 890 #if MSDOS_COMPILER || OS2 891 static char k_right[] = { '\340', PCK_RIGHT, 0 }; 892 static char k_left[] = { '\340', PCK_LEFT, 0 }; 893 static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; 894 static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; 895 static char k_insert[] = { '\340', PCK_INSERT, 0 }; 896 static char k_delete[] = { '\340', PCK_DELETE, 0 }; 897 static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; 898 static char k_ctl_backspace[] = { '\177', 0 }; 899 static char k_home[] = { '\340', PCK_HOME, 0 }; 900 static char k_end[] = { '\340', PCK_END, 0 }; 901 static char k_up[] = { '\340', PCK_UP, 0 }; 902 static char k_down[] = { '\340', PCK_DOWN, 0 }; 903 static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; 904 static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; 905 static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; 906 static char k_f1[] = { '\340', PCK_F1, 0 }; 907 #endif 908 #if !MSDOS_COMPILER 909 char *sp = tbuf; 910 #endif 911 912 switch (key) 913 { 914 #if OS2 915 /* 916 * If windowid is not NULL, assume less is executed in 917 * the XFree86 environment. 918 */ 919 case SK_RIGHT_ARROW: 920 s = windowid ? ltgetstr("kr", &sp) : k_right; 921 break; 922 case SK_LEFT_ARROW: 923 s = windowid ? ltgetstr("kl", &sp) : k_left; 924 break; 925 case SK_UP_ARROW: 926 s = windowid ? ltgetstr("ku", &sp) : k_up; 927 break; 928 case SK_DOWN_ARROW: 929 s = windowid ? ltgetstr("kd", &sp) : k_down; 930 break; 931 case SK_PAGE_UP: 932 s = windowid ? ltgetstr("kP", &sp) : k_pageup; 933 break; 934 case SK_PAGE_DOWN: 935 s = windowid ? ltgetstr("kN", &sp) : k_pagedown; 936 break; 937 case SK_HOME: 938 s = windowid ? ltgetstr("kh", &sp) : k_home; 939 break; 940 case SK_END: 941 s = windowid ? ltgetstr("@7", &sp) : k_end; 942 break; 943 case SK_DELETE: 944 if (windowid) 945 { 946 s = ltgetstr("kD", &sp); 947 if (s == NULL) 948 { 949 tbuf[0] = '\177'; 950 tbuf[1] = '\0'; 951 s = tbuf; 952 } 953 } else 954 s = k_delete; 955 break; 956 #endif 957 #if MSDOS_COMPILER 958 case SK_RIGHT_ARROW: 959 s = k_right; 960 break; 961 case SK_LEFT_ARROW: 962 s = k_left; 963 break; 964 case SK_UP_ARROW: 965 s = k_up; 966 break; 967 case SK_DOWN_ARROW: 968 s = k_down; 969 break; 970 case SK_PAGE_UP: 971 s = k_pageup; 972 break; 973 case SK_PAGE_DOWN: 974 s = k_pagedown; 975 break; 976 case SK_HOME: 977 s = k_home; 978 break; 979 case SK_END: 980 s = k_end; 981 break; 982 case SK_DELETE: 983 s = k_delete; 984 break; 985 #endif 986 #if MSDOS_COMPILER || OS2 987 case SK_INSERT: 988 s = k_insert; 989 break; 990 case SK_CTL_LEFT_ARROW: 991 s = k_ctl_left; 992 break; 993 case SK_CTL_RIGHT_ARROW: 994 s = k_ctl_right; 995 break; 996 case SK_CTL_BACKSPACE: 997 s = k_ctl_backspace; 998 break; 999 case SK_CTL_DELETE: 1000 s = k_ctl_delete; 1001 break; 1002 case SK_F1: 1003 s = k_f1; 1004 break; 1005 case SK_BACKTAB: 1006 s = k_backtab; 1007 break; 1008 #else 1009 case SK_RIGHT_ARROW: 1010 s = ltgetstr("kr", &sp); 1011 break; 1012 case SK_LEFT_ARROW: 1013 s = ltgetstr("kl", &sp); 1014 break; 1015 case SK_UP_ARROW: 1016 s = ltgetstr("ku", &sp); 1017 break; 1018 case SK_DOWN_ARROW: 1019 s = ltgetstr("kd", &sp); 1020 break; 1021 case SK_PAGE_UP: 1022 s = ltgetstr("kP", &sp); 1023 break; 1024 case SK_PAGE_DOWN: 1025 s = ltgetstr("kN", &sp); 1026 break; 1027 case SK_HOME: 1028 s = ltgetstr("kh", &sp); 1029 break; 1030 case SK_END: 1031 s = ltgetstr("@7", &sp); 1032 break; 1033 case SK_DELETE: 1034 s = ltgetstr("kD", &sp); 1035 if (s == NULL) 1036 { 1037 tbuf[0] = '\177'; 1038 tbuf[1] = '\0'; 1039 s = tbuf; 1040 } 1041 break; 1042 #endif 1043 case SK_CONTROL_K: 1044 tbuf[0] = CONTROL('K'); 1045 tbuf[1] = '\0'; 1046 s = tbuf; 1047 break; 1048 default: 1049 return (NULL); 1050 } 1051 return (s); 1052 } 1053 1054 /* 1055 * Get terminal capabilities via termcap. 1056 */ 1057 public void 1058 get_term() 1059 { 1060 #if MSDOS_COMPILER 1061 auto_wrap = 1; 1062 ignaw = 0; 1063 can_goto_line = 1; 1064 clear_bg = 1; 1065 /* 1066 * Set up default colors. 1067 * The xx_s_width and xx_e_width vars are already initialized to 0. 1068 */ 1069 #if MSDOS_COMPILER==MSOFTC 1070 sy_bg_color = _getbkcolor(); 1071 sy_fg_color = _gettextcolor(); 1072 get_clock(); 1073 #else 1074 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1075 { 1076 struct text_info w; 1077 gettextinfo(&w); 1078 sy_bg_color = (w.attribute >> 4) & 0x0F; 1079 sy_fg_color = (w.attribute >> 0) & 0x0F; 1080 } 1081 #else 1082 #if MSDOS_COMPILER==WIN32C 1083 { 1084 DWORD nread; 1085 CONSOLE_SCREEN_BUFFER_INFO scr; 1086 1087 con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); 1088 /* 1089 * Always open stdin in binary. Note this *must* be done 1090 * before any file operations have been done on fd0. 1091 */ 1092 SET_BINARY(0); 1093 GetConsoleScreenBufferInfo(con_out, &scr); 1094 ReadConsoleOutputAttribute(con_out, &curr_attr, 1095 1, scr.dwCursorPosition, &nread); 1096 sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ 1097 sy_fg_color = curr_attr & FG_COLORS; 1098 } 1099 #endif 1100 #endif 1101 #endif 1102 nm_fg_color = sy_fg_color; 1103 nm_bg_color = sy_bg_color; 1104 bo_fg_color = 11; 1105 bo_bg_color = 0; 1106 ul_fg_color = 9; 1107 ul_bg_color = 0; 1108 so_fg_color = 15; 1109 so_bg_color = 9; 1110 bl_fg_color = 15; 1111 bl_bg_color = 0; 1112 1113 /* 1114 * Get size of the screen. 1115 */ 1116 scrsize(); 1117 pos_init(); 1118 1119 1120 #else /* !MSDOS_COMPILER */ 1121 1122 char *sp; 1123 register char *t1, *t2; 1124 char *term; 1125 char termbuf[TERMBUF_SIZE]; 1126 1127 static char sbuf[TERMSBUF_SIZE]; 1128 1129 #if OS2 1130 /* 1131 * Make sure the termcap database is available. 1132 */ 1133 sp = lgetenv("TERMCAP"); 1134 if (sp == NULL || *sp == '\0') 1135 { 1136 char *termcap; 1137 if ((sp = homefile("termcap.dat")) != NULL) 1138 { 1139 size_t len = strlen(sp) + 9; 1140 termcap = (char *) ecalloc(len, sizeof(char)); 1141 snprintf(termcap, len, "TERMCAP=%s", sp); 1142 free(sp); 1143 putenv(termcap); 1144 } 1145 } 1146 #endif 1147 /* 1148 * Find out what kind of terminal this is. 1149 */ 1150 if ((term = lgetenv("TERM")) == NULL) 1151 term = DEFAULT_TERM; 1152 hardcopy = 0; 1153 if (tgetent(termbuf, term) != TGETENT_OK) 1154 hardcopy = 1; 1155 if (ltgetflag("hc")) 1156 hardcopy = 1; 1157 1158 /* 1159 * Get size of the screen. 1160 */ 1161 scrsize(); 1162 pos_init(); 1163 1164 auto_wrap = ltgetflag("am"); 1165 ignaw = ltgetflag("xn"); 1166 above_mem = ltgetflag("da"); 1167 below_mem = ltgetflag("db"); 1168 clear_bg = ltgetflag("ut"); 1169 1170 /* 1171 * Assumes termcap variable "sg" is the printing width of: 1172 * the standout sequence, the end standout sequence, 1173 * the underline sequence, the end underline sequence, 1174 * the boldface sequence, and the end boldface sequence. 1175 */ 1176 if ((so_s_width = ltgetnum("sg")) < 0) 1177 so_s_width = 0; 1178 so_e_width = so_s_width; 1179 1180 bo_s_width = bo_e_width = so_s_width; 1181 ul_s_width = ul_e_width = so_s_width; 1182 bl_s_width = bl_e_width = so_s_width; 1183 1184 #if HILITE_SEARCH 1185 if (so_s_width > 0 || so_e_width > 0) 1186 /* 1187 * Disable highlighting by default on magic cookie terminals. 1188 * Turning on highlighting might change the displayed width 1189 * of a line, causing the display to get messed up. 1190 * The user can turn it back on with -g, 1191 * but she won't like the results. 1192 */ 1193 hilite_search = 0; 1194 #endif 1195 1196 /* 1197 * Get various string-valued capabilities. 1198 */ 1199 sp = sbuf; 1200 1201 #if HAVE_OSPEED 1202 sc_pad = ltgetstr("pc", &sp); 1203 if (sc_pad != NULL) 1204 PC = *sc_pad; 1205 #endif 1206 1207 sc_s_keypad = ltgetstr("ks", &sp); 1208 if (sc_s_keypad == NULL) 1209 sc_s_keypad = ""; 1210 sc_e_keypad = ltgetstr("ke", &sp); 1211 if (sc_e_keypad == NULL) 1212 sc_e_keypad = ""; 1213 1214 sc_init = ltgetstr("ti", &sp); 1215 if (sc_init == NULL) 1216 sc_init = ""; 1217 1218 sc_deinit= ltgetstr("te", &sp); 1219 if (sc_deinit == NULL) 1220 sc_deinit = ""; 1221 1222 sc_eol_clear = ltgetstr("ce", &sp); 1223 if (sc_eol_clear == NULL || *sc_eol_clear == '\0') 1224 { 1225 missing_cap = 1; 1226 sc_eol_clear = ""; 1227 } 1228 1229 sc_eos_clear = ltgetstr("cd", &sp); 1230 if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) 1231 { 1232 missing_cap = 1; 1233 sc_eos_clear = ""; 1234 } 1235 1236 sc_clear = ltgetstr("cl", &sp); 1237 if (sc_clear == NULL || *sc_clear == '\0') 1238 { 1239 missing_cap = 1; 1240 sc_clear = "\n\n"; 1241 } 1242 1243 sc_move = ltgetstr("cm", &sp); 1244 if (sc_move == NULL || *sc_move == '\0') 1245 { 1246 /* 1247 * This is not an error here, because we don't 1248 * always need sc_move. 1249 * We need it only if we don't have home or lower-left. 1250 */ 1251 sc_move = ""; 1252 can_goto_line = 0; 1253 } else 1254 can_goto_line = 1; 1255 1256 tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp); 1257 tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); 1258 tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); 1259 tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); 1260 1261 sc_visual_bell = ltgetstr("vb", &sp); 1262 if (sc_visual_bell == NULL) 1263 sc_visual_bell = ""; 1264 1265 if (ltgetflag("bs")) 1266 sc_backspace = "\b"; 1267 else 1268 { 1269 sc_backspace = ltgetstr("bc", &sp); 1270 if (sc_backspace == NULL || *sc_backspace == '\0') 1271 sc_backspace = "\b"; 1272 } 1273 1274 /* 1275 * Choose between using "ho" and "cm" ("home" and "cursor move") 1276 * to move the cursor to the upper left corner of the screen. 1277 */ 1278 t1 = ltgetstr("ho", &sp); 1279 if (t1 == NULL) 1280 t1 = ""; 1281 if (*sc_move == '\0') 1282 t2 = ""; 1283 else 1284 { 1285 strlcpy(sp, tgoto(sc_move, 0, 0), sbuf + sizeof(sbuf) - sp); 1286 t2 = sp; 1287 sp += strlen(sp) + 1; 1288 } 1289 sc_home = cheaper(t1, t2, "|\b^"); 1290 1291 /* 1292 * Choose between using "ll" and "cm" ("lower left" and "cursor move") 1293 * to move the cursor to the lower left corner of the screen. 1294 */ 1295 t1 = ltgetstr("ll", &sp); 1296 if (t1 == NULL) 1297 t1 = ""; 1298 if (*sc_move == '\0') 1299 t2 = ""; 1300 else 1301 { 1302 strlcpy(sp, tgoto(sc_move, 0, sc_height-1), 1303 sbuf + sizeof(sbuf) - sp); 1304 t2 = sp; 1305 sp += strlen(sp) + 1; 1306 } 1307 sc_lower_left = cheaper(t1, t2, "\r"); 1308 1309 /* 1310 * Get carriage return string. 1311 */ 1312 sc_return = ltgetstr("cr", &sp); 1313 if (sc_return == NULL) 1314 sc_return = "\r"; 1315 1316 /* 1317 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 1318 * to add a line at the top of the screen. 1319 */ 1320 t1 = ltgetstr("al", &sp); 1321 if (t1 == NULL) 1322 t1 = ""; 1323 t2 = ltgetstr("sr", &sp); 1324 if (t2 == NULL) 1325 t2 = ""; 1326 #if OS2 1327 if (*t1 == '\0' && *t2 == '\0') 1328 sc_addline = ""; 1329 else 1330 #endif 1331 if (above_mem) 1332 sc_addline = t1; 1333 else 1334 sc_addline = cheaper(t1, t2, ""); 1335 if (*sc_addline == '\0') 1336 { 1337 /* 1338 * Force repaint on any backward movement. 1339 */ 1340 no_back_scroll = 1; 1341 } 1342 #endif /* MSDOS_COMPILER */ 1343 } 1344 1345 #if !MSDOS_COMPILER 1346 /* 1347 * Return the cost of displaying a termcap string. 1348 * We use the trick of calling tputs, but as a char printing function 1349 * we give it inc_costcount, which just increments "costcount". 1350 * This tells us how many chars would be printed by using this string. 1351 * {{ Couldn't we just use strlen? }} 1352 */ 1353 static int costcount; 1354 1355 /*ARGSUSED*/ 1356 static int 1357 inc_costcount(c) 1358 int c; 1359 { 1360 costcount++; 1361 return (c); 1362 } 1363 1364 static int 1365 cost(t) 1366 char *t; 1367 { 1368 costcount = 0; 1369 tputs(t, sc_height, inc_costcount); 1370 return (costcount); 1371 } 1372 1373 /* 1374 * Return the "best" of the two given termcap strings. 1375 * The best, if both exist, is the one with the lower 1376 * cost (see cost() function). 1377 */ 1378 static char * 1379 cheaper(t1, t2, def) 1380 char *t1, *t2; 1381 char *def; 1382 { 1383 if (*t1 == '\0' && *t2 == '\0') 1384 { 1385 missing_cap = 1; 1386 return (def); 1387 } 1388 if (*t1 == '\0') 1389 return (t2); 1390 if (*t2 == '\0') 1391 return (t1); 1392 if (cost(t1) < cost(t2)) 1393 return (t1); 1394 return (t2); 1395 } 1396 1397 static void 1398 tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp) 1399 char *incap; 1400 char *outcap; 1401 char **instr; 1402 char **outstr; 1403 char *def_instr; 1404 char *def_outstr; 1405 char **spp; 1406 { 1407 *instr = ltgetstr(incap, spp); 1408 if (*instr == NULL) 1409 { 1410 /* Use defaults. */ 1411 *instr = def_instr; 1412 *outstr = def_outstr; 1413 return; 1414 } 1415 1416 *outstr = ltgetstr(outcap, spp); 1417 if (*outstr == NULL) 1418 /* No specific out capability; use "me". */ 1419 *outstr = ltgetstr("me", spp); 1420 if (*outstr == NULL) 1421 /* Don't even have "me"; use a null string. */ 1422 *outstr = ""; 1423 } 1424 1425 #endif /* MSDOS_COMPILER */ 1426 1427 1428 /* 1429 * Below are the functions which perform all the 1430 * terminal-specific screen manipulation. 1431 */ 1432 1433 1434 #if MSDOS_COMPILER 1435 1436 #if MSDOS_COMPILER==WIN32C 1437 static void 1438 _settextposition(int row, int col) 1439 { 1440 COORD cpos; 1441 CONSOLE_SCREEN_BUFFER_INFO csbi; 1442 1443 GetConsoleScreenBufferInfo(con_out, &csbi); 1444 cpos.X = csbi.srWindow.Left + (col - 1); 1445 cpos.Y = csbi.srWindow.Top + (row - 1); 1446 SetConsoleCursorPosition(con_out, cpos); 1447 } 1448 #endif 1449 1450 /* 1451 * Initialize the screen to the correct color at startup. 1452 */ 1453 static void 1454 initcolor() 1455 { 1456 SETCOLORS(nm_fg_color, nm_bg_color); 1457 #if 0 1458 /* 1459 * This clears the screen at startup. This is different from 1460 * the behavior of other versions of less. Disable it for now. 1461 */ 1462 char *blanks; 1463 int row; 1464 int col; 1465 1466 /* 1467 * Create a complete, blank screen using "normal" colors. 1468 */ 1469 SETCOLORS(nm_fg_color, nm_bg_color); 1470 blanks = (char *) ecalloc(width+1, sizeof(char)); 1471 for (col = 0; col < sc_width; col++) 1472 blanks[col] = ' '; 1473 blanks[sc_width] = '\0'; 1474 for (row = 0; row < sc_height; row++) 1475 _outtext(blanks); 1476 free(blanks); 1477 #endif 1478 } 1479 #endif 1480 1481 #if MSDOS_COMPILER==WIN32C 1482 1483 /* 1484 * Termcap-like init with a private win32 console. 1485 */ 1486 static void 1487 win32_init_term() 1488 { 1489 CONSOLE_SCREEN_BUFFER_INFO scr; 1490 COORD size; 1491 1492 if (con_out_save == INVALID_HANDLE_VALUE) 1493 return; 1494 1495 GetConsoleScreenBufferInfo(con_out_save, &scr); 1496 1497 if (con_out_ours == INVALID_HANDLE_VALUE) 1498 { 1499 /* 1500 * Create our own screen buffer, so that we 1501 * may restore the original when done. 1502 */ 1503 con_out_ours = CreateConsoleScreenBuffer( 1504 GENERIC_WRITE | GENERIC_READ, 1505 FILE_SHARE_WRITE | FILE_SHARE_READ, 1506 (LPSECURITY_ATTRIBUTES) NULL, 1507 CONSOLE_TEXTMODE_BUFFER, 1508 (LPVOID) NULL); 1509 } 1510 1511 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1512 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1513 SetConsoleScreenBufferSize(con_out_ours, size); 1514 SetConsoleActiveScreenBuffer(con_out_ours); 1515 con_out = con_out_ours; 1516 } 1517 1518 /* 1519 * Restore the startup console. 1520 */ 1521 static void 1522 win32_deinit_term() 1523 { 1524 if (con_out_save == INVALID_HANDLE_VALUE) 1525 return; 1526 if (quitting) 1527 (void) CloseHandle(con_out_ours); 1528 SetConsoleActiveScreenBuffer(con_out_save); 1529 con_out = con_out_save; 1530 } 1531 1532 #endif 1533 1534 /* 1535 * Initialize terminal 1536 */ 1537 public void 1538 init() 1539 { 1540 #if !MSDOS_COMPILER 1541 if (!no_init) 1542 tputs(sc_init, sc_height, putchr); 1543 if (!no_keypad) 1544 tputs(sc_s_keypad, sc_height, putchr); 1545 if (top_scroll) 1546 { 1547 int i; 1548 1549 /* 1550 * This is nice to terminals with no alternate screen, 1551 * but with saved scrolled-off-the-top lines. This way, 1552 * no previous line is lost, but we start with a whole 1553 * screen to ourself. 1554 */ 1555 for (i = 1; i < sc_height; i++) 1556 putchr('\n'); 1557 } else 1558 line_left(); 1559 #else 1560 #if MSDOS_COMPILER==WIN32C 1561 if (!no_init) 1562 win32_init_term(); 1563 #endif 1564 initcolor(); 1565 flush(); 1566 #endif 1567 init_done = 1; 1568 } 1569 1570 /* 1571 * Deinitialize terminal 1572 */ 1573 public void 1574 deinit() 1575 { 1576 if (!init_done) 1577 return; 1578 #if !MSDOS_COMPILER 1579 if (!no_keypad) 1580 tputs(sc_e_keypad, sc_height, putchr); 1581 if (!no_init) 1582 tputs(sc_deinit, sc_height, putchr); 1583 #else 1584 /* Restore system colors. */ 1585 SETCOLORS(sy_fg_color, sy_bg_color); 1586 #if MSDOS_COMPILER==WIN32C 1587 if (!no_init) 1588 win32_deinit_term(); 1589 #else 1590 /* Need clreol to make SETCOLORS take effect. */ 1591 clreol(); 1592 #endif 1593 #endif 1594 init_done = 0; 1595 } 1596 1597 /* 1598 * Home cursor (move to upper left corner of screen). 1599 */ 1600 public void 1601 home() 1602 { 1603 #if !MSDOS_COMPILER 1604 tputs(sc_home, 1, putchr); 1605 #else 1606 flush(); 1607 _settextposition(1,1); 1608 #endif 1609 } 1610 1611 /* 1612 * Add a blank line (called with cursor at home). 1613 * Should scroll the display down. 1614 */ 1615 public void 1616 add_line() 1617 { 1618 #if !MSDOS_COMPILER 1619 tputs(sc_addline, sc_height, putchr); 1620 #else 1621 flush(); 1622 #if MSDOS_COMPILER==MSOFTC 1623 _scrolltextwindow(_GSCROLLDOWN); 1624 _settextposition(1,1); 1625 #else 1626 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1627 movetext(1,1, sc_width,sc_height-1, 1,2); 1628 gotoxy(1,1); 1629 clreol(); 1630 #else 1631 #if MSDOS_COMPILER==WIN32C 1632 { 1633 CHAR_INFO fillchar; 1634 SMALL_RECT rcSrc, rcClip; 1635 COORD new_org; 1636 CONSOLE_SCREEN_BUFFER_INFO csbi; 1637 1638 GetConsoleScreenBufferInfo(con_out,&csbi); 1639 1640 /* The clip rectangle is the entire visible screen. */ 1641 rcClip.Left = csbi.srWindow.Left; 1642 rcClip.Top = csbi.srWindow.Top; 1643 rcClip.Right = csbi.srWindow.Right; 1644 rcClip.Bottom = csbi.srWindow.Bottom; 1645 1646 /* The source rectangle is the visible screen minus the last line. */ 1647 rcSrc = rcClip; 1648 rcSrc.Bottom--; 1649 1650 /* Move the top left corner of the source window down one row. */ 1651 new_org.X = rcSrc.Left; 1652 new_org.Y = rcSrc.Top + 1; 1653 1654 /* Fill the right character and attributes. */ 1655 fillchar.Char.AsciiChar = ' '; 1656 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1657 fillchar.Attributes = curr_attr; 1658 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1659 _settextposition(1,1); 1660 } 1661 #endif 1662 #endif 1663 #endif 1664 #endif 1665 } 1666 1667 #if 0 1668 /* 1669 * Remove the n topmost lines and scroll everything below it in the 1670 * window upward. This is needed to stop leaking the topmost line 1671 * into the scrollback buffer when we go down-one-line (in WIN32). 1672 */ 1673 public void 1674 remove_top(n) 1675 int n; 1676 { 1677 #if MSDOS_COMPILER==WIN32C 1678 SMALL_RECT rcSrc, rcClip; 1679 CHAR_INFO fillchar; 1680 COORD new_org; 1681 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 1682 1683 if (n >= sc_height - 1) 1684 { 1685 clear(); 1686 home(); 1687 return; 1688 } 1689 1690 flush(); 1691 1692 GetConsoleScreenBufferInfo(con_out, &csbi); 1693 1694 /* Get the extent of all-visible-rows-but-the-last. */ 1695 rcSrc.Left = csbi.srWindow.Left; 1696 rcSrc.Top = csbi.srWindow.Top + n; 1697 rcSrc.Right = csbi.srWindow.Right; 1698 rcSrc.Bottom = csbi.srWindow.Bottom; 1699 1700 /* Get the clip rectangle. */ 1701 rcClip.Left = rcSrc.Left; 1702 rcClip.Top = csbi.srWindow.Top; 1703 rcClip.Right = rcSrc.Right; 1704 rcClip.Bottom = rcSrc.Bottom ; 1705 1706 /* Move the source window up n rows. */ 1707 new_org.X = rcSrc.Left; 1708 new_org.Y = rcSrc.Top - n; 1709 1710 /* Fill the right character and attributes. */ 1711 fillchar.Char.AsciiChar = ' '; 1712 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1713 fillchar.Attributes = curr_attr; 1714 1715 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1716 1717 /* Position cursor on first blank line. */ 1718 goto_line(sc_height - n - 1); 1719 #endif 1720 } 1721 #endif 1722 1723 #if MSDOS_COMPILER==WIN32C 1724 /* 1725 * Clear the screen. 1726 */ 1727 static void 1728 win32_clear() 1729 { 1730 /* 1731 * This will clear only the currently visible rows of the NT 1732 * console buffer, which means none of the precious scrollback 1733 * rows are touched making for faster scrolling. Note that, if 1734 * the window has fewer columns than the console buffer (i.e. 1735 * there is a horizontal scrollbar as well), the entire width 1736 * of the visible rows will be cleared. 1737 */ 1738 COORD topleft; 1739 DWORD nchars; 1740 DWORD winsz; 1741 CONSOLE_SCREEN_BUFFER_INFO csbi; 1742 1743 /* get the number of cells in the current buffer */ 1744 GetConsoleScreenBufferInfo(con_out, &csbi); 1745 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 1746 topleft.X = 0; 1747 topleft.Y = csbi.srWindow.Top; 1748 1749 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1750 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 1751 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 1752 } 1753 1754 /* 1755 * Remove the n topmost lines and scroll everything below it in the 1756 * window upward. 1757 */ 1758 public void 1759 win32_scroll_up(n) 1760 int n; 1761 { 1762 SMALL_RECT rcSrc, rcClip; 1763 CHAR_INFO fillchar; 1764 COORD topleft; 1765 COORD new_org; 1766 DWORD nchars; 1767 DWORD size; 1768 CONSOLE_SCREEN_BUFFER_INFO csbi; 1769 1770 if (n <= 0) 1771 return; 1772 1773 if (n >= sc_height - 1) 1774 { 1775 win32_clear(); 1776 _settextposition(1,1); 1777 return; 1778 } 1779 1780 /* Get the extent of what will remain visible after scrolling. */ 1781 GetConsoleScreenBufferInfo(con_out, &csbi); 1782 rcSrc.Left = csbi.srWindow.Left; 1783 rcSrc.Top = csbi.srWindow.Top + n; 1784 rcSrc.Right = csbi.srWindow.Right; 1785 rcSrc.Bottom = csbi.srWindow.Bottom; 1786 1787 /* Get the clip rectangle. */ 1788 rcClip.Left = rcSrc.Left; 1789 rcClip.Top = csbi.srWindow.Top; 1790 rcClip.Right = rcSrc.Right; 1791 rcClip.Bottom = rcSrc.Bottom ; 1792 1793 /* Move the source text to the top of the screen. */ 1794 new_org.X = rcSrc.Left; 1795 new_org.Y = rcClip.Top; 1796 1797 /* Fill the right character and attributes. */ 1798 fillchar.Char.AsciiChar = ' '; 1799 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 1800 1801 /* Scroll the window. */ 1802 SetConsoleTextAttribute(con_out, fillchar.Attributes); 1803 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1804 1805 /* Clear remaining lines at bottom. */ 1806 topleft.X = csbi.dwCursorPosition.X; 1807 topleft.Y = rcSrc.Bottom - n; 1808 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 1809 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 1810 &nchars); 1811 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 1812 &nchars); 1813 SetConsoleTextAttribute(con_out, curr_attr); 1814 1815 /* Move cursor n lines up from where it was. */ 1816 csbi.dwCursorPosition.Y -= n; 1817 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 1818 } 1819 #endif 1820 1821 /* 1822 * Move cursor to lower left corner of screen. 1823 */ 1824 public void 1825 lower_left() 1826 { 1827 #if !MSDOS_COMPILER 1828 tputs(sc_lower_left, 1, putchr); 1829 #else 1830 flush(); 1831 _settextposition(sc_height, 1); 1832 #endif 1833 } 1834 1835 /* 1836 * Move cursor to left position of current line. 1837 */ 1838 public void 1839 line_left() 1840 { 1841 #if !MSDOS_COMPILER 1842 tputs(sc_return, 1, putchr); 1843 #else 1844 int row; 1845 flush(); 1846 #if MSDOS_COMPILER==WIN32C 1847 { 1848 CONSOLE_SCREEN_BUFFER_INFO scr; 1849 GetConsoleScreenBufferInfo(con_out, &scr); 1850 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 1851 } 1852 #else 1853 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1854 row = wherey(); 1855 #else 1856 { 1857 struct rccoord tpos = _gettextposition(); 1858 row = tpos.row; 1859 } 1860 #endif 1861 #endif 1862 _settextposition(row, 1); 1863 #endif 1864 } 1865 1866 /* 1867 * Check if the console size has changed and reset internals 1868 * (in lieu of SIGWINCH for WIN32). 1869 */ 1870 public void 1871 check_winch() 1872 { 1873 #if MSDOS_COMPILER==WIN32C 1874 CONSOLE_SCREEN_BUFFER_INFO scr; 1875 COORD size; 1876 1877 if (con_out == INVALID_HANDLE_VALUE) 1878 return; 1879 1880 flush(); 1881 GetConsoleScreenBufferInfo(con_out, &scr); 1882 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1883 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1884 if (size.Y != sc_height || size.X != sc_width) 1885 { 1886 sc_height = size.Y; 1887 sc_width = size.X; 1888 if (!no_init && con_out_ours == con_out) 1889 SetConsoleScreenBufferSize(con_out, size); 1890 pos_init(); 1891 wscroll = (sc_height + 1) / 2; 1892 screen_trashed = 1; 1893 } 1894 #endif 1895 } 1896 1897 /* 1898 * Goto a specific line on the screen. 1899 */ 1900 public void 1901 goto_line(slinenum) 1902 int slinenum; 1903 { 1904 #if !MSDOS_COMPILER 1905 tputs(tgoto(sc_move, 0, slinenum), 1, putchr); 1906 #else 1907 flush(); 1908 _settextposition(slinenum+1, 1); 1909 #endif 1910 } 1911 1912 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 1913 /* 1914 * Create an alternate screen which is all white. 1915 * This screen is used to create a "flash" effect, by displaying it 1916 * briefly and then switching back to the normal screen. 1917 * {{ Yuck! There must be a better way to get a visual bell. }} 1918 */ 1919 static void 1920 create_flash() 1921 { 1922 #if MSDOS_COMPILER==MSOFTC 1923 struct videoconfig w; 1924 char *blanks; 1925 int row, col; 1926 1927 _getvideoconfig(&w); 1928 videopages = w.numvideopages; 1929 if (videopages < 2) 1930 { 1931 at_enter(AT_STANDOUT); 1932 at_exit(); 1933 } else 1934 { 1935 _setactivepage(1); 1936 at_enter(AT_STANDOUT); 1937 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 1938 for (col = 0; col < w.numtextcols; col++) 1939 blanks[col] = ' '; 1940 for (row = w.numtextrows; row > 0; row--) 1941 _outmem(blanks, w.numtextcols); 1942 _setactivepage(0); 1943 _setvisualpage(0); 1944 free(blanks); 1945 at_exit(); 1946 } 1947 #else 1948 #if MSDOS_COMPILER==BORLANDC 1949 register int n; 1950 1951 whitescreen = (unsigned short *) 1952 malloc(sc_width * sc_height * sizeof(short)); 1953 if (whitescreen == NULL) 1954 return; 1955 for (n = 0; n < sc_width * sc_height; n++) 1956 whitescreen[n] = 0x7020; 1957 #else 1958 #if MSDOS_COMPILER==WIN32C 1959 register int n; 1960 1961 whitescreen = (WORD *) 1962 malloc(sc_height * sc_width * sizeof(WORD)); 1963 if (whitescreen == NULL) 1964 return; 1965 /* Invert the standard colors. */ 1966 for (n = 0; n < sc_width * sc_height; n++) 1967 whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color); 1968 #endif 1969 #endif 1970 #endif 1971 flash_created = 1; 1972 } 1973 #endif /* MSDOS_COMPILER */ 1974 1975 /* 1976 * Output the "visual bell", if there is one. 1977 */ 1978 public void 1979 vbell() 1980 { 1981 #if !MSDOS_COMPILER 1982 if (*sc_visual_bell == '\0') 1983 return; 1984 tputs(sc_visual_bell, sc_height, putchr); 1985 #else 1986 #if MSDOS_COMPILER==DJGPPC 1987 ScreenVisualBell(); 1988 #else 1989 #if MSDOS_COMPILER==MSOFTC 1990 /* 1991 * Create a flash screen on the second video page. 1992 * Switch to that page, then switch back. 1993 */ 1994 if (!flash_created) 1995 create_flash(); 1996 if (videopages < 2) 1997 return; 1998 _setvisualpage(1); 1999 delay(100); 2000 _setvisualpage(0); 2001 #else 2002 #if MSDOS_COMPILER==BORLANDC 2003 unsigned short *currscreen; 2004 2005 /* 2006 * Get a copy of the current screen. 2007 * Display the flash screen. 2008 * Then restore the old screen. 2009 */ 2010 if (!flash_created) 2011 create_flash(); 2012 if (whitescreen == NULL) 2013 return; 2014 currscreen = (unsigned short *) 2015 malloc(sc_width * sc_height * sizeof(short)); 2016 if (currscreen == NULL) return; 2017 gettext(1, 1, sc_width, sc_height, currscreen); 2018 puttext(1, 1, sc_width, sc_height, whitescreen); 2019 delay(100); 2020 puttext(1, 1, sc_width, sc_height, currscreen); 2021 free(currscreen); 2022 #else 2023 #if MSDOS_COMPILER==WIN32C 2024 /* paint screen with an inverse color */ 2025 clear(); 2026 2027 /* leave it displayed for 100 msec. */ 2028 Sleep(100); 2029 2030 /* restore with a redraw */ 2031 repaint(); 2032 #endif 2033 #endif 2034 #endif 2035 #endif 2036 #endif 2037 } 2038 2039 /* 2040 * Make a noise. 2041 */ 2042 static void 2043 beep() 2044 { 2045 #if !MSDOS_COMPILER 2046 putchr(CONTROL('G')); 2047 #else 2048 #if MSDOS_COMPILER==WIN32C 2049 MessageBeep(0); 2050 #else 2051 write(STDOUT_FILENO, "\7", 1); 2052 #endif 2053 #endif 2054 } 2055 2056 /* 2057 * Ring the terminal bell. 2058 */ 2059 public void 2060 bell() 2061 { 2062 if (quiet == VERY_QUIET) 2063 vbell(); 2064 else 2065 beep(); 2066 } 2067 2068 /* 2069 * Clear the screen. 2070 */ 2071 public void 2072 clear() 2073 { 2074 #if !MSDOS_COMPILER 2075 tputs(sc_clear, sc_height, putchr); 2076 #else 2077 flush(); 2078 #if MSDOS_COMPILER==WIN32C 2079 win32_clear(); 2080 #else 2081 _clearscreen(_GCLEARSCREEN); 2082 #endif 2083 #endif 2084 } 2085 2086 /* 2087 * Clear from the cursor to the end of the cursor's line. 2088 * {{ This must not move the cursor. }} 2089 */ 2090 public void 2091 clear_eol() 2092 { 2093 #if !MSDOS_COMPILER 2094 tputs(sc_eol_clear, 1, putchr); 2095 #else 2096 #if MSDOS_COMPILER==MSOFTC 2097 short top, left; 2098 short bot, right; 2099 struct rccoord tpos; 2100 2101 flush(); 2102 /* 2103 * Save current state. 2104 */ 2105 tpos = _gettextposition(); 2106 _gettextwindow(&top, &left, &bot, &right); 2107 /* 2108 * Set a temporary window to the current line, 2109 * from the cursor's position to the right edge of the screen. 2110 * Then clear that window. 2111 */ 2112 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2113 _clearscreen(_GWINDOW); 2114 /* 2115 * Restore state. 2116 */ 2117 _settextwindow(top, left, bot, right); 2118 _settextposition(tpos.row, tpos.col); 2119 #else 2120 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2121 flush(); 2122 clreol(); 2123 #else 2124 #if MSDOS_COMPILER==WIN32C 2125 DWORD nchars; 2126 COORD cpos; 2127 CONSOLE_SCREEN_BUFFER_INFO scr; 2128 2129 flush(); 2130 memset(&scr, 0, sizeof(scr)); 2131 GetConsoleScreenBufferInfo(con_out, &scr); 2132 cpos.X = scr.dwCursorPosition.X; 2133 cpos.Y = scr.dwCursorPosition.Y; 2134 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2135 FillConsoleOutputAttribute(con_out, curr_attr, 2136 scr.dwSize.X - cpos.X, cpos, &nchars); 2137 FillConsoleOutputCharacter(con_out, ' ', 2138 scr.dwSize.X - cpos.X, cpos, &nchars); 2139 #endif 2140 #endif 2141 #endif 2142 #endif 2143 } 2144 2145 /* 2146 * Clear the current line. 2147 * Clear the screen if there's off-screen memory below the display. 2148 */ 2149 static void 2150 clear_eol_bot() 2151 { 2152 #if MSDOS_COMPILER 2153 clear_eol(); 2154 #else 2155 if (below_mem) 2156 tputs(sc_eos_clear, 1, putchr); 2157 else 2158 tputs(sc_eol_clear, 1, putchr); 2159 #endif 2160 } 2161 2162 /* 2163 * Clear the bottom line of the display. 2164 * Leave the cursor at the beginning of the bottom line. 2165 */ 2166 public void 2167 clear_bot() 2168 { 2169 /* 2170 * If we're in a non-normal attribute mode, temporarily exit 2171 * the mode while we do the clear. Some terminals fill the 2172 * cleared area with the current attribute. 2173 */ 2174 if (oldbot) 2175 lower_left(); 2176 else 2177 line_left(); 2178 2179 if (attrmode == AT_NORMAL) 2180 clear_eol_bot(); 2181 else 2182 { 2183 int saved_attrmode = attrmode; 2184 2185 at_exit(); 2186 clear_eol_bot(); 2187 at_enter(saved_attrmode); 2188 } 2189 } 2190 2191 public void 2192 at_enter(attr) 2193 int attr; 2194 { 2195 attr = apply_at_specials(attr); 2196 2197 #if !MSDOS_COMPILER 2198 /* The one with the most priority is last. */ 2199 if (attr & AT_UNDERLINE) 2200 tputs(sc_u_in, 1, putchr); 2201 if (attr & AT_BOLD) 2202 tputs(sc_b_in, 1, putchr); 2203 if (attr & AT_BLINK) 2204 tputs(sc_bl_in, 1, putchr); 2205 if (attr & AT_STANDOUT) 2206 tputs(sc_s_in, 1, putchr); 2207 #else 2208 flush(); 2209 /* The one with the most priority is first. */ 2210 if (attr & AT_STANDOUT) 2211 { 2212 SETCOLORS(so_fg_color, so_bg_color); 2213 } else if (attr & AT_BLINK) 2214 { 2215 SETCOLORS(bl_fg_color, bl_bg_color); 2216 } 2217 else if (attr & AT_BOLD) 2218 { 2219 SETCOLORS(bo_fg_color, bo_bg_color); 2220 } 2221 else if (attr & AT_UNDERLINE) 2222 { 2223 SETCOLORS(ul_fg_color, ul_bg_color); 2224 } 2225 #endif 2226 2227 attrmode = attr; 2228 } 2229 2230 public void 2231 at_exit() 2232 { 2233 #if !MSDOS_COMPILER 2234 /* Undo things in the reverse order we did them. */ 2235 if (attrmode & AT_STANDOUT) 2236 tputs(sc_s_out, 1, putchr); 2237 if (attrmode & AT_BLINK) 2238 tputs(sc_bl_out, 1, putchr); 2239 if (attrmode & AT_BOLD) 2240 tputs(sc_b_out, 1, putchr); 2241 if (attrmode & AT_UNDERLINE) 2242 tputs(sc_u_out, 1, putchr); 2243 #else 2244 flush(); 2245 SETCOLORS(nm_fg_color, nm_bg_color); 2246 #endif 2247 2248 attrmode = AT_NORMAL; 2249 } 2250 2251 public void 2252 at_switch(attr) 2253 int attr; 2254 { 2255 int new_attrmode = apply_at_specials(attr); 2256 int ignore_modes = AT_ANSI; 2257 2258 if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) 2259 { 2260 at_exit(); 2261 at_enter(attr); 2262 } 2263 } 2264 2265 public int 2266 is_at_equiv(attr1, attr2) 2267 int attr1; 2268 int attr2; 2269 { 2270 attr1 = apply_at_specials(attr1); 2271 attr2 = apply_at_specials(attr2); 2272 2273 return (attr1 == attr2); 2274 } 2275 2276 public int 2277 apply_at_specials(attr) 2278 int attr; 2279 { 2280 if (attr & AT_BINARY) 2281 attr |= binattr; 2282 if (attr & AT_HILITE) 2283 attr |= AT_STANDOUT; 2284 attr &= ~(AT_BINARY|AT_HILITE); 2285 2286 return attr; 2287 } 2288 2289 #if 0 /* No longer used */ 2290 /* 2291 * Erase the character to the left of the cursor 2292 * and move the cursor left. 2293 */ 2294 public void 2295 backspace() 2296 { 2297 #if !MSDOS_COMPILER 2298 /* 2299 * Erase the previous character by overstriking with a space. 2300 */ 2301 tputs(sc_backspace, 1, putchr); 2302 putchr(' '); 2303 tputs(sc_backspace, 1, putchr); 2304 #else 2305 #if MSDOS_COMPILER==MSOFTC 2306 struct rccoord tpos; 2307 2308 flush(); 2309 tpos = _gettextposition(); 2310 if (tpos.col <= 1) 2311 return; 2312 _settextposition(tpos.row, tpos.col-1); 2313 _outtext(" "); 2314 _settextposition(tpos.row, tpos.col-1); 2315 #else 2316 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2317 cputs("\b"); 2318 #else 2319 #if MSDOS_COMPILER==WIN32C 2320 COORD cpos; 2321 DWORD cChars; 2322 CONSOLE_SCREEN_BUFFER_INFO scr; 2323 2324 flush(); 2325 GetConsoleScreenBufferInfo(con_out, &scr); 2326 cpos = scr.dwCursorPosition; 2327 if (cpos.X <= 0) 2328 return; 2329 cpos.X--; 2330 SetConsoleCursorPosition(con_out, cpos); 2331 FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); 2332 SetConsoleCursorPosition(con_out, cpos); 2333 #endif 2334 #endif 2335 #endif 2336 #endif 2337 } 2338 #endif /* 0 */ 2339 2340 /* 2341 * Output a plain backspace, without erasing the previous char. 2342 */ 2343 public void 2344 putbs() 2345 { 2346 #if !MSDOS_COMPILER 2347 tputs(sc_backspace, 1, putchr); 2348 #else 2349 int row, col; 2350 2351 flush(); 2352 { 2353 #if MSDOS_COMPILER==MSOFTC 2354 struct rccoord tpos; 2355 tpos = _gettextposition(); 2356 row = tpos.row; 2357 col = tpos.col; 2358 #else 2359 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2360 row = wherey(); 2361 col = wherex(); 2362 #else 2363 #if MSDOS_COMPILER==WIN32C 2364 CONSOLE_SCREEN_BUFFER_INFO scr; 2365 GetConsoleScreenBufferInfo(con_out, &scr); 2366 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2367 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 2368 #endif 2369 #endif 2370 #endif 2371 } 2372 if (col <= 1) 2373 return; 2374 _settextposition(row, col-1); 2375 #endif /* MSDOS_COMPILER */ 2376 } 2377 2378 #if MSDOS_COMPILER==WIN32C 2379 /* 2380 * Determine whether an input character is waiting to be read. 2381 */ 2382 static int 2383 win32_kbhit(tty) 2384 HANDLE tty; 2385 { 2386 INPUT_RECORD ip; 2387 DWORD read; 2388 2389 if (keyCount > 0) 2390 return (TRUE); 2391 2392 currentKey.ascii = 0; 2393 currentKey.scan = 0; 2394 2395 /* 2396 * Wait for a real key-down event, but 2397 * ignore SHIFT and CONTROL key events. 2398 */ 2399 do 2400 { 2401 PeekConsoleInput(tty, &ip, 1, &read); 2402 if (read == 0) 2403 return (FALSE); 2404 ReadConsoleInput(tty, &ip, 1, &read); 2405 } while (ip.EventType != KEY_EVENT || 2406 ip.Event.KeyEvent.bKeyDown != TRUE || 2407 ip.Event.KeyEvent.wVirtualScanCode == 0 || 2408 ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 2409 ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || 2410 ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); 2411 2412 currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; 2413 currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; 2414 keyCount = ip.Event.KeyEvent.wRepeatCount; 2415 2416 if (ip.Event.KeyEvent.dwControlKeyState & 2417 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 2418 { 2419 switch (currentKey.scan) 2420 { 2421 case PCK_ALT_E: /* letter 'E' */ 2422 currentKey.ascii = 0; 2423 break; 2424 } 2425 } else if (ip.Event.KeyEvent.dwControlKeyState & 2426 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 2427 { 2428 switch (currentKey.scan) 2429 { 2430 case PCK_RIGHT: /* right arrow */ 2431 currentKey.scan = PCK_CTL_RIGHT; 2432 break; 2433 case PCK_LEFT: /* left arrow */ 2434 currentKey.scan = PCK_CTL_LEFT; 2435 break; 2436 case PCK_DELETE: /* delete */ 2437 currentKey.scan = PCK_CTL_DELETE; 2438 break; 2439 } 2440 } 2441 return (TRUE); 2442 } 2443 2444 /* 2445 * Read a character from the keyboard. 2446 */ 2447 public char 2448 WIN32getch(tty) 2449 int tty; 2450 { 2451 int ascii; 2452 2453 if (pending_scancode) 2454 { 2455 pending_scancode = 0; 2456 return ((char)(currentKey.scan & 0x00FF)); 2457 } 2458 2459 while (win32_kbhit((HANDLE)tty) == FALSE) 2460 { 2461 Sleep(20); 2462 if (ABORT_SIGS()) 2463 return ('\003'); 2464 continue; 2465 } 2466 keyCount --; 2467 ascii = currentKey.ascii; 2468 /* 2469 * On PC's, the extended keys return a 2 byte sequence beginning 2470 * with '00', so if the ascii code is 00, the next byte will be 2471 * the lsb of the scan code. 2472 */ 2473 pending_scancode = (ascii == 0x00); 2474 return ((char)ascii); 2475 } 2476 #endif 2477 2478 #if MSDOS_COMPILER 2479 /* 2480 */ 2481 public void 2482 WIN32setcolors(fg, bg) 2483 int fg; 2484 int bg; 2485 { 2486 SETCOLORS(fg, bg); 2487 } 2488 2489 /* 2490 */ 2491 public void 2492 WIN32textout(text, len) 2493 char *text; 2494 int len; 2495 { 2496 #if MSDOS_COMPILER==WIN32C 2497 DWORD written; 2498 WriteConsole(con_out, text, len, &written, NULL); 2499 #else 2500 char c = text[len]; 2501 text[len] = '\0'; 2502 cputs(text); 2503 text[len] = c; 2504 #endif 2505 } 2506 #endif 2507