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