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