1 /* 2 * Copyright (C) 1984-2017 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, see the README file. 8 */ 9 10 11 /* 12 * Routines which deal with the characteristics of the terminal. 13 * Uses termcap to be as terminal-independent as possible. 14 */ 15 16 #include "less.h" 17 #include "cmd.h" 18 19 #if MSDOS_COMPILER 20 #include "pckeys.h" 21 #if MSDOS_COMPILER==MSOFTC 22 #include <graph.h> 23 #else 24 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 25 #include <conio.h> 26 #if MSDOS_COMPILER==DJGPPC 27 #include <pc.h> 28 extern int fd0; 29 #endif 30 #else 31 #if MSDOS_COMPILER==WIN32C 32 #include <windows.h> 33 #endif 34 #endif 35 #endif 36 #include <time.h> 37 38 #else 39 40 #if HAVE_SYS_IOCTL_H 41 #include <sys/ioctl.h> 42 #endif 43 44 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 45 #include <termios.h> 46 #else 47 #if HAVE_TERMIO_H 48 #include <termio.h> 49 #else 50 #if HAVE_SGSTAT_H 51 #include <sgstat.h> 52 #else 53 #include <sgtty.h> 54 #endif 55 #endif 56 #endif 57 58 #if HAVE_TERMCAP_H 59 #include <termcap.h> 60 #endif 61 #ifdef _OSK 62 #include <signal.h> 63 #endif 64 #if OS2 65 #include <sys/signal.h> 66 #include "pckeys.h" 67 #endif 68 #if HAVE_SYS_STREAM_H 69 #include <sys/stream.h> 70 #endif 71 #if HAVE_SYS_PTEM_H 72 #include <sys/ptem.h> 73 #endif 74 75 #endif /* MSDOS_COMPILER */ 76 77 /* 78 * Check for broken termios package that forces you to manually 79 * set the line discipline. 80 */ 81 #ifdef __ultrix__ 82 #define MUST_SET_LINE_DISCIPLINE 1 83 #else 84 #define MUST_SET_LINE_DISCIPLINE 0 85 #endif 86 87 #if OS2 88 #define DEFAULT_TERM "ansi" 89 static char *windowid; 90 #else 91 #define DEFAULT_TERM "unknown" 92 #endif 93 94 #if MSDOS_COMPILER==MSOFTC 95 static int videopages; 96 static long msec_loops; 97 static int flash_created = 0; 98 #define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } 99 #endif 100 101 #if MSDOS_COMPILER==BORLANDC 102 static unsigned short *whitescreen; 103 static int flash_created = 0; 104 #endif 105 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 106 #define _settextposition(y,x) gotoxy(x,y) 107 #define _clearscreen(m) clrscr() 108 #define _outtext(s) cputs(s) 109 #define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } 110 extern int sc_height; 111 #endif 112 113 #if MSDOS_COMPILER==WIN32C 114 struct keyRecord 115 { 116 int ascii; 117 int scan; 118 } currentKey; 119 120 static int keyCount = 0; 121 static WORD curr_attr; 122 static int pending_scancode = 0; 123 static WORD *whitescreen; 124 125 static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 126 static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 127 HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 128 129 extern int quitting; 130 static void win32_init_term(); 131 static void win32_deinit_term(); 132 133 #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) 134 #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) 135 #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 136 #define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ 137 if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ 138 error("SETCOLORS failed"); } 139 #endif 140 141 #if MSDOS_COMPILER 142 public int nm_fg_color; /* Color of normal text */ 143 public int nm_bg_color; 144 public int bo_fg_color; /* Color of bold text */ 145 public int bo_bg_color; 146 public int ul_fg_color; /* Color of underlined text */ 147 public int ul_bg_color; 148 public int so_fg_color; /* Color of standout text */ 149 public int so_bg_color; 150 public int bl_fg_color; /* Color of blinking text */ 151 public int bl_bg_color; 152 static int sy_fg_color; /* Color of system text (before less) */ 153 static int sy_bg_color; 154 public int sgr_mode; /* Honor ANSI sequences rather than using above */ 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 extern int line_count; 208 209 #if !MSDOS_COMPILER 210 static char *cheaper(); 211 static void tmodes(); 212 #endif 213 214 /* 215 * These two variables are sometimes defined in, 216 * and needed by, the termcap library. 217 */ 218 #if MUST_DEFINE_OSPEED 219 extern short ospeed; /* Terminal output baud rate */ 220 extern char PC; /* Pad character */ 221 #endif 222 #ifdef _OSK 223 short ospeed; 224 char PC_, *UP, *BC; 225 #endif 226 227 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 228 extern int no_back_scroll; 229 extern int swindow; 230 extern int no_init; 231 extern int no_keypad; 232 extern int sigs; 233 extern int wscroll; 234 extern int screen_trashed; 235 extern int tty; 236 extern int top_scroll; 237 extern int quit_if_one_screen; 238 extern int oldbot; 239 #if HILITE_SEARCH 240 extern int hilite_search; 241 #endif 242 243 extern char *tgetstr(); 244 extern char *tgoto(); 245 246 247 /* 248 * Change terminal to "raw mode", or restore to "normal" mode. 249 * "Raw mode" means 250 * 1. An outstanding read will complete on receipt of a single keystroke. 251 * 2. Input is not echoed. 252 * 3. On output, \n is mapped to \r\n. 253 * 4. \t is NOT expanded into spaces. 254 * 5. Signal-causing characters such as ctrl-C (interrupt), 255 * etc. are NOT disabled. 256 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 257 */ 258 public void 259 raw_mode(on) 260 int on; 261 { 262 static int curr_on = 0; 263 264 if (on == curr_on) 265 return; 266 erase2_char = '\b'; /* in case OS doesn't know about erase2 */ 267 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 268 { 269 struct termios s; 270 static struct termios save_term; 271 static int saved_term = 0; 272 273 if (on) 274 { 275 /* 276 * Get terminal modes. 277 */ 278 tcgetattr(tty, &s); 279 280 /* 281 * Save modes and set certain variables dependent on modes. 282 */ 283 if (!saved_term) 284 { 285 save_term = s; 286 saved_term = 1; 287 } 288 #if HAVE_OSPEED 289 switch (cfgetospeed(&s)) 290 { 291 #ifdef B0 292 case B0: ospeed = 0; break; 293 #endif 294 #ifdef B50 295 case B50: ospeed = 1; break; 296 #endif 297 #ifdef B75 298 case B75: ospeed = 2; break; 299 #endif 300 #ifdef B110 301 case B110: ospeed = 3; break; 302 #endif 303 #ifdef B134 304 case B134: ospeed = 4; break; 305 #endif 306 #ifdef B150 307 case B150: ospeed = 5; break; 308 #endif 309 #ifdef B200 310 case B200: ospeed = 6; break; 311 #endif 312 #ifdef B300 313 case B300: ospeed = 7; break; 314 #endif 315 #ifdef B600 316 case B600: ospeed = 8; break; 317 #endif 318 #ifdef B1200 319 case B1200: ospeed = 9; break; 320 #endif 321 #ifdef B1800 322 case B1800: ospeed = 10; break; 323 #endif 324 #ifdef B2400 325 case B2400: ospeed = 11; break; 326 #endif 327 #ifdef B4800 328 case B4800: ospeed = 12; break; 329 #endif 330 #ifdef B9600 331 case B9600: ospeed = 13; break; 332 #endif 333 #ifdef EXTA 334 case EXTA: ospeed = 14; break; 335 #endif 336 #ifdef EXTB 337 case EXTB: ospeed = 15; break; 338 #endif 339 #ifdef B57600 340 case B57600: ospeed = 16; break; 341 #endif 342 #ifdef B115200 343 case B115200: ospeed = 17; break; 344 #endif 345 default: ; 346 } 347 #endif 348 erase_char = s.c_cc[VERASE]; 349 #ifdef VERASE2 350 erase2_char = s.c_cc[VERASE2]; 351 #endif 352 kill_char = s.c_cc[VKILL]; 353 #ifdef VWERASE 354 werase_char = s.c_cc[VWERASE]; 355 #else 356 werase_char = CONTROL('W'); 357 #endif 358 359 /* 360 * Set the modes to the way we want them. 361 */ 362 s.c_lflag &= ~(0 363 #ifdef ICANON 364 | ICANON 365 #endif 366 #ifdef ECHO 367 | ECHO 368 #endif 369 #ifdef ECHOE 370 | ECHOE 371 #endif 372 #ifdef ECHOK 373 | ECHOK 374 #endif 375 #if ECHONL 376 | ECHONL 377 #endif 378 ); 379 380 s.c_oflag |= (0 381 #ifdef OXTABS 382 | OXTABS 383 #else 384 #ifdef TAB3 385 | TAB3 386 #else 387 #ifdef XTABS 388 | XTABS 389 #endif 390 #endif 391 #endif 392 #ifdef OPOST 393 | OPOST 394 #endif 395 #ifdef ONLCR 396 | ONLCR 397 #endif 398 ); 399 400 s.c_oflag &= ~(0 401 #ifdef ONOEOT 402 | ONOEOT 403 #endif 404 #ifdef OCRNL 405 | OCRNL 406 #endif 407 #ifdef ONOCR 408 | ONOCR 409 #endif 410 #ifdef ONLRET 411 | ONLRET 412 #endif 413 ); 414 s.c_cc[VMIN] = 1; 415 s.c_cc[VTIME] = 0; 416 #ifdef VLNEXT 417 s.c_cc[VLNEXT] = 0; 418 #endif 419 #ifdef VDSUSP 420 s.c_cc[VDSUSP] = 0; 421 #endif 422 #if MUST_SET_LINE_DISCIPLINE 423 /* 424 * System's termios is broken; need to explicitly 425 * request TERMIODISC line discipline. 426 */ 427 s.c_line = TERMIODISC; 428 #endif 429 } else 430 { 431 /* 432 * Restore saved modes. 433 */ 434 s = save_term; 435 } 436 #if HAVE_FSYNC 437 fsync(tty); 438 #endif 439 tcsetattr(tty, TCSADRAIN, &s); 440 #if MUST_SET_LINE_DISCIPLINE 441 if (!on) 442 { 443 /* 444 * Broken termios *ignores* any line discipline 445 * except TERMIODISC. A different old line discipline 446 * is therefore not restored, yet. Restore the old 447 * line discipline by hand. 448 */ 449 ioctl(tty, TIOCSETD, &save_term.c_line); 450 } 451 #endif 452 } 453 #else 454 #ifdef TCGETA 455 { 456 struct termio s; 457 static struct termio save_term; 458 static int saved_term = 0; 459 460 if (on) 461 { 462 /* 463 * Get terminal modes. 464 */ 465 ioctl(tty, TCGETA, &s); 466 467 /* 468 * Save modes and set certain variables dependent on modes. 469 */ 470 if (!saved_term) 471 { 472 save_term = s; 473 saved_term = 1; 474 } 475 #if HAVE_OSPEED 476 ospeed = s.c_cflag & CBAUD; 477 #endif 478 erase_char = s.c_cc[VERASE]; 479 kill_char = s.c_cc[VKILL]; 480 #ifdef VWERASE 481 werase_char = s.c_cc[VWERASE]; 482 #else 483 werase_char = CONTROL('W'); 484 #endif 485 486 /* 487 * Set the modes to the way we want them. 488 */ 489 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); 490 s.c_oflag |= (OPOST|ONLCR|TAB3); 491 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); 492 s.c_cc[VMIN] = 1; 493 s.c_cc[VTIME] = 0; 494 } else 495 { 496 /* 497 * Restore saved modes. 498 */ 499 s = save_term; 500 } 501 ioctl(tty, TCSETAW, &s); 502 } 503 #else 504 #ifdef TIOCGETP 505 { 506 struct sgttyb s; 507 static struct sgttyb save_term; 508 static int saved_term = 0; 509 510 if (on) 511 { 512 /* 513 * Get terminal modes. 514 */ 515 ioctl(tty, TIOCGETP, &s); 516 517 /* 518 * Save modes and set certain variables dependent on modes. 519 */ 520 if (!saved_term) 521 { 522 save_term = s; 523 saved_term = 1; 524 } 525 #if HAVE_OSPEED 526 ospeed = s.sg_ospeed; 527 #endif 528 erase_char = s.sg_erase; 529 kill_char = s.sg_kill; 530 werase_char = CONTROL('W'); 531 532 /* 533 * Set the modes to the way we want them. 534 */ 535 s.sg_flags |= CBREAK; 536 s.sg_flags &= ~(ECHO|XTABS); 537 } else 538 { 539 /* 540 * Restore saved modes. 541 */ 542 s = save_term; 543 } 544 ioctl(tty, TIOCSETN, &s); 545 } 546 #else 547 #ifdef _OSK 548 { 549 struct sgbuf s; 550 static struct sgbuf save_term; 551 static int saved_term = 0; 552 553 if (on) 554 { 555 /* 556 * Get terminal modes. 557 */ 558 _gs_opt(tty, &s); 559 560 /* 561 * Save modes and set certain variables dependent on modes. 562 */ 563 if (!saved_term) 564 { 565 save_term = s; 566 saved_term = 1; 567 } 568 erase_char = s.sg_bspch; 569 kill_char = s.sg_dlnch; 570 werase_char = CONTROL('W'); 571 572 /* 573 * Set the modes to the way we want them. 574 */ 575 s.sg_echo = 0; 576 s.sg_eofch = 0; 577 s.sg_pause = 0; 578 s.sg_psch = 0; 579 } else 580 { 581 /* 582 * Restore saved modes. 583 */ 584 s = save_term; 585 } 586 _ss_opt(tty, &s); 587 } 588 #else 589 /* MS-DOS, Windows, or OS2 */ 590 #if OS2 591 /* OS2 */ 592 LSIGNAL(SIGINT, SIG_IGN); 593 #endif 594 erase_char = '\b'; 595 #if MSDOS_COMPILER==DJGPPC 596 kill_char = CONTROL('U'); 597 /* 598 * So that when we shell out or run another program, its 599 * stdin is in cooked mode. We do not switch stdin to binary 600 * mode if fd0 is zero, since that means we were called before 601 * tty was reopened in open_getchr, in which case we would be 602 * changing the original stdin device outside less. 603 */ 604 if (fd0 != 0) 605 setmode(0, on ? O_BINARY : O_TEXT); 606 #else 607 kill_char = ESC; 608 #endif 609 werase_char = CONTROL('W'); 610 #endif 611 #endif 612 #endif 613 #endif 614 curr_on = on; 615 } 616 617 #if !MSDOS_COMPILER 618 /* 619 * Some glue to prevent calling termcap functions if tgetent() failed. 620 */ 621 static int hardcopy; 622 623 static char * 624 ltget_env(capname) 625 char *capname; 626 { 627 char name[16]; 628 char *s; 629 630 s = lgetenv("LESS_TERMCAP_DEBUG"); 631 if (s != NULL && *s != '\0') 632 { 633 struct env { struct env *next; char *name; char *value; }; 634 static struct env *envs = NULL; 635 struct env *p; 636 for (p = envs; p != NULL; p = p->next) 637 if (strcmp(p->name, capname) == 0) 638 return p->value; 639 p = (struct env *) ecalloc(1, sizeof(struct env)); 640 p->name = save(capname); 641 p->value = (char *) ecalloc(strlen(capname)+3, sizeof(char)); 642 sprintf(p->value, "<%s>", capname); 643 p->next = envs; 644 envs = p; 645 return p->value; 646 } 647 strcpy(name, "LESS_TERMCAP_"); 648 strcat(name, capname); 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 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 if (sc_height <= 0) 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 if (sc_width <= 0) 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 sgr_mode = 0; 1113 1114 /* 1115 * Get size of the screen. 1116 */ 1117 scrsize(); 1118 pos_init(); 1119 1120 1121 #else /* !MSDOS_COMPILER */ 1122 1123 char *sp; 1124 char *t1, *t2; 1125 char *term; 1126 char termbuf[TERMBUF_SIZE]; 1127 1128 static char sbuf[TERMSBUF_SIZE]; 1129 1130 #if OS2 1131 /* 1132 * Make sure the termcap database is available. 1133 */ 1134 sp = lgetenv("TERMCAP"); 1135 if (sp == NULL || *sp == '\0') 1136 { 1137 char *termcap; 1138 if ((sp = homefile("termcap.dat")) != NULL) 1139 { 1140 termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); 1141 sprintf(termcap, "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 strcpy(sp, tgoto(sc_move, 0, 0)); 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 strcpy(sp, tgoto(sc_move, 0, sc_height-1)); 1303 t2 = sp; 1304 sp += strlen(sp) + 1; 1305 } 1306 sc_lower_left = cheaper(t1, t2, "\r"); 1307 1308 /* 1309 * Get carriage return string. 1310 */ 1311 sc_return = ltgetstr("cr", &sp); 1312 if (sc_return == NULL) 1313 sc_return = "\r"; 1314 1315 /* 1316 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 1317 * to add a line at the top of the screen. 1318 */ 1319 t1 = ltgetstr("al", &sp); 1320 if (t1 == NULL) 1321 t1 = ""; 1322 t2 = ltgetstr("sr", &sp); 1323 if (t2 == NULL) 1324 t2 = ""; 1325 #if OS2 1326 if (*t1 == '\0' && *t2 == '\0') 1327 sc_addline = ""; 1328 else 1329 #endif 1330 if (above_mem) 1331 sc_addline = t1; 1332 else 1333 sc_addline = cheaper(t1, t2, ""); 1334 if (*sc_addline == '\0') 1335 { 1336 /* 1337 * Force repaint on any backward movement. 1338 */ 1339 no_back_scroll = 1; 1340 } 1341 #endif /* MSDOS_COMPILER */ 1342 } 1343 1344 #if !MSDOS_COMPILER 1345 /* 1346 * Return the cost of displaying a termcap string. 1347 * We use the trick of calling tputs, but as a char printing function 1348 * we give it inc_costcount, which just increments "costcount". 1349 * This tells us how many chars would be printed by using this string. 1350 * {{ Couldn't we just use strlen? }} 1351 */ 1352 static int costcount; 1353 1354 /*ARGSUSED*/ 1355 static int 1356 inc_costcount(c) 1357 int c; 1358 { 1359 costcount++; 1360 return (c); 1361 } 1362 1363 static int 1364 cost(t) 1365 char *t; 1366 { 1367 costcount = 0; 1368 tputs(t, sc_height, inc_costcount); 1369 return (costcount); 1370 } 1371 1372 /* 1373 * Return the "best" of the two given termcap strings. 1374 * The best, if both exist, is the one with the lower 1375 * cost (see cost() function). 1376 */ 1377 static char * 1378 cheaper(t1, t2, def) 1379 char *t1, *t2; 1380 char *def; 1381 { 1382 if (*t1 == '\0' && *t2 == '\0') 1383 { 1384 missing_cap = 1; 1385 return (def); 1386 } 1387 if (*t1 == '\0') 1388 return (t2); 1389 if (*t2 == '\0') 1390 return (t1); 1391 if (cost(t1) < cost(t2)) 1392 return (t1); 1393 return (t2); 1394 } 1395 1396 static void 1397 tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp) 1398 char *incap; 1399 char *outcap; 1400 char **instr; 1401 char **outstr; 1402 char *def_instr; 1403 char *def_outstr; 1404 char **spp; 1405 { 1406 *instr = ltgetstr(incap, spp); 1407 if (*instr == NULL) 1408 { 1409 /* Use defaults. */ 1410 *instr = def_instr; 1411 *outstr = def_outstr; 1412 return; 1413 } 1414 1415 *outstr = ltgetstr(outcap, spp); 1416 if (*outstr == NULL) 1417 /* No specific out capability; use "me". */ 1418 *outstr = ltgetstr("me", spp); 1419 if (*outstr == NULL) 1420 /* Don't even have "me"; use a null string. */ 1421 *outstr = ""; 1422 } 1423 1424 #endif /* MSDOS_COMPILER */ 1425 1426 1427 /* 1428 * Below are the functions which perform all the 1429 * terminal-specific screen manipulation. 1430 */ 1431 1432 1433 #if MSDOS_COMPILER 1434 1435 #if MSDOS_COMPILER==WIN32C 1436 static void 1437 _settextposition(int row, int col) 1438 { 1439 COORD cpos; 1440 CONSOLE_SCREEN_BUFFER_INFO csbi; 1441 1442 GetConsoleScreenBufferInfo(con_out, &csbi); 1443 cpos.X = csbi.srWindow.Left + (col - 1); 1444 cpos.Y = csbi.srWindow.Top + (row - 1); 1445 SetConsoleCursorPosition(con_out, cpos); 1446 } 1447 #endif 1448 1449 /* 1450 * Initialize the screen to the correct color at startup. 1451 */ 1452 static void 1453 initcolor() 1454 { 1455 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1456 intensevideo(); 1457 #endif 1458 SETCOLORS(nm_fg_color, nm_bg_color); 1459 #if 0 1460 /* 1461 * This clears the screen at startup. This is different from 1462 * the behavior of other versions of less. Disable it for now. 1463 */ 1464 char *blanks; 1465 int row; 1466 int col; 1467 1468 /* 1469 * Create a complete, blank screen using "normal" colors. 1470 */ 1471 SETCOLORS(nm_fg_color, nm_bg_color); 1472 blanks = (char *) ecalloc(width+1, sizeof(char)); 1473 for (col = 0; col < sc_width; col++) 1474 blanks[col] = ' '; 1475 blanks[sc_width] = '\0'; 1476 for (row = 0; row < sc_height; row++) 1477 _outtext(blanks); 1478 free(blanks); 1479 #endif 1480 } 1481 #endif 1482 1483 #if MSDOS_COMPILER==WIN32C 1484 1485 /* 1486 * Termcap-like init with a private win32 console. 1487 */ 1488 static void 1489 win32_init_term() 1490 { 1491 CONSOLE_SCREEN_BUFFER_INFO scr; 1492 COORD size; 1493 1494 if (con_out_save == INVALID_HANDLE_VALUE) 1495 return; 1496 1497 GetConsoleScreenBufferInfo(con_out_save, &scr); 1498 1499 if (con_out_ours == INVALID_HANDLE_VALUE) 1500 { 1501 /* 1502 * Create our own screen buffer, so that we 1503 * may restore the original when done. 1504 */ 1505 con_out_ours = CreateConsoleScreenBuffer( 1506 GENERIC_WRITE | GENERIC_READ, 1507 FILE_SHARE_WRITE | FILE_SHARE_READ, 1508 (LPSECURITY_ATTRIBUTES) NULL, 1509 CONSOLE_TEXTMODE_BUFFER, 1510 (LPVOID) NULL); 1511 } 1512 1513 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1514 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1515 SetConsoleScreenBufferSize(con_out_ours, size); 1516 SetConsoleActiveScreenBuffer(con_out_ours); 1517 con_out = con_out_ours; 1518 } 1519 1520 /* 1521 * Restore the startup console. 1522 */ 1523 static void 1524 win32_deinit_term() 1525 { 1526 if (con_out_save == INVALID_HANDLE_VALUE) 1527 return; 1528 if (quitting) 1529 (void) CloseHandle(con_out_ours); 1530 SetConsoleActiveScreenBuffer(con_out_save); 1531 con_out = con_out_save; 1532 } 1533 1534 #endif 1535 1536 /* 1537 * Initialize terminal 1538 */ 1539 public void 1540 init() 1541 { 1542 #if !MSDOS_COMPILER 1543 if (quit_if_one_screen && line_count >= sc_height) 1544 quit_if_one_screen = FALSE; 1545 if (!no_init && !quit_if_one_screen) 1546 tputs(sc_init, sc_height, putchr); 1547 if (!no_keypad) 1548 tputs(sc_s_keypad, sc_height, putchr); 1549 if (top_scroll) 1550 { 1551 int i; 1552 1553 /* 1554 * This is nice to terminals with no alternate screen, 1555 * but with saved scrolled-off-the-top lines. This way, 1556 * no previous line is lost, but we start with a whole 1557 * screen to ourself. 1558 */ 1559 for (i = 1; i < sc_height; i++) 1560 putchr('\n'); 1561 } else 1562 line_left(); 1563 #else 1564 #if MSDOS_COMPILER==WIN32C 1565 if (!no_init) 1566 win32_init_term(); 1567 #endif 1568 initcolor(); 1569 flush(); 1570 #endif 1571 init_done = 1; 1572 } 1573 1574 /* 1575 * Deinitialize terminal 1576 */ 1577 public void 1578 deinit() 1579 { 1580 if (!init_done) 1581 return; 1582 #if !MSDOS_COMPILER 1583 if (!no_keypad) 1584 tputs(sc_e_keypad, sc_height, putchr); 1585 if (!no_init && !quit_if_one_screen) 1586 tputs(sc_deinit, sc_height, putchr); 1587 #else 1588 /* Restore system colors. */ 1589 SETCOLORS(sy_fg_color, sy_bg_color); 1590 #if MSDOS_COMPILER==WIN32C 1591 if (!no_init) 1592 win32_deinit_term(); 1593 #else 1594 /* Need clreol to make SETCOLORS take effect. */ 1595 clreol(); 1596 #endif 1597 #endif 1598 init_done = 0; 1599 } 1600 1601 /* 1602 * Home cursor (move to upper left corner of screen). 1603 */ 1604 public void 1605 home() 1606 { 1607 #if !MSDOS_COMPILER 1608 tputs(sc_home, 1, putchr); 1609 #else 1610 flush(); 1611 _settextposition(1,1); 1612 #endif 1613 } 1614 1615 /* 1616 * Add a blank line (called with cursor at home). 1617 * Should scroll the display down. 1618 */ 1619 public void 1620 add_line() 1621 { 1622 #if !MSDOS_COMPILER 1623 tputs(sc_addline, sc_height, putchr); 1624 #else 1625 flush(); 1626 #if MSDOS_COMPILER==MSOFTC 1627 _scrolltextwindow(_GSCROLLDOWN); 1628 _settextposition(1,1); 1629 #else 1630 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1631 movetext(1,1, sc_width,sc_height-1, 1,2); 1632 gotoxy(1,1); 1633 clreol(); 1634 #else 1635 #if MSDOS_COMPILER==WIN32C 1636 { 1637 CHAR_INFO fillchar; 1638 SMALL_RECT rcSrc, rcClip; 1639 COORD new_org; 1640 CONSOLE_SCREEN_BUFFER_INFO csbi; 1641 1642 GetConsoleScreenBufferInfo(con_out,&csbi); 1643 1644 /* The clip rectangle is the entire visible screen. */ 1645 rcClip.Left = csbi.srWindow.Left; 1646 rcClip.Top = csbi.srWindow.Top; 1647 rcClip.Right = csbi.srWindow.Right; 1648 rcClip.Bottom = csbi.srWindow.Bottom; 1649 1650 /* The source rectangle is the visible screen minus the last line. */ 1651 rcSrc = rcClip; 1652 rcSrc.Bottom--; 1653 1654 /* Move the top left corner of the source window down one row. */ 1655 new_org.X = rcSrc.Left; 1656 new_org.Y = rcSrc.Top + 1; 1657 1658 /* Fill the right character and attributes. */ 1659 fillchar.Char.AsciiChar = ' '; 1660 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1661 fillchar.Attributes = curr_attr; 1662 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1663 _settextposition(1,1); 1664 } 1665 #endif 1666 #endif 1667 #endif 1668 #endif 1669 } 1670 1671 #if 0 1672 /* 1673 * Remove the n topmost lines and scroll everything below it in the 1674 * window upward. This is needed to stop leaking the topmost line 1675 * into the scrollback buffer when we go down-one-line (in WIN32). 1676 */ 1677 public void 1678 remove_top(n) 1679 int n; 1680 { 1681 #if MSDOS_COMPILER==WIN32C 1682 SMALL_RECT rcSrc, rcClip; 1683 CHAR_INFO fillchar; 1684 COORD new_org; 1685 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 1686 1687 if (n >= sc_height - 1) 1688 { 1689 clear(); 1690 home(); 1691 return; 1692 } 1693 1694 flush(); 1695 1696 GetConsoleScreenBufferInfo(con_out, &csbi); 1697 1698 /* Get the extent of all-visible-rows-but-the-last. */ 1699 rcSrc.Left = csbi.srWindow.Left; 1700 rcSrc.Top = csbi.srWindow.Top + n; 1701 rcSrc.Right = csbi.srWindow.Right; 1702 rcSrc.Bottom = csbi.srWindow.Bottom; 1703 1704 /* Get the clip rectangle. */ 1705 rcClip.Left = rcSrc.Left; 1706 rcClip.Top = csbi.srWindow.Top; 1707 rcClip.Right = rcSrc.Right; 1708 rcClip.Bottom = rcSrc.Bottom ; 1709 1710 /* Move the source window up n rows. */ 1711 new_org.X = rcSrc.Left; 1712 new_org.Y = rcSrc.Top - n; 1713 1714 /* Fill the right character and attributes. */ 1715 fillchar.Char.AsciiChar = ' '; 1716 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1717 fillchar.Attributes = curr_attr; 1718 1719 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1720 1721 /* Position cursor on first blank line. */ 1722 goto_line(sc_height - n - 1); 1723 #endif 1724 } 1725 #endif 1726 1727 #if MSDOS_COMPILER==WIN32C 1728 /* 1729 * Clear the screen. 1730 */ 1731 static void 1732 win32_clear() 1733 { 1734 /* 1735 * This will clear only the currently visible rows of the NT 1736 * console buffer, which means none of the precious scrollback 1737 * rows are touched making for faster scrolling. Note that, if 1738 * the window has fewer columns than the console buffer (i.e. 1739 * there is a horizontal scrollbar as well), the entire width 1740 * of the visible rows will be cleared. 1741 */ 1742 COORD topleft; 1743 DWORD nchars; 1744 DWORD winsz; 1745 CONSOLE_SCREEN_BUFFER_INFO csbi; 1746 1747 /* get the number of cells in the current buffer */ 1748 GetConsoleScreenBufferInfo(con_out, &csbi); 1749 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 1750 topleft.X = 0; 1751 topleft.Y = csbi.srWindow.Top; 1752 1753 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1754 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 1755 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 1756 } 1757 1758 /* 1759 * Remove the n topmost lines and scroll everything below it in the 1760 * window upward. 1761 */ 1762 public void 1763 win32_scroll_up(n) 1764 int n; 1765 { 1766 SMALL_RECT rcSrc, rcClip; 1767 CHAR_INFO fillchar; 1768 COORD topleft; 1769 COORD new_org; 1770 DWORD nchars; 1771 DWORD size; 1772 CONSOLE_SCREEN_BUFFER_INFO csbi; 1773 1774 if (n <= 0) 1775 return; 1776 1777 if (n >= sc_height - 1) 1778 { 1779 win32_clear(); 1780 _settextposition(1,1); 1781 return; 1782 } 1783 1784 /* Get the extent of what will remain visible after scrolling. */ 1785 GetConsoleScreenBufferInfo(con_out, &csbi); 1786 rcSrc.Left = csbi.srWindow.Left; 1787 rcSrc.Top = csbi.srWindow.Top + n; 1788 rcSrc.Right = csbi.srWindow.Right; 1789 rcSrc.Bottom = csbi.srWindow.Bottom; 1790 1791 /* Get the clip rectangle. */ 1792 rcClip.Left = rcSrc.Left; 1793 rcClip.Top = csbi.srWindow.Top; 1794 rcClip.Right = rcSrc.Right; 1795 rcClip.Bottom = rcSrc.Bottom ; 1796 1797 /* Move the source text to the top of the screen. */ 1798 new_org.X = rcSrc.Left; 1799 new_org.Y = rcClip.Top; 1800 1801 /* Fill the right character and attributes. */ 1802 fillchar.Char.AsciiChar = ' '; 1803 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 1804 1805 /* Scroll the window. */ 1806 SetConsoleTextAttribute(con_out, fillchar.Attributes); 1807 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1808 1809 /* Clear remaining lines at bottom. */ 1810 topleft.X = csbi.dwCursorPosition.X; 1811 topleft.Y = rcSrc.Bottom - n; 1812 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 1813 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 1814 &nchars); 1815 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 1816 &nchars); 1817 SetConsoleTextAttribute(con_out, curr_attr); 1818 1819 /* Move cursor n lines up from where it was. */ 1820 csbi.dwCursorPosition.Y -= n; 1821 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 1822 } 1823 #endif 1824 1825 /* 1826 * Move cursor to lower left corner of screen. 1827 */ 1828 public void 1829 lower_left() 1830 { 1831 #if !MSDOS_COMPILER 1832 tputs(sc_lower_left, 1, putchr); 1833 #else 1834 flush(); 1835 _settextposition(sc_height, 1); 1836 #endif 1837 } 1838 1839 /* 1840 * Move cursor to left position of current line. 1841 */ 1842 public void 1843 line_left() 1844 { 1845 #if !MSDOS_COMPILER 1846 tputs(sc_return, 1, putchr); 1847 #else 1848 int row; 1849 flush(); 1850 #if MSDOS_COMPILER==WIN32C 1851 { 1852 CONSOLE_SCREEN_BUFFER_INFO scr; 1853 GetConsoleScreenBufferInfo(con_out, &scr); 1854 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 1855 } 1856 #else 1857 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1858 row = wherey(); 1859 #else 1860 { 1861 struct rccoord tpos = _gettextposition(); 1862 row = tpos.row; 1863 } 1864 #endif 1865 #endif 1866 _settextposition(row, 1); 1867 #endif 1868 } 1869 1870 /* 1871 * Check if the console size has changed and reset internals 1872 * (in lieu of SIGWINCH for WIN32). 1873 */ 1874 public void 1875 check_winch() 1876 { 1877 #if MSDOS_COMPILER==WIN32C 1878 CONSOLE_SCREEN_BUFFER_INFO scr; 1879 COORD size; 1880 1881 if (con_out == INVALID_HANDLE_VALUE) 1882 return; 1883 1884 flush(); 1885 GetConsoleScreenBufferInfo(con_out, &scr); 1886 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1887 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1888 if (size.Y != sc_height || size.X != sc_width) 1889 { 1890 sc_height = size.Y; 1891 sc_width = size.X; 1892 if (!no_init && con_out_ours == con_out) 1893 SetConsoleScreenBufferSize(con_out, size); 1894 pos_init(); 1895 wscroll = (sc_height + 1) / 2; 1896 screen_trashed = 1; 1897 } 1898 #endif 1899 } 1900 1901 /* 1902 * Goto a specific line on the screen. 1903 */ 1904 public void 1905 goto_line(slinenum) 1906 int slinenum; 1907 { 1908 #if !MSDOS_COMPILER 1909 tputs(tgoto(sc_move, 0, slinenum), 1, putchr); 1910 #else 1911 flush(); 1912 _settextposition(slinenum+1, 1); 1913 #endif 1914 } 1915 1916 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 1917 /* 1918 * Create an alternate screen which is all white. 1919 * This screen is used to create a "flash" effect, by displaying it 1920 * briefly and then switching back to the normal screen. 1921 * {{ Yuck! There must be a better way to get a visual bell. }} 1922 */ 1923 static void 1924 create_flash() 1925 { 1926 #if MSDOS_COMPILER==MSOFTC 1927 struct videoconfig w; 1928 char *blanks; 1929 int row, col; 1930 1931 _getvideoconfig(&w); 1932 videopages = w.numvideopages; 1933 if (videopages < 2) 1934 { 1935 at_enter(AT_STANDOUT); 1936 at_exit(); 1937 } else 1938 { 1939 _setactivepage(1); 1940 at_enter(AT_STANDOUT); 1941 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 1942 for (col = 0; col < w.numtextcols; col++) 1943 blanks[col] = ' '; 1944 for (row = w.numtextrows; row > 0; row--) 1945 _outmem(blanks, w.numtextcols); 1946 _setactivepage(0); 1947 _setvisualpage(0); 1948 free(blanks); 1949 at_exit(); 1950 } 1951 #else 1952 #if MSDOS_COMPILER==BORLANDC 1953 int n; 1954 1955 whitescreen = (unsigned short *) 1956 malloc(sc_width * sc_height * sizeof(short)); 1957 if (whitescreen == NULL) 1958 return; 1959 for (n = 0; n < sc_width * sc_height; n++) 1960 whitescreen[n] = 0x7020; 1961 #else 1962 #if MSDOS_COMPILER==WIN32C 1963 int n; 1964 1965 whitescreen = (WORD *) 1966 malloc(sc_height * sc_width * sizeof(WORD)); 1967 if (whitescreen == NULL) 1968 return; 1969 /* Invert the standard colors. */ 1970 for (n = 0; n < sc_width * sc_height; n++) 1971 whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color); 1972 #endif 1973 #endif 1974 #endif 1975 flash_created = 1; 1976 } 1977 #endif /* MSDOS_COMPILER */ 1978 1979 /* 1980 * Output the "visual bell", if there is one. 1981 */ 1982 public void 1983 vbell() 1984 { 1985 #if !MSDOS_COMPILER 1986 if (*sc_visual_bell == '\0') 1987 return; 1988 tputs(sc_visual_bell, sc_height, putchr); 1989 #else 1990 #if MSDOS_COMPILER==DJGPPC 1991 ScreenVisualBell(); 1992 #else 1993 #if MSDOS_COMPILER==MSOFTC 1994 /* 1995 * Create a flash screen on the second video page. 1996 * Switch to that page, then switch back. 1997 */ 1998 if (!flash_created) 1999 create_flash(); 2000 if (videopages < 2) 2001 return; 2002 _setvisualpage(1); 2003 delay(100); 2004 _setvisualpage(0); 2005 #else 2006 #if MSDOS_COMPILER==BORLANDC 2007 unsigned short *currscreen; 2008 2009 /* 2010 * Get a copy of the current screen. 2011 * Display the flash screen. 2012 * Then restore the old screen. 2013 */ 2014 if (!flash_created) 2015 create_flash(); 2016 if (whitescreen == NULL) 2017 return; 2018 currscreen = (unsigned short *) 2019 malloc(sc_width * sc_height * sizeof(short)); 2020 if (currscreen == NULL) return; 2021 gettext(1, 1, sc_width, sc_height, currscreen); 2022 puttext(1, 1, sc_width, sc_height, whitescreen); 2023 delay(100); 2024 puttext(1, 1, sc_width, sc_height, currscreen); 2025 free(currscreen); 2026 #else 2027 #if MSDOS_COMPILER==WIN32C 2028 /* paint screen with an inverse color */ 2029 clear(); 2030 2031 /* leave it displayed for 100 msec. */ 2032 Sleep(100); 2033 2034 /* restore with a redraw */ 2035 repaint(); 2036 #endif 2037 #endif 2038 #endif 2039 #endif 2040 #endif 2041 } 2042 2043 /* 2044 * Make a noise. 2045 */ 2046 static void 2047 beep() 2048 { 2049 #if !MSDOS_COMPILER 2050 putchr(CONTROL('G')); 2051 #else 2052 #if MSDOS_COMPILER==WIN32C 2053 MessageBeep(0); 2054 #else 2055 write(1, "\7", 1); 2056 #endif 2057 #endif 2058 } 2059 2060 /* 2061 * Ring the terminal bell. 2062 */ 2063 public void 2064 bell() 2065 { 2066 if (quiet == VERY_QUIET) 2067 vbell(); 2068 else 2069 beep(); 2070 } 2071 2072 /* 2073 * Clear the screen. 2074 */ 2075 public void 2076 clear() 2077 { 2078 #if !MSDOS_COMPILER 2079 tputs(sc_clear, sc_height, putchr); 2080 #else 2081 flush(); 2082 #if MSDOS_COMPILER==WIN32C 2083 win32_clear(); 2084 #else 2085 _clearscreen(_GCLEARSCREEN); 2086 #endif 2087 #endif 2088 } 2089 2090 /* 2091 * Clear from the cursor to the end of the cursor's line. 2092 * {{ This must not move the cursor. }} 2093 */ 2094 public void 2095 clear_eol() 2096 { 2097 #if !MSDOS_COMPILER 2098 tputs(sc_eol_clear, 1, putchr); 2099 #else 2100 #if MSDOS_COMPILER==MSOFTC 2101 short top, left; 2102 short bot, right; 2103 struct rccoord tpos; 2104 2105 flush(); 2106 /* 2107 * Save current state. 2108 */ 2109 tpos = _gettextposition(); 2110 _gettextwindow(&top, &left, &bot, &right); 2111 /* 2112 * Set a temporary window to the current line, 2113 * from the cursor's position to the right edge of the screen. 2114 * Then clear that window. 2115 */ 2116 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2117 _clearscreen(_GWINDOW); 2118 /* 2119 * Restore state. 2120 */ 2121 _settextwindow(top, left, bot, right); 2122 _settextposition(tpos.row, tpos.col); 2123 #else 2124 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2125 flush(); 2126 clreol(); 2127 #else 2128 #if MSDOS_COMPILER==WIN32C 2129 DWORD nchars; 2130 COORD cpos; 2131 CONSOLE_SCREEN_BUFFER_INFO scr; 2132 2133 flush(); 2134 memset(&scr, 0, sizeof(scr)); 2135 GetConsoleScreenBufferInfo(con_out, &scr); 2136 cpos.X = scr.dwCursorPosition.X; 2137 cpos.Y = scr.dwCursorPosition.Y; 2138 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2139 FillConsoleOutputAttribute(con_out, curr_attr, 2140 scr.dwSize.X - cpos.X, cpos, &nchars); 2141 FillConsoleOutputCharacter(con_out, ' ', 2142 scr.dwSize.X - cpos.X, cpos, &nchars); 2143 #endif 2144 #endif 2145 #endif 2146 #endif 2147 } 2148 2149 /* 2150 * Clear the current line. 2151 * Clear the screen if there's off-screen memory below the display. 2152 */ 2153 static void 2154 clear_eol_bot() 2155 { 2156 #if MSDOS_COMPILER 2157 clear_eol(); 2158 #else 2159 if (below_mem) 2160 tputs(sc_eos_clear, 1, putchr); 2161 else 2162 tputs(sc_eol_clear, 1, putchr); 2163 #endif 2164 } 2165 2166 /* 2167 * Clear the bottom line of the display. 2168 * Leave the cursor at the beginning of the bottom line. 2169 */ 2170 public void 2171 clear_bot() 2172 { 2173 /* 2174 * If we're in a non-normal attribute mode, temporarily exit 2175 * the mode while we do the clear. Some terminals fill the 2176 * cleared area with the current attribute. 2177 */ 2178 if (oldbot) 2179 lower_left(); 2180 else 2181 line_left(); 2182 2183 if (attrmode == AT_NORMAL) 2184 clear_eol_bot(); 2185 else 2186 { 2187 int saved_attrmode = attrmode; 2188 2189 at_exit(); 2190 clear_eol_bot(); 2191 at_enter(saved_attrmode); 2192 } 2193 } 2194 2195 public void 2196 at_enter(attr) 2197 int attr; 2198 { 2199 attr = apply_at_specials(attr); 2200 2201 #if !MSDOS_COMPILER 2202 /* The one with the most priority is last. */ 2203 if (attr & AT_UNDERLINE) 2204 tputs(sc_u_in, 1, putchr); 2205 if (attr & AT_BOLD) 2206 tputs(sc_b_in, 1, putchr); 2207 if (attr & AT_BLINK) 2208 tputs(sc_bl_in, 1, putchr); 2209 if (attr & AT_STANDOUT) 2210 tputs(sc_s_in, 1, putchr); 2211 #else 2212 flush(); 2213 /* The one with the most priority is first. */ 2214 if (attr & AT_STANDOUT) 2215 { 2216 SETCOLORS(so_fg_color, so_bg_color); 2217 } else if (attr & AT_BLINK) 2218 { 2219 SETCOLORS(bl_fg_color, bl_bg_color); 2220 } 2221 else if (attr & AT_BOLD) 2222 { 2223 SETCOLORS(bo_fg_color, bo_bg_color); 2224 } 2225 else if (attr & AT_UNDERLINE) 2226 { 2227 SETCOLORS(ul_fg_color, ul_bg_color); 2228 } 2229 #endif 2230 2231 attrmode = attr; 2232 } 2233 2234 public void 2235 at_exit() 2236 { 2237 #if !MSDOS_COMPILER 2238 /* Undo things in the reverse order we did them. */ 2239 if (attrmode & AT_STANDOUT) 2240 tputs(sc_s_out, 1, putchr); 2241 if (attrmode & AT_BLINK) 2242 tputs(sc_bl_out, 1, putchr); 2243 if (attrmode & AT_BOLD) 2244 tputs(sc_b_out, 1, putchr); 2245 if (attrmode & AT_UNDERLINE) 2246 tputs(sc_u_out, 1, putchr); 2247 #else 2248 flush(); 2249 SETCOLORS(nm_fg_color, nm_bg_color); 2250 #endif 2251 2252 attrmode = AT_NORMAL; 2253 } 2254 2255 public void 2256 at_switch(attr) 2257 int attr; 2258 { 2259 int new_attrmode = apply_at_specials(attr); 2260 int ignore_modes = AT_ANSI; 2261 2262 if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) 2263 { 2264 at_exit(); 2265 at_enter(attr); 2266 } 2267 } 2268 2269 public int 2270 is_at_equiv(attr1, attr2) 2271 int attr1; 2272 int attr2; 2273 { 2274 attr1 = apply_at_specials(attr1); 2275 attr2 = apply_at_specials(attr2); 2276 2277 return (attr1 == attr2); 2278 } 2279 2280 public int 2281 apply_at_specials(attr) 2282 int attr; 2283 { 2284 if (attr & AT_BINARY) 2285 attr |= binattr; 2286 if (attr & AT_HILITE) 2287 attr |= AT_STANDOUT; 2288 attr &= ~(AT_BINARY|AT_HILITE); 2289 2290 return attr; 2291 } 2292 2293 #if 0 /* No longer used */ 2294 /* 2295 * Erase the character to the left of the cursor 2296 * and move the cursor left. 2297 */ 2298 public void 2299 backspace() 2300 { 2301 #if !MSDOS_COMPILER 2302 /* 2303 * Erase the previous character by overstriking with a space. 2304 */ 2305 tputs(sc_backspace, 1, putchr); 2306 putchr(' '); 2307 tputs(sc_backspace, 1, putchr); 2308 #else 2309 #if MSDOS_COMPILER==MSOFTC 2310 struct rccoord tpos; 2311 2312 flush(); 2313 tpos = _gettextposition(); 2314 if (tpos.col <= 1) 2315 return; 2316 _settextposition(tpos.row, tpos.col-1); 2317 _outtext(" "); 2318 _settextposition(tpos.row, tpos.col-1); 2319 #else 2320 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2321 cputs("\b"); 2322 #else 2323 #if MSDOS_COMPILER==WIN32C 2324 COORD cpos; 2325 DWORD cChars; 2326 CONSOLE_SCREEN_BUFFER_INFO scr; 2327 2328 flush(); 2329 GetConsoleScreenBufferInfo(con_out, &scr); 2330 cpos = scr.dwCursorPosition; 2331 if (cpos.X <= 0) 2332 return; 2333 cpos.X--; 2334 SetConsoleCursorPosition(con_out, cpos); 2335 FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); 2336 SetConsoleCursorPosition(con_out, cpos); 2337 #endif 2338 #endif 2339 #endif 2340 #endif 2341 } 2342 #endif /* 0 */ 2343 2344 /* 2345 * Output a plain backspace, without erasing the previous char. 2346 */ 2347 public void 2348 putbs() 2349 { 2350 #if !MSDOS_COMPILER 2351 tputs(sc_backspace, 1, putchr); 2352 #else 2353 int row, col; 2354 2355 flush(); 2356 { 2357 #if MSDOS_COMPILER==MSOFTC 2358 struct rccoord tpos; 2359 tpos = _gettextposition(); 2360 row = tpos.row; 2361 col = tpos.col; 2362 #else 2363 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2364 row = wherey(); 2365 col = wherex(); 2366 #else 2367 #if MSDOS_COMPILER==WIN32C 2368 CONSOLE_SCREEN_BUFFER_INFO scr; 2369 GetConsoleScreenBufferInfo(con_out, &scr); 2370 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2371 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 2372 #endif 2373 #endif 2374 #endif 2375 } 2376 if (col <= 1) 2377 return; 2378 _settextposition(row, col-1); 2379 #endif /* MSDOS_COMPILER */ 2380 } 2381 2382 #if MSDOS_COMPILER==WIN32C 2383 /* 2384 * Determine whether an input character is waiting to be read. 2385 */ 2386 static int 2387 win32_kbhit(tty) 2388 HANDLE tty; 2389 { 2390 INPUT_RECORD ip; 2391 DWORD read; 2392 2393 if (keyCount > 0) 2394 return (TRUE); 2395 2396 currentKey.ascii = 0; 2397 currentKey.scan = 0; 2398 2399 /* 2400 * Wait for a real key-down event, but 2401 * ignore SHIFT and CONTROL key events. 2402 */ 2403 do 2404 { 2405 PeekConsoleInput(tty, &ip, 1, &read); 2406 if (read == 0) 2407 return (FALSE); 2408 ReadConsoleInput(tty, &ip, 1, &read); 2409 } while (ip.EventType != KEY_EVENT || 2410 ip.Event.KeyEvent.bKeyDown != TRUE || 2411 ip.Event.KeyEvent.wVirtualScanCode == 0 || 2412 ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 2413 ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || 2414 ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); 2415 2416 currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; 2417 currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; 2418 keyCount = ip.Event.KeyEvent.wRepeatCount; 2419 2420 if (ip.Event.KeyEvent.dwControlKeyState & 2421 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) 2422 { 2423 switch (currentKey.scan) 2424 { 2425 case PCK_ALT_E: /* letter 'E' */ 2426 currentKey.ascii = 0; 2427 break; 2428 } 2429 } else if (ip.Event.KeyEvent.dwControlKeyState & 2430 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 2431 { 2432 switch (currentKey.scan) 2433 { 2434 case PCK_RIGHT: /* right arrow */ 2435 currentKey.scan = PCK_CTL_RIGHT; 2436 break; 2437 case PCK_LEFT: /* left arrow */ 2438 currentKey.scan = PCK_CTL_LEFT; 2439 break; 2440 case PCK_DELETE: /* delete */ 2441 currentKey.scan = PCK_CTL_DELETE; 2442 break; 2443 } 2444 } else if (ip.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) 2445 { 2446 switch (currentKey.scan) 2447 { 2448 case PCK_SHIFT_TAB: /* tab */ 2449 currentKey.ascii = 0; 2450 break; 2451 } 2452 } 2453 2454 return (TRUE); 2455 } 2456 2457 /* 2458 * Read a character from the keyboard. 2459 */ 2460 public char 2461 WIN32getch(tty) 2462 int tty; 2463 { 2464 int ascii; 2465 2466 if (pending_scancode) 2467 { 2468 pending_scancode = 0; 2469 return ((char)(currentKey.scan & 0x00FF)); 2470 } 2471 2472 while (win32_kbhit((HANDLE)tty) == FALSE) 2473 { 2474 Sleep(20); 2475 if (ABORT_SIGS()) 2476 return ('\003'); 2477 continue; 2478 } 2479 keyCount --; 2480 ascii = currentKey.ascii; 2481 /* 2482 * On PC's, the extended keys return a 2 byte sequence beginning 2483 * with '00', so if the ascii code is 00, the next byte will be 2484 * the lsb of the scan code. 2485 */ 2486 pending_scancode = (ascii == 0x00); 2487 return ((char)ascii); 2488 } 2489 #endif 2490 2491 #if MSDOS_COMPILER 2492 /* 2493 */ 2494 public void 2495 WIN32setcolors(fg, bg) 2496 int fg; 2497 int bg; 2498 { 2499 SETCOLORS(fg, bg); 2500 } 2501 2502 /* 2503 */ 2504 public void 2505 WIN32textout(text, len) 2506 char *text; 2507 int len; 2508 { 2509 #if MSDOS_COMPILER==WIN32C 2510 DWORD written; 2511 WriteConsole(con_out, text, len, &written, NULL); 2512 #else 2513 char c = text[len]; 2514 text[len] = '\0'; 2515 cputs(text); 2516 text[len] = c; 2517 #endif 2518 } 2519 #endif 2520