1 /* 2 * Copyright (C) 1984-2025 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 /* 12 * Routines which deal with the characteristics of the terminal. 13 * Uses termcap to be as terminal-independent as possible. 14 */ 15 16 #include "less.h" 17 #include "cmd.h" 18 19 #if MSDOS_COMPILER 20 #include "pckeys.h" 21 #if MSDOS_COMPILER==MSOFTC 22 #include <graph.h> 23 #else 24 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 25 #include <conio.h> 26 #if MSDOS_COMPILER==DJGPPC 27 #include <pc.h> 28 extern int fd0; 29 #endif 30 #else 31 #if MSDOS_COMPILER==WIN32C 32 #include <windows.h> 33 #endif 34 #endif 35 #endif 36 #include <time.h> 37 38 #ifndef FOREGROUND_BLUE 39 #define FOREGROUND_BLUE 0x0001 40 #endif 41 #ifndef FOREGROUND_GREEN 42 #define FOREGROUND_GREEN 0x0002 43 #endif 44 #ifndef FOREGROUND_RED 45 #define FOREGROUND_RED 0x0004 46 #endif 47 #ifndef FOREGROUND_INTENSITY 48 #define FOREGROUND_INTENSITY 0x0008 49 #endif 50 51 #else 52 53 #if HAVE_SYS_IOCTL_H 54 #include <sys/ioctl.h> 55 #endif 56 57 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 58 #include <termios.h> 59 #else 60 #if HAVE_TERMIO_H 61 #include <termio.h> 62 #else 63 #if HAVE_SGSTAT_H 64 #include <sgstat.h> 65 #else 66 #include <sgtty.h> 67 #endif 68 #endif 69 #endif 70 71 #if HAVE_NCURSESW_TERMCAP_H 72 #include <ncursesw/termcap.h> 73 #else 74 #if HAVE_NCURSES_TERMCAP_H 75 #include <ncurses/termcap.h> 76 #else 77 #if HAVE_TERMCAP_H 78 #include <termcap.h> 79 #endif 80 #endif 81 #endif 82 #ifdef _OSK 83 #include <signal.h> 84 #endif 85 #if OS2 86 #include <sys/signal.h> 87 #include "pckeys.h" 88 #endif 89 #if HAVE_SYS_STREAM_H 90 #include <sys/stream.h> 91 #endif 92 #if HAVE_SYS_PTEM_H 93 #include <sys/ptem.h> 94 #endif 95 96 #endif /* MSDOS_COMPILER */ 97 98 /* 99 * Check for broken termios package that forces you to manually 100 * set the line discipline. 101 */ 102 #ifdef __ultrix__ 103 #define MUST_SET_LINE_DISCIPLINE 1 104 #else 105 #define MUST_SET_LINE_DISCIPLINE 0 106 #endif 107 108 #if OS2 109 #define DEFAULT_TERM "ansi" 110 static char *windowid; 111 #else 112 #define DEFAULT_TERM "unknown" 113 #endif 114 115 #if MSDOS_COMPILER==MSOFTC 116 static int videopages; 117 static long msec_loops; 118 static int flash_created = 0; 119 #define SET_FG_COLOR(fg) _settextcolor(fg) 120 #define SET_BG_COLOR(bg) _setbkcolor(bg) 121 #define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } 122 #endif 123 124 #if MSDOS_COMPILER==BORLANDC 125 static unsigned short *whitescreen; 126 static int flash_created = 0; 127 #endif 128 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 129 #define _settextposition(y,x) gotoxy(x,y) 130 #define _clearscreen(m) clrscr() 131 #define _outtext(s) cputs(s) 132 #define SET_FG_COLOR(fg) textcolor(fg) 133 #define SET_BG_COLOR(bg) textbackground(bg) 134 #define SETCOLORS(fg,bg) { SET_FG_COLOR(fg); SET_BG_COLOR(bg); } 135 extern int sc_height; 136 #endif 137 138 #if MSDOS_COMPILER==WIN32C 139 #define UTF8_MAX_LENGTH 4 140 141 static WORD curr_attr; 142 143 static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ 144 static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ 145 HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ 146 147 extern int utf_mode; 148 extern lbool quitting; 149 static void win32_init_term(); 150 static void win32_deinit_term(); 151 152 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 153 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 154 #endif 155 156 #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) 157 #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) 158 #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) 159 #define APPLY_COLORS() { if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ 160 error("SETCOLORS failed", NULL_PARG); } 161 #define SET_FG_COLOR(fg) { curr_attr &= ~0x0f; curr_attr |= (fg); APPLY_COLORS(); } 162 #define SET_BG_COLOR(bg) { curr_attr &= ~0xf0; curr_attr |= ((bg)<<4); APPLY_COLORS(); } 163 #define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); APPLY_COLORS(); } 164 #endif 165 166 #if MSDOS_COMPILER 167 public int nm_fg_color = CV_ERROR; /* Color of normal text */ 168 public int nm_bg_color = CV_ERROR; 169 public int nm_attr = 0; 170 public int bo_fg_color = CV_ERROR; /* Color of bold text */ 171 public int bo_bg_color = CV_ERROR; 172 public int bo_attr = 0; 173 public int ul_fg_color = CV_ERROR; /* Color of underlined text */ 174 public int ul_bg_color = CV_ERROR; 175 public int ul_attr = 0; 176 public int so_fg_color = CV_ERROR; /* Color of standout text */ 177 public int so_bg_color = CV_ERROR; 178 public int so_attr = 0; 179 public int bl_fg_color = CV_ERROR; /* Color of blinking text */ 180 public int bl_bg_color = CV_ERROR; 181 public int bl_attr = 0; 182 static int sy_fg_color; /* Color of system text (before less) */ 183 static int sy_bg_color; 184 public int sgr_mode; /* Honor ANSI sequences rather than using above */ 185 #if MSDOS_COMPILER==WIN32C 186 static DWORD init_console_output_mode; 187 extern DWORD init_console_input_mode; 188 extern DWORD curr_console_input_mode; 189 extern DWORD base_console_input_mode; 190 extern DWORD mouse_console_input_mode; 191 public int vt_enabled = -1; /* Is virtual terminal processing available? */ 192 #endif 193 #else 194 195 /* 196 * Strings passed to tputs() to do various terminal functions. 197 */ 198 static constant char 199 *sc_pad, /* Pad string */ 200 *sc_home, /* Cursor home */ 201 *sc_addline, /* Add line, scroll down following lines */ 202 *sc_lower_left, /* Cursor to last line, first column */ 203 *sc_return, /* Cursor to beginning of current line */ 204 *sc_move, /* General cursor positioning */ 205 *sc_clear, /* Clear screen */ 206 *sc_eol_clear, /* Clear to end of line */ 207 *sc_eos_clear, /* Clear to end of screen */ 208 *sc_s_in, /* Enter standout (highlighted) mode */ 209 *sc_s_out, /* Exit standout mode */ 210 *sc_u_in, /* Enter underline mode */ 211 *sc_u_out, /* Exit underline mode */ 212 *sc_b_in, /* Enter bold mode */ 213 *sc_b_out, /* Exit bold mode */ 214 *sc_bl_in, /* Enter blink mode */ 215 *sc_bl_out, /* Exit blink mode */ 216 *sc_visual_bell, /* Visual bell (flash screen) sequence */ 217 *sc_backspace, /* Backspace cursor */ 218 *sc_s_keypad, /* Start keypad mode */ 219 *sc_e_keypad, /* End keypad mode */ 220 *sc_s_mousecap, /* Start mouse capture mode */ 221 *sc_e_mousecap, /* End mouse capture mode */ 222 *sc_s_bracketed_paste, /* Start bracketed paste mode */ 223 *sc_e_bracketed_paste, /* End bracketed paste mode */ 224 *sc_suspend, /* Suspend screen updates */ 225 *sc_resume, /* Resume screen updates */ 226 *sc_init, /* Startup terminal initialization */ 227 *sc_deinit; /* Exit terminal de-initialization */ 228 229 static int attrcolor = -1; 230 #endif 231 232 static int init_done = 0; 233 234 public int auto_wrap; /* Terminal does \r\n when write past margin */ 235 public int ignaw; /* Terminal ignores \n immediately after wrap */ 236 public int erase_char; /* The user's erase char */ 237 public int erase2_char; /* The user's other erase char */ 238 public int kill_char; /* The user's line-kill char */ 239 public int werase_char; /* The user's word-erase char */ 240 public int sc_width, sc_height; /* Height & width of screen */ 241 public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ 242 public int ul_s_width, ul_e_width; /* Printing width of underline seq */ 243 public int so_s_width, so_e_width; /* Printing width of standout seq */ 244 public int bl_s_width, bl_e_width; /* Printing width of blink seq */ 245 public int above_mem, below_mem; /* Memory retained above/below screen */ 246 public int can_goto_line; /* Can move cursor to any line */ 247 public int clear_bg; /* Clear fills with background color */ 248 public lbool missing_cap = FALSE; /* Some capability is missing */ 249 public constant char *kent = NULL; /* Keypad ENTER sequence */ 250 public lbool term_init_done = FALSE; 251 public lbool full_screen = TRUE; 252 253 static int attrmode = AT_NORMAL; 254 static int termcap_debug = -1; 255 static int no_alt_screen; /* sc_init does not switch to alt screen */ 256 extern int binattr; 257 extern int one_screen; 258 extern int shell_lines; 259 260 #if !MSDOS_COMPILER 261 static constant char *cheaper(constant char *t1, constant char *t2, constant char *def); 262 static void tmodes(constant char *incap, constant char *outcap, constant char **instr, 263 constant char **outstr, constant char *def_instr, constant char *def_outstr, char **spp); 264 #endif 265 266 /* 267 * These two variables are sometimes defined in, 268 * and needed by, the termcap library. 269 */ 270 #if MUST_DEFINE_OSPEED 271 extern short ospeed; /* Terminal output baud rate */ 272 extern char PC; /* Pad character */ 273 #endif 274 #ifdef _OSK 275 short ospeed; 276 char PC_, *UP, *BC; 277 #endif 278 279 extern int quiet; /* If VERY_QUIET, use visual bell for bell */ 280 extern int no_vbell; 281 extern int no_back_scroll; 282 extern int no_init; 283 extern int no_keypad; 284 extern int sigs; 285 extern int top_scroll; 286 extern int quit_if_one_screen; 287 extern int oldbot; 288 extern int mousecap; 289 extern int is_tty; 290 extern int use_color; 291 extern int no_paste; 292 #if HILITE_SEARCH 293 extern int hilite_search; 294 #endif 295 #if MSDOS_COMPILER==WIN32C 296 extern int wscroll; 297 extern HANDLE tty; 298 #else 299 extern int tty; 300 #endif 301 302 #if (HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS) || defined(TCGETA) 303 /* 304 * Set termio flags for use by less. 305 */ 306 static void set_termio_flags( 307 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 308 struct termios *s 309 #else 310 struct termio *s 311 #endif 312 ) 313 { 314 s->c_lflag &= ~(0 315 #ifdef ICANON 316 | ICANON 317 #endif 318 #ifdef ECHO 319 | ECHO 320 #endif 321 #ifdef ECHOE 322 | ECHOE 323 #endif 324 #ifdef ECHOK 325 | ECHOK 326 #endif 327 #ifdef ECHONL 328 | ECHONL 329 #endif 330 ); 331 332 s->c_oflag |= (0 333 #ifdef OPOST 334 | OPOST 335 #endif 336 #ifdef ONLCR 337 | ONLCR 338 #endif 339 ); 340 341 s->c_oflag &= ~(0 342 #ifdef ONOEOT 343 | ONOEOT 344 #endif 345 #ifdef OCRNL 346 | OCRNL 347 #endif 348 #ifdef ONOCR 349 | ONOCR 350 #endif 351 #ifdef ONLRET 352 | ONLRET 353 #endif 354 ); 355 } 356 #endif 357 358 /* 359 * Change terminal to "raw mode", or restore to "normal" mode. 360 * "Raw mode" means 361 * 1. An outstanding read will complete on receipt of a single keystroke. 362 * 2. Input is not echoed. 363 * 3. On output, \n is mapped to \r\n. 364 * 4. \t is NOT expanded into spaces. 365 * 5. Signal-causing characters such as ctrl-C (interrupt), 366 * etc. are NOT disabled. 367 * It doesn't matter whether an input \n is mapped to \r, or vice versa. 368 */ 369 public void raw_mode(int on) 370 { 371 static int curr_on = 0; 372 373 if (on == curr_on) 374 return; 375 erase2_char = '\b'; /* in case OS doesn't know about erase2 */ 376 #if LESSTEST 377 if (is_lesstest()) 378 { 379 /* {{ For consistent conditions when running tests. }} */ 380 erase_char = '\b'; 381 kill_char = CONTROL('U'); 382 werase_char = CONTROL('W'); 383 } else 384 #endif /*LESSTEST*/ 385 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS 386 { 387 struct termios s; 388 static struct termios save_term; 389 static int saved_term = 0; 390 391 if (on) 392 { 393 /* 394 * Get terminal modes. 395 */ 396 if (tcgetattr(tty, &s) < 0) 397 { 398 erase_char = '\b'; 399 kill_char = CONTROL('U'); 400 werase_char = CONTROL('W'); 401 } else 402 { 403 /* 404 * Save modes and set certain variables dependent on modes. 405 */ 406 if (!saved_term) 407 { 408 save_term = s; 409 saved_term = 1; 410 } 411 #if HAVE_OSPEED 412 switch (cfgetospeed(&s)) 413 { 414 #ifdef B0 415 case B0: ospeed = 0; break; 416 #endif 417 #ifdef B50 418 case B50: ospeed = 1; break; 419 #endif 420 #ifdef B75 421 case B75: ospeed = 2; break; 422 #endif 423 #ifdef B110 424 case B110: ospeed = 3; break; 425 #endif 426 #ifdef B134 427 case B134: ospeed = 4; break; 428 #endif 429 #ifdef B150 430 case B150: ospeed = 5; break; 431 #endif 432 #ifdef B200 433 case B200: ospeed = 6; break; 434 #endif 435 #ifdef B300 436 case B300: ospeed = 7; break; 437 #endif 438 #ifdef B600 439 case B600: ospeed = 8; break; 440 #endif 441 #ifdef B1200 442 case B1200: ospeed = 9; break; 443 #endif 444 #ifdef B1800 445 case B1800: ospeed = 10; break; 446 #endif 447 #ifdef B2400 448 case B2400: ospeed = 11; break; 449 #endif 450 #ifdef B4800 451 case B4800: ospeed = 12; break; 452 #endif 453 #ifdef B9600 454 case B9600: ospeed = 13; break; 455 #endif 456 #ifdef EXTA 457 case EXTA: ospeed = 14; break; 458 #endif 459 #ifdef EXTB 460 case EXTB: ospeed = 15; break; 461 #endif 462 #ifdef B57600 463 case B57600: ospeed = 16; break; 464 #endif 465 #ifdef B115200 466 case B115200: ospeed = 17; break; 467 #endif 468 default: ; 469 } 470 #endif 471 erase_char = s.c_cc[VERASE]; 472 #ifdef VERASE2 473 erase2_char = s.c_cc[VERASE2]; 474 #endif 475 kill_char = s.c_cc[VKILL]; 476 #ifdef VWERASE 477 werase_char = s.c_cc[VWERASE]; 478 #else 479 werase_char = CONTROL('W'); 480 #endif 481 482 /* 483 * Set the modes to the way we want them. 484 */ 485 set_termio_flags(&s); 486 s.c_cc[VMIN] = 1; 487 s.c_cc[VTIME] = 0; 488 #ifdef VLNEXT 489 s.c_cc[VLNEXT] = 0; 490 #endif 491 #ifdef VDSUSP 492 s.c_cc[VDSUSP] = 0; 493 #endif 494 #ifdef VSTOP 495 s.c_cc[VSTOP] = 0; 496 #endif 497 #ifdef VSTART 498 s.c_cc[VSTART] = 0; 499 #endif 500 #ifdef VDISCARD 501 s.c_cc[VDISCARD] = 0; 502 #endif 503 #if MUST_SET_LINE_DISCIPLINE 504 /* 505 * System's termios is broken; need to explicitly 506 * request TERMIODISC line discipline. 507 */ 508 s.c_line = TERMIODISC; 509 #endif 510 } 511 } else 512 { 513 /* 514 * Restore saved modes. 515 */ 516 s = save_term; 517 } 518 #if HAVE_FSYNC 519 fsync(tty); 520 #endif 521 tcsetattr(tty, TCSADRAIN, &s); 522 #if MUST_SET_LINE_DISCIPLINE 523 if (!on) 524 { 525 /* 526 * Broken termios *ignores* any line discipline 527 * except TERMIODISC. A different old line discipline 528 * is therefore not restored, yet. Restore the old 529 * line discipline by hand. 530 */ 531 ioctl(tty, TIOCSETD, &save_term.c_line); 532 } 533 #endif 534 } 535 #else 536 #ifdef TCGETA 537 { 538 struct termio s; 539 static struct termio save_term; 540 static int saved_term = 0; 541 542 if (on) 543 { 544 /* 545 * Get terminal modes. 546 */ 547 ioctl(tty, TCGETA, &s); 548 549 /* 550 * Save modes and set certain variables dependent on modes. 551 */ 552 if (!saved_term) 553 { 554 save_term = s; 555 saved_term = 1; 556 } 557 #if HAVE_OSPEED 558 ospeed = s.c_cflag & CBAUD; 559 #endif 560 erase_char = s.c_cc[VERASE]; 561 kill_char = s.c_cc[VKILL]; 562 #ifdef VWERASE 563 werase_char = s.c_cc[VWERASE]; 564 #else 565 werase_char = CONTROL('W'); 566 #endif 567 568 /* 569 * Set the modes to the way we want them. 570 */ 571 set_termio_flags(&s); 572 s.c_cc[VMIN] = 1; 573 s.c_cc[VTIME] = 0; 574 #ifdef VSTOP 575 s.c_cc[VSTOP] = 0; 576 #endif 577 #ifdef VSTART 578 s.c_cc[VSTART] = 0; 579 #endif 580 } else 581 { 582 /* 583 * Restore saved modes. 584 */ 585 s = save_term; 586 } 587 ioctl(tty, TCSETAW, &s); 588 } 589 #else 590 #ifdef TIOCGETP 591 { 592 struct sgttyb s; 593 static struct sgttyb save_term; 594 static int saved_term = 0; 595 596 if (on) 597 { 598 /* 599 * Get terminal modes. 600 */ 601 ioctl(tty, TIOCGETP, &s); 602 603 /* 604 * Save modes and set certain variables dependent on modes. 605 */ 606 if (!saved_term) 607 { 608 save_term = s; 609 saved_term = 1; 610 } 611 #if HAVE_OSPEED 612 ospeed = s.sg_ospeed; 613 #endif 614 erase_char = s.sg_erase; 615 kill_char = s.sg_kill; 616 werase_char = CONTROL('W'); 617 618 /* 619 * Set the modes to the way we want them. 620 */ 621 s.sg_flags |= CBREAK; 622 s.sg_flags &= ~(ECHO); 623 } else 624 { 625 /* 626 * Restore saved modes. 627 */ 628 s = save_term; 629 } 630 ioctl(tty, TIOCSETN, &s); 631 } 632 #else 633 #ifdef _OSK 634 { 635 struct sgbuf s; 636 static struct sgbuf save_term; 637 static int saved_term = 0; 638 639 if (on) 640 { 641 /* 642 * Get terminal modes. 643 */ 644 _gs_opt(tty, &s); 645 646 /* 647 * Save modes and set certain variables dependent on modes. 648 */ 649 if (!saved_term) 650 { 651 save_term = s; 652 saved_term = 1; 653 } 654 erase_char = s.sg_bspch; 655 kill_char = s.sg_dlnch; 656 werase_char = CONTROL('W'); 657 658 /* 659 * Set the modes to the way we want them. 660 */ 661 s.sg_echo = 0; 662 s.sg_eofch = 0; 663 s.sg_pause = 0; 664 s.sg_psch = 0; 665 } else 666 { 667 /* 668 * Restore saved modes. 669 */ 670 s = save_term; 671 } 672 _ss_opt(tty, &s); 673 } 674 #else 675 /* MS-DOS, Windows, or OS2 */ 676 #if OS2 677 /* OS2 */ 678 LSIGNAL(SIGINT, SIG_IGN); 679 #endif 680 erase_char = '\b'; 681 #if MSDOS_COMPILER==DJGPPC 682 kill_char = CONTROL('U'); 683 /* 684 * So that when we shell out or run another program, its 685 * stdin is in cooked mode. We do not switch stdin to binary 686 * mode if fd0 is zero, since that means we were called before 687 * tty was reopened in open_getchr, in which case we would be 688 * changing the original stdin device outside less. 689 */ 690 if (fd0 != 0) 691 setmode(0, on ? O_BINARY : O_TEXT); 692 #else 693 kill_char = ESC; 694 #endif 695 werase_char = CONTROL('W'); 696 #endif 697 #endif 698 #endif 699 #endif 700 curr_on = on; 701 } 702 703 #if !MSDOS_COMPILER 704 /* 705 * Some glue to prevent calling termcap functions if tgetent() failed. 706 */ 707 static int hardcopy; 708 709 static constant char * ltget_env(constant char *capname) 710 { 711 char name[64]; 712 713 if (termcap_debug) 714 { 715 struct env { struct env *next; char *name; char *value; }; 716 static struct env *envs = NULL; 717 struct env *p; 718 for (p = envs; p != NULL; p = p->next) 719 if (strcmp(p->name, capname) == 0) 720 return p->value; 721 p = (struct env *) ecalloc(1, sizeof(struct env)); 722 p->name = save(capname); 723 p->value = (char *) ecalloc(strlen(capname)+3, sizeof(char)); 724 sprintf(p->value, "<%s>", capname); 725 p->next = envs; 726 envs = p; 727 return p->value; 728 } 729 SNPRINTF1(name, sizeof(name), "LESS_TERMCAP_%s", capname); 730 return (lgetenv(name)); 731 } 732 733 static int ltgetflag(constant char *capname) 734 { 735 constant char *s; 736 737 if ((s = ltget_env(capname)) != NULL) 738 return (*s != '\0' && *s != '0'); 739 if (hardcopy) 740 return (0); 741 return (tgetflag(capname)); 742 } 743 744 static int ltgetnum(constant char *capname) 745 { 746 constant char *s; 747 748 if ((s = ltget_env(capname)) != NULL) 749 return (atoi(s)); 750 if (hardcopy) 751 return (-1); 752 return (tgetnum(capname)); 753 } 754 755 static constant char * ltgetstr(constant char *capname, char **pp) 756 { 757 constant char *s; 758 759 if ((s = ltget_env(capname)) != NULL) 760 return (s); 761 if (hardcopy) 762 return (NULL); 763 return (tgetstr(capname, pp)); 764 } 765 #endif /* MSDOS_COMPILER */ 766 767 /* 768 * Get size of the output screen. 769 */ 770 static void scrsize(void) 771 { 772 constant char *s; 773 int sys_height; 774 int sys_width; 775 #if !MSDOS_COMPILER 776 int n; 777 #endif 778 779 #define DEF_SC_WIDTH 80 780 #if MSDOS_COMPILER 781 #define DEF_SC_HEIGHT 25 782 #else 783 #define DEF_SC_HEIGHT 24 784 #endif 785 786 787 sys_width = sys_height = 0; 788 789 #if LESSTEST 790 if (0) /* can't use is_lesstest(): ttyin_name may not be set by scan_option yet */ 791 #endif /*LESSTEST*/ 792 { 793 #if MSDOS_COMPILER==MSOFTC 794 { 795 struct videoconfig w; 796 _getvideoconfig(&w); 797 sys_height = w.numtextrows; 798 sys_width = w.numtextcols; 799 } 800 #else 801 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 802 { 803 struct text_info w; 804 gettextinfo(&w); 805 sys_height = w.screenheight; 806 sys_width = w.screenwidth; 807 } 808 #else 809 #if MSDOS_COMPILER==WIN32C 810 { 811 CONSOLE_SCREEN_BUFFER_INFO scr; 812 GetConsoleScreenBufferInfo(con_out, &scr); 813 sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; 814 sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; 815 } 816 #else 817 #if OS2 818 { 819 int s[2]; 820 _scrsize(s); 821 sys_width = s[0]; 822 sys_height = s[1]; 823 /* 824 * When using terminal emulators for XFree86/OS2, the 825 * _scrsize function does not work well. 826 * Call the scrsize.exe program to get the window size. 827 */ 828 windowid = getenv("WINDOWID"); 829 if (windowid != NULL) 830 { 831 FILE *fd = popen("scrsize", "rt"); 832 if (fd != NULL) 833 { 834 int w, h; 835 fscanf(fd, "%i %i", &w, &h); 836 if (w > 0 && h > 0) 837 { 838 sys_width = w; 839 sys_height = h; 840 } 841 pclose(fd); 842 } 843 } 844 } 845 #else 846 #ifdef TIOCGWINSZ 847 { 848 struct winsize w; 849 if (ioctl(2, TIOCGWINSZ, &w) == 0) 850 { 851 if (w.ws_row > 0) 852 sys_height = w.ws_row; 853 if (w.ws_col > 0) 854 sys_width = w.ws_col; 855 } 856 } 857 #else 858 #ifdef WIOCGETD 859 { 860 struct uwdata w; 861 if (ioctl(2, WIOCGETD, &w) == 0) 862 { 863 if (w.uw_height > 0) 864 sys_height = w.uw_height / w.uw_vs; 865 if (w.uw_width > 0) 866 sys_width = w.uw_width / w.uw_hs; 867 } 868 } 869 #endif 870 #endif 871 #endif 872 #endif 873 #endif 874 #endif 875 } 876 877 if (sys_height > 0) 878 sc_height = sys_height; 879 else if ((s = lgetenv("LINES")) != NULL) 880 sc_height = atoi(s); 881 #if !MSDOS_COMPILER 882 else if ((n = ltgetnum("li")) > 0) 883 sc_height = n; 884 #endif 885 if ((s = lgetenv("LESS_LINES")) != NULL) 886 { 887 int height = atoi(s); 888 sc_height = (height < 0) ? sc_height + height : height; 889 full_screen = FALSE; 890 } 891 if (sc_height <= 0) 892 sc_height = DEF_SC_HEIGHT; 893 894 if (sys_width > 0) 895 sc_width = sys_width; 896 else if ((s = lgetenv("COLUMNS")) != NULL) 897 sc_width = atoi(s); 898 #if !MSDOS_COMPILER 899 else if ((n = ltgetnum("co")) > 0) 900 sc_width = n; 901 #endif 902 if ((s = lgetenv("LESS_COLUMNS")) != NULL) 903 { 904 int width = atoi(s); 905 sc_width = (width < 0) ? sc_width + width : width; 906 } 907 if (sc_width <= 0) 908 sc_width = DEF_SC_WIDTH; 909 screen_size_changed(); 910 } 911 912 /* 913 * Recalculate things that depend on the screen size. 914 */ 915 public void screen_size_changed(void) 916 { 917 calc_jump_sline(); 918 calc_shift_count(); 919 calc_match_shift(); 920 } 921 922 #if MSDOS_COMPILER==MSOFTC 923 /* 924 * Figure out how many empty loops it takes to delay a millisecond. 925 */ 926 static void get_clock(void) 927 { 928 clock_t start; 929 930 /* 931 * Get synchronized at the start of a tick. 932 */ 933 start = clock(); 934 while (clock() == start) 935 ; 936 /* 937 * Now count loops till the next tick. 938 */ 939 start = clock(); 940 msec_loops = 0; 941 while (clock() == start) 942 msec_loops++; 943 /* 944 * Convert from (loops per clock) to (loops per millisecond). 945 */ 946 msec_loops *= CLOCKS_PER_SEC; 947 msec_loops /= 1000; 948 } 949 950 /* 951 * Delay for a specified number of milliseconds. 952 */ 953 static void delay(int msec) 954 { 955 long i; 956 957 while (msec-- > 0) 958 { 959 for (i = 0; i < msec_loops; i++) 960 (void) clock(); 961 } 962 } 963 #endif 964 965 /* 966 * Return the characters actually input by a "special" key. 967 */ 968 public constant char * special_key_str(int key) 969 { 970 static char tbuf[40]; 971 constant char *s; 972 #if MSDOS_COMPILER || OS2 973 static char k_right[] = { '\340', PCK_RIGHT, 0 }; 974 static char k_left[] = { '\340', PCK_LEFT, 0 }; 975 static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; 976 static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; 977 static char k_insert[] = { '\340', PCK_INSERT, 0 }; 978 static char k_delete[] = { '\340', PCK_DELETE, 0 }; 979 static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; 980 static char k_ctl_backspace[] = { '\177', 0 }; 981 static char k_backspace[] = { '\b', 0 }; 982 static char k_home[] = { '\340', PCK_HOME, 0 }; 983 static char k_end[] = { '\340', PCK_END, 0 }; 984 static char k_up[] = { '\340', PCK_UP, 0 }; 985 static char k_down[] = { '\340', PCK_DOWN, 0 }; 986 static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; 987 static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; 988 static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; 989 static char k_f1[] = { '\340', PCK_F1, 0 }; 990 #endif 991 #if !MSDOS_COMPILER 992 char *sp = tbuf; 993 #endif 994 995 switch (key) 996 { 997 #if OS2 998 /* 999 * If windowid is not NULL, assume less is executed in 1000 * the XFree86 environment. 1001 */ 1002 case SK_RIGHT_ARROW: 1003 s = windowid ? ltgetstr("kr", &sp) : k_right; 1004 break; 1005 case SK_LEFT_ARROW: 1006 s = windowid ? ltgetstr("kl", &sp) : k_left; 1007 break; 1008 case SK_UP_ARROW: 1009 s = windowid ? ltgetstr("ku", &sp) : k_up; 1010 break; 1011 case SK_DOWN_ARROW: 1012 s = windowid ? ltgetstr("kd", &sp) : k_down; 1013 break; 1014 case SK_PAGE_UP: 1015 s = windowid ? ltgetstr("kP", &sp) : k_pageup; 1016 break; 1017 case SK_PAGE_DOWN: 1018 s = windowid ? ltgetstr("kN", &sp) : k_pagedown; 1019 break; 1020 case SK_HOME: 1021 s = windowid ? ltgetstr("kh", &sp) : k_home; 1022 break; 1023 case SK_END: 1024 s = windowid ? ltgetstr("@7", &sp) : k_end; 1025 break; 1026 case SK_DELETE: 1027 s = windowid ? ltgetstr("kD", &sp) : k_delete; 1028 if (s == NULL) 1029 { 1030 tbuf[0] = '\177'; 1031 tbuf[1] = '\0'; 1032 s = tbuf; 1033 } 1034 break; 1035 #endif 1036 #if MSDOS_COMPILER 1037 case SK_RIGHT_ARROW: 1038 s = k_right; 1039 break; 1040 case SK_LEFT_ARROW: 1041 s = k_left; 1042 break; 1043 case SK_UP_ARROW: 1044 s = k_up; 1045 break; 1046 case SK_DOWN_ARROW: 1047 s = k_down; 1048 break; 1049 case SK_PAGE_UP: 1050 s = k_pageup; 1051 break; 1052 case SK_PAGE_DOWN: 1053 s = k_pagedown; 1054 break; 1055 case SK_HOME: 1056 s = k_home; 1057 break; 1058 case SK_END: 1059 s = k_end; 1060 break; 1061 case SK_DELETE: 1062 s = k_delete; 1063 break; 1064 #endif 1065 #if MSDOS_COMPILER || OS2 1066 case SK_INSERT: 1067 s = k_insert; 1068 break; 1069 case SK_CTL_LEFT_ARROW: 1070 s = k_ctl_left; 1071 break; 1072 case SK_CTL_RIGHT_ARROW: 1073 s = k_ctl_right; 1074 break; 1075 case SK_CTL_BACKSPACE: 1076 s = k_ctl_backspace; 1077 break; 1078 case SK_CTL_DELETE: 1079 s = k_ctl_delete; 1080 break; 1081 case SK_BACKSPACE: 1082 s = k_backspace; 1083 break; 1084 case SK_F1: 1085 s = k_f1; 1086 break; 1087 case SK_BACKTAB: 1088 s = k_backtab; 1089 break; 1090 #else 1091 case SK_RIGHT_ARROW: 1092 s = ltgetstr("kr", &sp); 1093 break; 1094 case SK_LEFT_ARROW: 1095 s = ltgetstr("kl", &sp); 1096 break; 1097 case SK_UP_ARROW: 1098 s = ltgetstr("ku", &sp); 1099 break; 1100 case SK_DOWN_ARROW: 1101 s = ltgetstr("kd", &sp); 1102 break; 1103 case SK_PAGE_UP: 1104 s = ltgetstr("kP", &sp); 1105 break; 1106 case SK_PAGE_DOWN: 1107 s = ltgetstr("kN", &sp); 1108 break; 1109 case SK_HOME: 1110 s = ltgetstr("kh", &sp); 1111 break; 1112 case SK_END: 1113 s = ltgetstr("@7", &sp); 1114 break; 1115 case SK_DELETE: 1116 s = ltgetstr("kD", &sp); 1117 if (s == NULL) 1118 { 1119 tbuf[0] = '\177'; 1120 tbuf[1] = '\0'; 1121 s = tbuf; 1122 } 1123 break; 1124 case SK_BACKSPACE: 1125 s = ltgetstr("kb", &sp); 1126 if (s == NULL) 1127 { 1128 tbuf[0] = '\b'; 1129 tbuf[1] = '\0'; 1130 s = tbuf; 1131 } 1132 break; 1133 #endif 1134 case SK_CONTROL_K: 1135 tbuf[0] = CONTROL('K'); 1136 tbuf[1] = '\0'; 1137 s = tbuf; 1138 break; 1139 default: 1140 return (NULL); 1141 } 1142 return (s); 1143 } 1144 1145 #if MSDOS_COMPILER 1146 public void init_win_colors(void) 1147 { 1148 if (nm_fg_color == CV_ERROR || nm_fg_color == CV_NOCHANGE) nm_fg_color = sy_fg_color; 1149 if (nm_bg_color == CV_ERROR || nm_bg_color == CV_NOCHANGE) nm_bg_color = sy_bg_color; 1150 if (bo_fg_color == CV_NOCHANGE) bo_fg_color = sy_fg_color; else if (bo_fg_color == CV_ERROR) bo_fg_color = sy_fg_color | 8; 1151 if (bo_bg_color == CV_NOCHANGE) bo_bg_color = sy_bg_color; else if (bo_bg_color == CV_ERROR) bo_bg_color = sy_bg_color; 1152 if (ul_fg_color == CV_NOCHANGE) ul_fg_color = sy_fg_color; else if (ul_fg_color == CV_ERROR) ul_fg_color = (sy_bg_color == 3 || sy_bg_color == 11) ? 0 : 11; 1153 if (ul_bg_color == CV_NOCHANGE) ul_bg_color = sy_bg_color; else if (ul_bg_color == CV_ERROR) ul_bg_color = sy_bg_color; 1154 if (so_fg_color == CV_NOCHANGE) so_fg_color = sy_fg_color; else if (so_fg_color == CV_ERROR) so_fg_color = sy_bg_color; 1155 if (so_bg_color == CV_NOCHANGE) so_bg_color = sy_bg_color; else if (so_bg_color == CV_ERROR) so_bg_color = sy_fg_color; 1156 if (bl_fg_color == CV_NOCHANGE) bl_fg_color = sy_fg_color; else if (bl_fg_color == CV_ERROR) bl_fg_color = ul_bg_color; 1157 if (bl_bg_color == CV_NOCHANGE) bl_bg_color = sy_bg_color; else if (bl_bg_color == CV_ERROR) bl_bg_color = ul_fg_color; 1158 nm_fg_color |= nm_attr; 1159 bo_fg_color |= bo_attr; 1160 ul_fg_color |= ul_attr; 1161 so_fg_color |= so_attr; 1162 bl_fg_color |= bl_attr; 1163 } 1164 #endif /* MSDOS_COMPILER */ 1165 1166 /* 1167 * Get terminal capabilities via termcap. 1168 */ 1169 public void get_term(void) 1170 { 1171 termcap_debug = !isnullenv(lgetenv("LESS_TERMCAP_DEBUG")); 1172 #if MSDOS_COMPILER 1173 auto_wrap = 1; 1174 ignaw = 0; 1175 can_goto_line = 1; 1176 clear_bg = 1; 1177 /* 1178 * Set up default colors. 1179 * The xx_s_width and xx_e_width vars are already initialized to 0. 1180 */ 1181 #if MSDOS_COMPILER==MSOFTC 1182 sy_bg_color = _getbkcolor(); 1183 sy_fg_color = _gettextcolor(); 1184 get_clock(); 1185 #else 1186 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1187 { 1188 struct text_info w; 1189 gettextinfo(&w); 1190 sy_bg_color = (w.attribute >> 4) & 0x0F; 1191 sy_fg_color = (w.attribute >> 0) & 0x0F; 1192 } 1193 #else 1194 #if MSDOS_COMPILER==WIN32C 1195 { 1196 CONSOLE_SCREEN_BUFFER_INFO scr; 1197 1198 con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); 1199 /* 1200 * Always open stdin in binary. Note this *must* be done 1201 * before any file operations have been done on fd0. 1202 */ 1203 SET_BINARY(0); 1204 GetConsoleMode(con_out, &init_console_output_mode); 1205 GetConsoleScreenBufferInfo(con_out, &scr); 1206 curr_attr = scr.wAttributes; 1207 sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ 1208 sy_fg_color = curr_attr & FG_COLORS; 1209 } 1210 #endif 1211 #endif 1212 #endif 1213 init_win_colors(); 1214 1215 /* 1216 * Get size of the screen. 1217 */ 1218 scrsize(); 1219 pos_init(); 1220 1221 #else /* !MSDOS_COMPILER */ 1222 { 1223 char *sp; 1224 constant char *t1; 1225 constant char *t2; 1226 constant char *term; 1227 /* 1228 * Some termcap libraries assume termbuf is static 1229 * (accessible after tgetent returns). 1230 */ 1231 static char termbuf[TERMBUF_SIZE]; 1232 static char sbuf[TERMSBUF_SIZE]; 1233 1234 #if OS2 1235 /* 1236 * Make sure the termcap database is available. 1237 */ 1238 constant char *cp = lgetenv("TERMCAP"); 1239 if (isnullenv(cp)) 1240 { 1241 char *termcap; 1242 if ((sp = homefile("termcap.dat")) != NULL) 1243 { 1244 termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); 1245 sprintf(termcap, "TERMCAP=%s", sp); 1246 free(sp); 1247 putenv(termcap); 1248 } 1249 } 1250 #endif 1251 /* 1252 * Find out what kind of terminal this is. 1253 */ 1254 if ((term = lgetenv("TERM")) == NULL) 1255 term = DEFAULT_TERM; 1256 hardcopy = 0; 1257 /* {{ Should probably just pass NULL instead of termbuf. }} */ 1258 if (tgetent(termbuf, term) != TGETENT_OK) 1259 hardcopy = 1; 1260 if (ltgetflag("hc")) 1261 hardcopy = 1; 1262 1263 /* 1264 * Get size of the screen. 1265 */ 1266 scrsize(); 1267 pos_init(); 1268 1269 auto_wrap = ltgetflag("am"); 1270 ignaw = ltgetflag("xn"); 1271 above_mem = ltgetflag("da"); 1272 below_mem = ltgetflag("db"); 1273 clear_bg = ltgetflag("ut"); 1274 no_alt_screen = ltgetflag("NR"); 1275 1276 /* 1277 * Assumes termcap variable "sg" is the printing width of: 1278 * the standout sequence, the end standout sequence, 1279 * the underline sequence, the end underline sequence, 1280 * the boldface sequence, and the end boldface sequence. 1281 */ 1282 if ((so_s_width = ltgetnum("sg")) < 0) 1283 so_s_width = 0; 1284 so_e_width = so_s_width; 1285 1286 bo_s_width = bo_e_width = so_s_width; 1287 ul_s_width = ul_e_width = so_s_width; 1288 bl_s_width = bl_e_width = so_s_width; 1289 1290 #if HILITE_SEARCH 1291 if (so_s_width > 0 || so_e_width > 0) 1292 /* 1293 * Disable highlighting by default on magic cookie terminals. 1294 * Turning on highlighting might change the displayed width 1295 * of a line, causing the display to get messed up. 1296 * The user can turn it back on with -g, 1297 * but she won't like the results. 1298 */ 1299 hilite_search = 0; 1300 #endif 1301 1302 /* 1303 * Get various string-valued capabilities. 1304 */ 1305 sp = sbuf; 1306 1307 #if HAVE_OSPEED 1308 sc_pad = ltgetstr("pc", &sp); 1309 if (sc_pad != NULL) 1310 PC = *sc_pad; 1311 #endif 1312 1313 sc_s_keypad = ltgetstr("ks", &sp); 1314 if (sc_s_keypad == NULL) 1315 sc_s_keypad = ""; 1316 sc_e_keypad = ltgetstr("ke", &sp); 1317 if (sc_e_keypad == NULL) 1318 sc_e_keypad = ""; 1319 kent = ltgetstr("@8", &sp); 1320 1321 sc_s_mousecap = ltgetstr("MOUSE_START", &sp); 1322 if (sc_s_mousecap == NULL) 1323 sc_s_mousecap = ESCS "[?1000h" ESCS "[?1002h" ESCS "[?1006h"; 1324 sc_e_mousecap = ltgetstr("MOUSE_END", &sp); 1325 if (sc_e_mousecap == NULL) 1326 sc_e_mousecap = ESCS "[?1006l" ESCS "[?1002l" ESCS "[?1000l"; 1327 1328 sc_s_bracketed_paste = ltgetstr("BRACKETED_PASTE_START", &sp); 1329 if (sc_s_bracketed_paste == NULL) 1330 sc_s_bracketed_paste = ESCS"[?2004h"; 1331 sc_e_bracketed_paste = ltgetstr("BRACKETED_PASTE_END", &sp); 1332 if (sc_e_bracketed_paste == NULL) 1333 sc_e_bracketed_paste = ESCS"[?2004l"; 1334 1335 sc_suspend = ltgetstr("SUSPEND", &sp); 1336 if (sc_suspend == NULL) 1337 sc_suspend = ""; 1338 sc_resume = ltgetstr("RESUME", &sp); 1339 if (sc_resume == NULL) 1340 sc_resume = ""; 1341 1342 sc_init = ltgetstr("ti", &sp); 1343 if (sc_init == NULL) 1344 sc_init = ""; 1345 1346 sc_deinit= ltgetstr("te", &sp); 1347 if (sc_deinit == NULL) 1348 sc_deinit = ""; 1349 1350 sc_eol_clear = ltgetstr("ce", &sp); 1351 if (sc_eol_clear == NULL || *sc_eol_clear == '\0') 1352 { 1353 missing_cap = TRUE; 1354 sc_eol_clear = ""; 1355 } 1356 1357 sc_eos_clear = ltgetstr("cd", &sp); 1358 if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) 1359 { 1360 missing_cap = TRUE; 1361 sc_eos_clear = ""; 1362 } 1363 1364 sc_clear = ltgetstr("cl", &sp); 1365 if (sc_clear == NULL || *sc_clear == '\0') 1366 { 1367 missing_cap = TRUE; 1368 sc_clear = "\n\n"; 1369 } 1370 1371 sc_move = ltgetstr("cm", &sp); 1372 if (sc_move == NULL || *sc_move == '\0') 1373 { 1374 /* 1375 * This is not an error here, because we don't 1376 * always need sc_move. 1377 * We need it only if we don't have home or lower-left. 1378 */ 1379 sc_move = ""; 1380 can_goto_line = 0; 1381 } else 1382 can_goto_line = 1; 1383 1384 tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp); 1385 tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); 1386 tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); 1387 tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); 1388 1389 sc_visual_bell = ltgetstr("vb", &sp); 1390 if (sc_visual_bell == NULL) 1391 sc_visual_bell = ""; 1392 1393 if (ltgetflag("bs")) 1394 sc_backspace = "\b"; 1395 else 1396 { 1397 sc_backspace = ltgetstr("bc", &sp); 1398 if (sc_backspace == NULL || *sc_backspace == '\0') 1399 sc_backspace = "\b"; 1400 } 1401 1402 /* 1403 * Choose between using "ho" and "cm" ("home" and "cursor move") 1404 * to move the cursor to the upper left corner of the screen. 1405 */ 1406 t1 = ltgetstr("ho", &sp); 1407 if (t1 == NULL) 1408 t1 = ""; 1409 if (*sc_move == '\0') 1410 t2 = ""; 1411 else 1412 { 1413 strcpy(sp, tgoto(sc_move, 0, 0)); 1414 t2 = sp; 1415 sp += strlen(sp) + 1; 1416 } 1417 sc_home = cheaper(t1, t2, "|\b^"); 1418 1419 /* 1420 * Choose between using "ll" and "cm" ("lower left" and "cursor move") 1421 * to move the cursor to the lower left corner of the screen. 1422 */ 1423 t1 = ltgetstr("ll", &sp); 1424 if (t1 == NULL || !full_screen) 1425 t1 = ""; 1426 if (*sc_move == '\0') 1427 t2 = ""; 1428 else 1429 { 1430 strcpy(sp, tgoto(sc_move, 0, sc_height-1)); 1431 t2 = sp; 1432 sp += strlen(sp) + 1; 1433 } 1434 sc_lower_left = cheaper(t1, t2, "\r"); 1435 1436 /* 1437 * Get carriage return string. 1438 */ 1439 sc_return = ltgetstr("cr", &sp); 1440 if (sc_return == NULL) 1441 sc_return = "\r"; 1442 1443 /* 1444 * Choose between using "al" or "sr" ("add line" or "scroll reverse") 1445 * to add a line at the top of the screen. 1446 */ 1447 t1 = ltgetstr("al", &sp); 1448 if (t1 == NULL) 1449 t1 = ""; 1450 t2 = ltgetstr("sr", &sp); 1451 if (t2 == NULL) 1452 t2 = ""; 1453 if (*t1 == '\0' && *t2 == '\0') 1454 sc_addline = ""; 1455 else if (above_mem) 1456 sc_addline = t1; 1457 else 1458 sc_addline = cheaper(t1, t2, ""); 1459 if (*sc_addline == '\0') 1460 { 1461 /* 1462 * Force repaint on any backward movement. 1463 */ 1464 no_back_scroll = 1; 1465 } 1466 } 1467 #endif /* MSDOS_COMPILER */ 1468 { 1469 const char *env = lgetenv("LESS_SHELL_LINES"); 1470 shell_lines = isnullenv(env) ? 1 : atoi(env); 1471 if (shell_lines >= sc_height) 1472 shell_lines = sc_height - 1; 1473 } 1474 } 1475 1476 #if !MSDOS_COMPILER 1477 /* 1478 * Return the cost of displaying a termcap string. 1479 * We use the trick of calling tputs, but as a char printing function 1480 * we give it inc_costcount, which just increments "costcount". 1481 * This tells us how many chars would be printed by using this string. 1482 * {{ Couldn't we just use strlen? }} 1483 */ 1484 static int costcount; 1485 1486 /*ARGSUSED*/ 1487 static int inc_costcount(int c) 1488 { 1489 costcount++; 1490 return (c); 1491 } 1492 1493 static int cost(constant char *t) 1494 { 1495 costcount = 0; 1496 tputs(t, sc_height, inc_costcount); 1497 return (costcount); 1498 } 1499 1500 /* 1501 * Return the "best" of the two given termcap strings. 1502 * The best, if both exist, is the one with the lower 1503 * cost (see cost() function). 1504 */ 1505 static constant char * cheaper(constant char *t1, constant char *t2, constant char *def) 1506 { 1507 if (*t1 == '\0' && *t2 == '\0') 1508 { 1509 missing_cap = TRUE; 1510 return (def); 1511 } 1512 if (*t1 == '\0') 1513 return (t2); 1514 if (*t2 == '\0') 1515 return (t1); 1516 if (cost(t1) < cost(t2)) 1517 return (t1); 1518 return (t2); 1519 } 1520 1521 static void tmodes(constant char *incap, constant char *outcap, constant char **instr, constant char **outstr, constant char *def_instr, constant char *def_outstr, char **spp) 1522 { 1523 *instr = ltgetstr(incap, spp); 1524 if (*instr == NULL) 1525 { 1526 /* Use defaults. */ 1527 *instr = def_instr; 1528 *outstr = def_outstr; 1529 return; 1530 } 1531 1532 *outstr = ltgetstr(outcap, spp); 1533 if (*outstr == NULL) 1534 /* No specific out capability; use "me". */ 1535 *outstr = ltgetstr("me", spp); 1536 if (*outstr == NULL) 1537 /* Don't even have "me"; use a null string. */ 1538 *outstr = ""; 1539 } 1540 1541 #endif /* MSDOS_COMPILER */ 1542 1543 1544 /* 1545 * Below are the functions which perform all the 1546 * terminal-specific screen manipulation. 1547 */ 1548 1549 1550 #if MSDOS_COMPILER 1551 1552 #if MSDOS_COMPILER==WIN32C 1553 static void _settextposition(int row, int col) 1554 { 1555 COORD cpos; 1556 CONSOLE_SCREEN_BUFFER_INFO csbi; 1557 1558 GetConsoleScreenBufferInfo(con_out, &csbi); 1559 cpos.X = csbi.srWindow.Left + (col - 1); 1560 cpos.Y = csbi.srWindow.Top + (row - 1); 1561 SetConsoleCursorPosition(con_out, cpos); 1562 } 1563 #endif 1564 1565 /* 1566 * Initialize the screen to the correct color at startup. 1567 */ 1568 static void initcolor(void) 1569 { 1570 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1571 intensevideo(); 1572 #endif 1573 SETCOLORS(nm_fg_color, nm_bg_color); 1574 #if 0 1575 /* 1576 * This clears the screen at startup. This is different from 1577 * the behavior of other versions of less. Disable it for now. 1578 */ 1579 char *blanks; 1580 int row; 1581 int col; 1582 1583 /* 1584 * Create a complete, blank screen using "normal" colors. 1585 */ 1586 SETCOLORS(nm_fg_color, nm_bg_color); 1587 blanks = (char *) ecalloc(width+1, sizeof(char)); 1588 for (col = 0; col < sc_width; col++) 1589 blanks[col] = ' '; 1590 blanks[sc_width] = '\0'; 1591 for (row = 0; row < sc_height; row++) 1592 _outtext(blanks); 1593 free(blanks); 1594 #endif 1595 } 1596 #endif 1597 1598 #if MSDOS_COMPILER==WIN32C 1599 1600 /* 1601 * Enable virtual terminal processing, if available. 1602 */ 1603 static void win32_init_vt_term(void) 1604 { 1605 if (vt_enabled == 0 || (vt_enabled == 1 && con_out == con_out_ours)) 1606 return; // already initialized 1607 1608 /* don't care about the initial mode, and win VT hard-enables am+xn */ 1609 vt_enabled = SetConsoleMode(con_out, ENABLE_PROCESSED_OUTPUT | 1610 ENABLE_VIRTUAL_TERMINAL_PROCESSING | 1611 ENABLE_WRAP_AT_EOL_OUTPUT); 1612 if (vt_enabled) 1613 { 1614 auto_wrap = 1; 1615 ignaw = 1; 1616 } 1617 } 1618 1619 static void win32_deinit_vt_term(void) 1620 { 1621 if (vt_enabled == 1 && con_out == con_out_save) 1622 SetConsoleMode(con_out, init_console_output_mode); 1623 } 1624 1625 /* 1626 * Termcap-like init with a private win32 console. 1627 */ 1628 static void win32_init_term(void) 1629 { 1630 CONSOLE_SCREEN_BUFFER_INFO scr; 1631 COORD size; 1632 1633 if (con_out_save == INVALID_HANDLE_VALUE) 1634 return; 1635 1636 GetConsoleScreenBufferInfo(con_out_save, &scr); 1637 1638 if (con_out_ours == INVALID_HANDLE_VALUE) 1639 { 1640 /* 1641 * Create our own screen buffer, so that we 1642 * may restore the original when done. 1643 */ 1644 con_out_ours = CreateConsoleScreenBuffer( 1645 GENERIC_WRITE | GENERIC_READ, 1646 FILE_SHARE_WRITE | FILE_SHARE_READ, 1647 (LPSECURITY_ATTRIBUTES) NULL, 1648 CONSOLE_TEXTMODE_BUFFER, 1649 (LPVOID) NULL); 1650 1651 // we don't care about the initial state. we need processed 1652 // output without anything else (no wrap at EOL, no VT, 1653 // no disabled auto-return). 1654 if (SetConsoleMode(con_out_ours, ENABLE_PROCESSED_OUTPUT)) 1655 auto_wrap = 0; 1656 } 1657 1658 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 1659 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 1660 SetConsoleScreenBufferSize(con_out_ours, size); 1661 SetConsoleActiveScreenBuffer(con_out_ours); 1662 con_out = con_out_ours; 1663 } 1664 1665 /* 1666 * Restore the startup console. 1667 */ 1668 static void win32_deinit_term(void) 1669 { 1670 if (con_out_save == INVALID_HANDLE_VALUE) 1671 return; 1672 if (quitting) 1673 (void) CloseHandle(con_out_ours); 1674 SetConsoleActiveScreenBuffer(con_out_save); 1675 con_out = con_out_save; 1676 } 1677 1678 #endif 1679 1680 #if !MSDOS_COMPILER 1681 static void do_tputs(constant char *str, int affcnt, int (*f_putc)(int)) 1682 { 1683 #if LESSTEST 1684 if (is_lesstest() && f_putc == putchr) 1685 putstr(str); 1686 else 1687 #endif /*LESSTEST*/ 1688 tputs(str, affcnt, f_putc); 1689 } 1690 1691 /* 1692 * Like tputs but we handle $<...> delay strings here because 1693 * some implementations of tputs don't perform delays correctly. 1694 */ 1695 static void ltputs(constant char *str, int affcnt, int (*f_putc)(int)) 1696 { 1697 while (str != NULL && *str != '\0') 1698 { 1699 #if HAVE_STRSTR 1700 constant char *obrac = strstr(str, "$<"); 1701 if (obrac != NULL) 1702 { 1703 char str2[64]; 1704 size_t slen = ptr_diff(obrac, str); 1705 if (slen < sizeof(str2)) 1706 { 1707 int delay; 1708 /* Output first part of string (before "$<"). */ 1709 memcpy(str2, str, slen); 1710 str2[slen] = '\0'; 1711 do_tputs(str2, affcnt, f_putc); 1712 str += slen + 2; 1713 /* Perform the delay. */ 1714 delay = lstrtoic(str, &str, 10); 1715 if (*str == '*') 1716 if (ckd_mul(&delay, delay, affcnt)) 1717 delay = INT_MAX; 1718 flush(); 1719 sleep_ms(delay); 1720 /* Skip past closing ">" at end of delay string. */ 1721 str = strstr(str, ">"); 1722 if (str != NULL) 1723 str++; 1724 continue; 1725 } 1726 } 1727 #endif 1728 /* Pass the rest of the string to tputs and we're done. */ 1729 do_tputs(str, affcnt, f_putc); 1730 break; 1731 } 1732 } 1733 #endif /* MSDOS_COMPILER */ 1734 1735 /* 1736 * Configure the terminal so mouse clicks and wheel moves 1737 * produce input to less. 1738 */ 1739 public void init_mouse(void) 1740 { 1741 #if !MSDOS_COMPILER 1742 ltputs(sc_s_mousecap, sc_height, putchr); 1743 #else 1744 #if MSDOS_COMPILER==WIN32C 1745 curr_console_input_mode = mouse_console_input_mode; 1746 SetConsoleMode(tty, curr_console_input_mode); 1747 #endif 1748 #endif 1749 } 1750 1751 /* 1752 * Configure the terminal so mouse clicks and wheel moves 1753 * are handled by the system (so text can be selected, etc). 1754 */ 1755 public void deinit_mouse(void) 1756 { 1757 #if !MSDOS_COMPILER 1758 ltputs(sc_e_mousecap, sc_height, putchr); 1759 #else 1760 #if MSDOS_COMPILER==WIN32C 1761 curr_console_input_mode = base_console_input_mode; 1762 SetConsoleMode(tty, curr_console_input_mode); 1763 #endif 1764 #endif 1765 } 1766 1767 /* 1768 * Suspend screen updates. 1769 */ 1770 public void suspend_screen(void) 1771 { 1772 #if !MSDOS_COMPILER 1773 ltputs(sc_suspend, 1, putchr); 1774 #endif 1775 } 1776 1777 /* 1778 * Resume screen updates. 1779 */ 1780 public void resume_screen(void) 1781 { 1782 #if !MSDOS_COMPILER 1783 ltputs(sc_resume, 1, putchr); 1784 #endif 1785 } 1786 1787 /* 1788 * Initialize terminal 1789 */ 1790 public void init(void) 1791 { 1792 clear_bot_if_needed(); 1793 #if !MSDOS_COMPILER 1794 if (!(quit_if_one_screen && one_screen)) 1795 { 1796 if (!no_init) 1797 { 1798 ltputs(sc_init, sc_height, putchr); 1799 /* 1800 * Some terminals leave the cursor unmoved when switching 1801 * to the alt screen. To avoid having the text appear at 1802 * a seemingly random line on the alt screen, move to 1803 * lower left if we are using an alt screen. 1804 */ 1805 if (*sc_init != '\0' && *sc_deinit != '\0' && !no_alt_screen) 1806 lower_left(); 1807 term_init_done = 1; 1808 } 1809 if (!no_keypad) 1810 ltputs(sc_s_keypad, sc_height, putchr); 1811 if (mousecap) 1812 init_mouse(); 1813 if (no_paste) 1814 init_bracketed_paste(); 1815 } 1816 init_done = 1; 1817 if (top_scroll) 1818 { 1819 int i; 1820 1821 /* 1822 * This is nice to terminals with no alternate screen, 1823 * but with saved scrolled-off-the-top lines. This way, 1824 * no previous line is lost, but we start with a whole 1825 * screen to ourself. 1826 */ 1827 for (i = 1; i < sc_height; i++) 1828 putchr('\n'); 1829 } else 1830 line_left(); 1831 #else 1832 #if MSDOS_COMPILER==WIN32C 1833 if (!(quit_if_one_screen && one_screen)) 1834 { 1835 if (!no_init) 1836 { 1837 win32_init_term(); 1838 term_init_done = 1; 1839 } 1840 if (mousecap) 1841 init_mouse(); 1842 1843 } 1844 win32_init_vt_term(); 1845 #endif 1846 init_done = 1; 1847 initcolor(); 1848 flush(); 1849 #endif 1850 } 1851 1852 /* 1853 * Deinitialize terminal 1854 */ 1855 public void deinit(void) 1856 { 1857 if (!init_done) 1858 return; 1859 #if !MSDOS_COMPILER 1860 if (!(quit_if_one_screen && one_screen)) 1861 { 1862 if (mousecap) 1863 deinit_mouse(); 1864 if (no_paste) 1865 deinit_bracketed_paste(); 1866 if (!no_keypad) 1867 ltputs(sc_e_keypad, sc_height, putchr); 1868 if (!no_init) 1869 ltputs(sc_deinit, sc_height, putchr); 1870 } 1871 #else 1872 /* Restore system colors. */ 1873 SETCOLORS(sy_fg_color, sy_bg_color); 1874 #if MSDOS_COMPILER==WIN32C 1875 win32_deinit_vt_term(); 1876 if (!(quit_if_one_screen && one_screen)) 1877 { 1878 if (mousecap) 1879 deinit_mouse(); 1880 if (!no_init) 1881 win32_deinit_term(); 1882 } 1883 #else 1884 /* Need clreol to make SETCOLORS take effect. */ 1885 clreol(); 1886 #endif 1887 #endif 1888 init_done = 0; 1889 } 1890 1891 /* 1892 * Are we interactive (ie. writing to an initialized tty)? 1893 */ 1894 public int interactive(void) 1895 { 1896 return (is_tty && init_done); 1897 } 1898 1899 static void assert_interactive(void) 1900 { 1901 if (interactive()) return; 1902 /* abort(); */ 1903 } 1904 1905 /* 1906 * Home cursor (move to upper left corner of screen). 1907 */ 1908 public void home(void) 1909 { 1910 assert_interactive(); 1911 #if !MSDOS_COMPILER 1912 ltputs(sc_home, 1, putchr); 1913 #else 1914 flush(); 1915 _settextposition(1,1); 1916 #endif 1917 } 1918 1919 #if LESSTEST 1920 public void dump_screen(void) 1921 { 1922 char dump_cmd[32]; 1923 SNPRINTF1(dump_cmd, sizeof(dump_cmd), ESCS"0;0;%dR", sc_width * sc_height); 1924 ltputs(dump_cmd, sc_height, putchr); 1925 flush(); 1926 } 1927 #endif /*LESSTEST*/ 1928 1929 /* 1930 * Add a blank line (called with cursor at home). 1931 * Should scroll the display down. 1932 */ 1933 public void add_line(void) 1934 { 1935 assert_interactive(); 1936 #if !MSDOS_COMPILER 1937 ltputs(sc_addline, sc_height, putchr); 1938 #else 1939 flush(); 1940 #if MSDOS_COMPILER==MSOFTC 1941 _scrolltextwindow(_GSCROLLDOWN); 1942 _settextposition(1,1); 1943 #else 1944 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 1945 movetext(1,1, sc_width,sc_height-1, 1,2); 1946 gotoxy(1,1); 1947 clreol(); 1948 #else 1949 #if MSDOS_COMPILER==WIN32C 1950 { 1951 CHAR_INFO fillchar; 1952 SMALL_RECT rcSrc, rcClip; 1953 COORD new_org; 1954 CONSOLE_SCREEN_BUFFER_INFO csbi; 1955 1956 GetConsoleScreenBufferInfo(con_out,&csbi); 1957 1958 /* The clip rectangle is the entire visible screen. */ 1959 rcClip.Left = csbi.srWindow.Left; 1960 rcClip.Top = csbi.srWindow.Top; 1961 rcClip.Right = csbi.srWindow.Right; 1962 rcClip.Bottom = csbi.srWindow.Bottom; 1963 1964 /* The source rectangle is the visible screen minus the last line. */ 1965 rcSrc = rcClip; 1966 rcSrc.Bottom--; 1967 1968 /* Move the top left corner of the source window down one row. */ 1969 new_org.X = rcSrc.Left; 1970 new_org.Y = rcSrc.Top + 1; 1971 1972 /* Fill the right character and attributes. */ 1973 fillchar.Char.AsciiChar = ' '; 1974 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 1975 fillchar.Attributes = curr_attr; 1976 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 1977 _settextposition(1,1); 1978 } 1979 #endif 1980 #endif 1981 #endif 1982 #endif 1983 } 1984 1985 #if 0 1986 /* 1987 * Remove the n topmost lines and scroll everything below it in the 1988 * window upward. This is needed to stop leaking the topmost line 1989 * into the scrollback buffer when we go down-one-line (in WIN32). 1990 */ 1991 public void remove_top(int n) 1992 { 1993 #if MSDOS_COMPILER==WIN32C 1994 SMALL_RECT rcSrc, rcClip; 1995 CHAR_INFO fillchar; 1996 COORD new_org; 1997 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ 1998 1999 if (n >= sc_height - 1) 2000 { 2001 clear(); 2002 home(); 2003 return; 2004 } 2005 2006 flush(); 2007 2008 GetConsoleScreenBufferInfo(con_out, &csbi); 2009 2010 /* Get the extent of all-visible-rows-but-the-last. */ 2011 rcSrc.Left = csbi.srWindow.Left; 2012 rcSrc.Top = csbi.srWindow.Top + n; 2013 rcSrc.Right = csbi.srWindow.Right; 2014 rcSrc.Bottom = csbi.srWindow.Bottom; 2015 2016 /* Get the clip rectangle. */ 2017 rcClip.Left = rcSrc.Left; 2018 rcClip.Top = csbi.srWindow.Top; 2019 rcClip.Right = rcSrc.Right; 2020 rcClip.Bottom = rcSrc.Bottom ; 2021 2022 /* Move the source window up n rows. */ 2023 new_org.X = rcSrc.Left; 2024 new_org.Y = rcSrc.Top - n; 2025 2026 /* Fill the right character and attributes. */ 2027 fillchar.Char.AsciiChar = ' '; 2028 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2029 fillchar.Attributes = curr_attr; 2030 2031 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2032 2033 /* Position cursor on first blank line. */ 2034 goto_line(sc_height - n - 1); 2035 #endif 2036 } 2037 #endif 2038 2039 #if MSDOS_COMPILER==WIN32C 2040 /* 2041 * Clear the screen. 2042 */ 2043 static void win32_clear(void) 2044 { 2045 /* 2046 * This will clear only the currently visible rows of the NT 2047 * console buffer, which means none of the precious scrollback 2048 * rows are touched making for faster scrolling. Note that, if 2049 * the window has fewer columns than the console buffer (i.e. 2050 * there is a horizontal scrollbar as well), the entire width 2051 * of the visible rows will be cleared. 2052 */ 2053 COORD topleft; 2054 DWORD nchars; 2055 DWORD winsz; 2056 CONSOLE_SCREEN_BUFFER_INFO csbi; 2057 2058 /* get the number of cells in the current buffer */ 2059 GetConsoleScreenBufferInfo(con_out, &csbi); 2060 winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); 2061 topleft.X = 0; 2062 topleft.Y = csbi.srWindow.Top; 2063 2064 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2065 FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); 2066 FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); 2067 } 2068 2069 /* 2070 * Remove the n topmost lines and scroll everything below it in the 2071 * window upward. 2072 */ 2073 public void win32_scroll_up(int n) 2074 { 2075 SMALL_RECT rcSrc, rcClip; 2076 CHAR_INFO fillchar; 2077 COORD topleft; 2078 COORD new_org; 2079 DWORD nchars; 2080 DWORD size; 2081 CONSOLE_SCREEN_BUFFER_INFO csbi; 2082 2083 if (n <= 0) 2084 return; 2085 2086 if (n >= sc_height - 1) 2087 { 2088 win32_clear(); 2089 _settextposition(1,1); 2090 return; 2091 } 2092 2093 /* Get the extent of what will remain visible after scrolling. */ 2094 GetConsoleScreenBufferInfo(con_out, &csbi); 2095 rcSrc.Left = csbi.srWindow.Left; 2096 rcSrc.Top = csbi.srWindow.Top + n; 2097 rcSrc.Right = csbi.srWindow.Right; 2098 rcSrc.Bottom = csbi.srWindow.Bottom; 2099 2100 /* Get the clip rectangle. */ 2101 rcClip.Left = rcSrc.Left; 2102 rcClip.Top = csbi.srWindow.Top; 2103 rcClip.Right = rcSrc.Right; 2104 rcClip.Bottom = rcSrc.Bottom ; 2105 2106 /* Move the source text to the top of the screen. */ 2107 new_org.X = rcSrc.Left; 2108 new_org.Y = rcClip.Top; 2109 2110 /* Fill the right character and attributes. */ 2111 fillchar.Char.AsciiChar = ' '; 2112 fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); 2113 2114 /* Scroll the window. */ 2115 SetConsoleTextAttribute(con_out, fillchar.Attributes); 2116 ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); 2117 2118 /* Clear remaining lines at bottom. */ 2119 topleft.X = csbi.dwCursorPosition.X; 2120 topleft.Y = rcSrc.Bottom - n; 2121 size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); 2122 FillConsoleOutputCharacter(con_out, ' ', size, topleft, 2123 &nchars); 2124 FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, 2125 &nchars); 2126 SetConsoleTextAttribute(con_out, curr_attr); 2127 2128 /* Move cursor n lines up from where it was. */ 2129 csbi.dwCursorPosition.Y -= n; 2130 SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); 2131 } 2132 #endif 2133 2134 /* 2135 * Move cursor to lower left corner of screen. 2136 */ 2137 public void lower_left(void) 2138 { 2139 assert_interactive(); 2140 #if !MSDOS_COMPILER 2141 ltputs(sc_lower_left, 1, putchr); 2142 #else 2143 flush(); 2144 _settextposition(sc_height, 1); 2145 #endif 2146 } 2147 2148 /* 2149 * Move cursor to left position of current line. 2150 */ 2151 public void line_left(void) 2152 { 2153 assert_interactive(); 2154 #if !MSDOS_COMPILER 2155 ltputs(sc_return, 1, putchr); 2156 #else 2157 { 2158 int row; 2159 flush(); 2160 #if MSDOS_COMPILER==WIN32C 2161 { 2162 CONSOLE_SCREEN_BUFFER_INFO scr; 2163 GetConsoleScreenBufferInfo(con_out, &scr); 2164 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2165 } 2166 #else 2167 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2168 row = wherey(); 2169 #else 2170 { 2171 struct rccoord tpos = _gettextposition(); 2172 row = tpos.row; 2173 } 2174 #endif 2175 #endif 2176 _settextposition(row, 1); 2177 } 2178 #endif 2179 } 2180 2181 /* 2182 * Check if the console size has changed and reset internals 2183 * (in lieu of SIGWINCH for WIN32). 2184 */ 2185 public void check_winch(void) 2186 { 2187 #if MSDOS_COMPILER==WIN32C 2188 CONSOLE_SCREEN_BUFFER_INFO scr; 2189 COORD size; 2190 2191 if (con_out == INVALID_HANDLE_VALUE) 2192 return; 2193 2194 flush(); 2195 GetConsoleScreenBufferInfo(con_out, &scr); 2196 size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; 2197 size.X = scr.srWindow.Right - scr.srWindow.Left + 1; 2198 if (size.Y != sc_height || size.X != sc_width) 2199 { 2200 sc_height = size.Y; 2201 sc_width = size.X; 2202 if (!no_init && con_out_ours == con_out) 2203 SetConsoleScreenBufferSize(con_out, size); 2204 pos_init(); 2205 wscroll = (sc_height + 1) / 2; 2206 screen_trashed(); 2207 } 2208 #endif 2209 } 2210 2211 /* 2212 * Goto a specific line on the screen. 2213 */ 2214 public void goto_line(int sindex) 2215 { 2216 assert_interactive(); 2217 #if !MSDOS_COMPILER 2218 ltputs(tgoto(sc_move, 0, sindex), 1, putchr); 2219 #else 2220 flush(); 2221 _settextposition(sindex+1, 1); 2222 #endif 2223 } 2224 2225 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC 2226 /* 2227 * Create an alternate screen which is all white. 2228 * This screen is used to create a "flash" effect, by displaying it 2229 * briefly and then switching back to the normal screen. 2230 * {{ Yuck! There must be a better way to get a visual bell. }} 2231 */ 2232 static void create_flash(void) 2233 { 2234 #if MSDOS_COMPILER==MSOFTC 2235 struct videoconfig w; 2236 char *blanks; 2237 int row, col; 2238 2239 _getvideoconfig(&w); 2240 videopages = w.numvideopages; 2241 if (videopages < 2) 2242 { 2243 at_enter(AT_STANDOUT); 2244 at_exit(); 2245 } else 2246 { 2247 _setactivepage(1); 2248 at_enter(AT_STANDOUT); 2249 blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); 2250 for (col = 0; col < w.numtextcols; col++) 2251 blanks[col] = ' '; 2252 for (row = w.numtextrows; row > 0; row--) 2253 _outmem(blanks, w.numtextcols); 2254 _setactivepage(0); 2255 _setvisualpage(0); 2256 free(blanks); 2257 at_exit(); 2258 } 2259 #else 2260 #if MSDOS_COMPILER==BORLANDC 2261 int n; 2262 2263 whitescreen = (unsigned short *) 2264 malloc(sc_width * sc_height * sizeof(short)); 2265 if (whitescreen == NULL) 2266 return; 2267 for (n = 0; n < sc_width * sc_height; n++) 2268 whitescreen[n] = 0x7020; 2269 #endif 2270 #endif 2271 flash_created = 1; 2272 } 2273 #endif /* MSDOS_COMPILER */ 2274 2275 /* 2276 * Output the "visual bell", if there is one. 2277 */ 2278 public void vbell(void) 2279 { 2280 if (no_vbell) 2281 return; 2282 #if !MSDOS_COMPILER 2283 if (*sc_visual_bell == '\0') 2284 return; 2285 ltputs(sc_visual_bell, sc_height, putchr); 2286 #else 2287 #if MSDOS_COMPILER==DJGPPC 2288 ScreenVisualBell(); 2289 #else 2290 #if MSDOS_COMPILER==MSOFTC 2291 /* 2292 * Create a flash screen on the second video page. 2293 * Switch to that page, then switch back. 2294 */ 2295 if (!flash_created) 2296 create_flash(); 2297 if (videopages < 2) 2298 return; 2299 _setvisualpage(1); 2300 delay(100); 2301 _setvisualpage(0); 2302 #else 2303 #if MSDOS_COMPILER==BORLANDC 2304 unsigned short *currscreen; 2305 2306 /* 2307 * Get a copy of the current screen. 2308 * Display the flash screen. 2309 * Then restore the old screen. 2310 */ 2311 if (!flash_created) 2312 create_flash(); 2313 if (whitescreen == NULL) 2314 return; 2315 currscreen = (unsigned short *) 2316 malloc(sc_width * sc_height * sizeof(short)); 2317 if (currscreen == NULL) return; 2318 gettext(1, 1, sc_width, sc_height, currscreen); 2319 puttext(1, 1, sc_width, sc_height, whitescreen); 2320 delay(100); 2321 puttext(1, 1, sc_width, sc_height, currscreen); 2322 free(currscreen); 2323 #else 2324 #if MSDOS_COMPILER==WIN32C 2325 /* paint screen with an inverse color */ 2326 clear(); 2327 2328 /* leave it displayed for 100 msec. */ 2329 Sleep(100); 2330 2331 /* restore with a redraw */ 2332 repaint(); 2333 #endif 2334 #endif 2335 #endif 2336 #endif 2337 #endif 2338 } 2339 2340 /* 2341 * Make a noise. 2342 */ 2343 static void beep(void) 2344 { 2345 #if !MSDOS_COMPILER 2346 putchr(CONTROL('G')); 2347 #else 2348 #if MSDOS_COMPILER==WIN32C 2349 MessageBeep(0); 2350 #else 2351 write(1, "\7", 1); 2352 #endif 2353 #endif 2354 } 2355 2356 /* 2357 * Ring the terminal bell. 2358 */ 2359 public void bell(void) 2360 { 2361 if (quiet == VERY_QUIET) 2362 vbell(); 2363 else 2364 beep(); 2365 } 2366 2367 /* 2368 * Clear the screen. 2369 */ 2370 public void clear(void) 2371 { 2372 assert_interactive(); 2373 suspend_screen(); 2374 #if !MSDOS_COMPILER 2375 ltputs(sc_clear, sc_height, putchr); 2376 #else 2377 flush(); 2378 #if MSDOS_COMPILER==WIN32C 2379 win32_clear(); 2380 #else 2381 _clearscreen(_GCLEARSCREEN); 2382 #endif 2383 #endif 2384 } 2385 2386 /* 2387 * Clear from the cursor to the end of the cursor's line. 2388 * {{ This must not move the cursor. }} 2389 */ 2390 public void clear_eol(void) 2391 { 2392 /* assert_interactive();*/ 2393 #if !MSDOS_COMPILER 2394 ltputs(sc_eol_clear, 1, putchr); 2395 #else 2396 #if MSDOS_COMPILER==MSOFTC 2397 short top, left; 2398 short bot, right; 2399 struct rccoord tpos; 2400 2401 flush(); 2402 /* 2403 * Save current state. 2404 */ 2405 tpos = _gettextposition(); 2406 _gettextwindow(&top, &left, &bot, &right); 2407 /* 2408 * Set a temporary window to the current line, 2409 * from the cursor's position to the right edge of the screen. 2410 * Then clear that window. 2411 */ 2412 _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); 2413 _clearscreen(_GWINDOW); 2414 /* 2415 * Restore state. 2416 */ 2417 _settextwindow(top, left, bot, right); 2418 _settextposition(tpos.row, tpos.col); 2419 #else 2420 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2421 flush(); 2422 clreol(); 2423 #else 2424 #if MSDOS_COMPILER==WIN32C 2425 DWORD nchars; 2426 COORD cpos; 2427 CONSOLE_SCREEN_BUFFER_INFO scr; 2428 2429 flush(); 2430 memset(&scr, 0, sizeof(scr)); 2431 GetConsoleScreenBufferInfo(con_out, &scr); 2432 cpos.X = scr.dwCursorPosition.X; 2433 cpos.Y = scr.dwCursorPosition.Y; 2434 curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); 2435 FillConsoleOutputAttribute(con_out, curr_attr, 2436 scr.dwSize.X - cpos.X, cpos, &nchars); 2437 FillConsoleOutputCharacter(con_out, ' ', 2438 scr.dwSize.X - cpos.X, cpos, &nchars); 2439 #endif 2440 #endif 2441 #endif 2442 #endif 2443 } 2444 2445 /* 2446 * Clear the current line. 2447 * Clear the screen if there's off-screen memory below the display. 2448 */ 2449 static void clear_eol_bot(void) 2450 { 2451 assert_interactive(); 2452 #if MSDOS_COMPILER 2453 clear_eol(); 2454 #else 2455 if (below_mem) 2456 ltputs(sc_eos_clear, 1, putchr); 2457 else 2458 ltputs(sc_eol_clear, 1, putchr); 2459 #endif 2460 } 2461 2462 /* 2463 * Clear the bottom line of the display. 2464 * Leave the cursor at the beginning of the bottom line. 2465 */ 2466 public void clear_bot(void) 2467 { 2468 /* 2469 * If we're in a non-normal attribute mode, temporarily exit 2470 * the mode while we do the clear. Some terminals fill the 2471 * cleared area with the current attribute. 2472 */ 2473 if (oldbot) 2474 lower_left(); 2475 else 2476 line_left(); 2477 2478 if (attrmode == AT_NORMAL) 2479 clear_eol_bot(); 2480 else 2481 { 2482 int saved_attrmode = attrmode; 2483 2484 at_exit(); 2485 clear_eol_bot(); 2486 at_enter(saved_attrmode); 2487 } 2488 } 2489 2490 /* 2491 * Enable or disable bracketed paste mode. 2492 * When enabled, the terminal sends an "open bracket" sequence 2493 * before pasted content and "close bracket" after it. 2494 */ 2495 public void init_bracketed_paste(void) 2496 { 2497 #if !MSDOS_COMPILER 2498 ltputs(sc_s_bracketed_paste, 1, putchr); 2499 #endif 2500 } 2501 2502 public void deinit_bracketed_paste(void) 2503 { 2504 #if !MSDOS_COMPILER 2505 ltputs(sc_e_bracketed_paste, 1, putchr); 2506 #endif 2507 } 2508 2509 /* 2510 * Color string may be "x[y]" where x and y are 4-bit color chars, 2511 * or "N[.M]" where N and M are decimal integers> 2512 * Any of x,y,N,M may also be "-" to mean "unchanged". 2513 */ 2514 2515 /* 2516 * Parse a 4-bit color char. 2517 */ 2518 static int parse_color4(char ch) 2519 { 2520 switch (ch) 2521 { 2522 case 'k': return 0; 2523 case 'r': return CV_RED; 2524 case 'g': return CV_GREEN; 2525 case 'y': return CV_RED|CV_GREEN; 2526 case 'b': return CV_BLUE; 2527 case 'm': return CV_RED|CV_BLUE; 2528 case 'c': return CV_GREEN|CV_BLUE; 2529 case 'w': return CV_RED|CV_GREEN|CV_BLUE; 2530 case 'K': return 0|CV_BRIGHT; 2531 case 'R': return CV_RED|CV_BRIGHT; 2532 case 'G': return CV_GREEN|CV_BRIGHT; 2533 case 'Y': return CV_RED|CV_GREEN|CV_BRIGHT; 2534 case 'B': return CV_BLUE|CV_BRIGHT; 2535 case 'M': return CV_RED|CV_BLUE|CV_BRIGHT; 2536 case 'C': return CV_GREEN|CV_BLUE|CV_BRIGHT; 2537 case 'W': return CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT; 2538 case '-': return CV_NOCHANGE; 2539 default: return CV_ERROR; 2540 } 2541 } 2542 2543 /* 2544 * Parse a color as a decimal integer. 2545 */ 2546 static int parse_color6(constant char **ps) 2547 { 2548 if (**ps == '-') 2549 { 2550 (*ps)++; 2551 return CV_NOCHANGE; 2552 } else 2553 { 2554 constant char *os = *ps; 2555 int color = lstrtoic(os, ps, 10); 2556 if (color < 0 || *ps == os) 2557 return CV_ERROR; 2558 return color; 2559 } 2560 } 2561 2562 /* 2563 * Parse a color pair and return the foreground/background/attribute values. 2564 * Return type of color specifier: 2565 * CV_4BIT: fg/bg values are OR of CV_{RGB} bits. 2566 * CV_6BIT: fg/bg values are integers entered by user. 2567 */ 2568 public COLOR_TYPE parse_color(constant char *str, mutable int *p_fg, mutable int *p_bg, mutable CHAR_ATTR *p_cattr) 2569 { 2570 int fg; 2571 int bg = CV_ERROR; 2572 CHAR_ATTR cattr = CATTR_NULL; 2573 COLOR_TYPE type = CT_NULL; 2574 2575 if (str == NULL || *str == '\0') 2576 return CT_NULL; 2577 if (*str == '+') 2578 str++; /* ignore leading + */ 2579 2580 fg = parse_color4(*str); 2581 if (fg != CV_ERROR) 2582 { 2583 if (str[1] == '\0' || strchr("*~_&dsul", str[1]) != NULL) 2584 { 2585 bg = CV_NOCHANGE; 2586 str++; /* skip the fg char */ 2587 } else 2588 { 2589 bg = parse_color4(str[1]); 2590 if (bg != CV_ERROR) 2591 str += 2; /* skip both fg and bg chars */ 2592 } 2593 } 2594 if (fg != CV_ERROR && bg != CV_ERROR) 2595 type = CT_4BIT; 2596 else 2597 { 2598 fg = (*str == '.') ? CV_NOCHANGE : parse_color6(&str); 2599 if (fg != CV_ERROR) 2600 { 2601 if (*str != '.') 2602 bg = CV_NOCHANGE; 2603 else 2604 { 2605 str++; /* skip the dot */ 2606 bg = parse_color6(&str); 2607 } 2608 } 2609 if (fg != CV_ERROR && bg != CV_ERROR) 2610 type = CT_6BIT; 2611 } 2612 if (type != CT_NULL) 2613 { 2614 for (;; str++) 2615 { 2616 if (*str == '*' || *str == 'd') 2617 cattr |= CATTR_BOLD; 2618 else if (*str == '~' || *str == 's') 2619 cattr |= CATTR_STANDOUT; 2620 else if (*str == '_' || *str == 'u') 2621 cattr |= CATTR_UNDERLINE; 2622 else if (*str == '&' || *str == 'l') /* can't use 'k' because of conflict with "black" */ 2623 cattr |= CATTR_BLINK; 2624 else 2625 break; 2626 } 2627 if (p_fg != NULL) *p_fg = fg; 2628 if (p_bg != NULL) *p_bg = bg; 2629 if (p_cattr != NULL) *p_cattr = cattr; 2630 } 2631 return type; 2632 } 2633 2634 #if !MSDOS_COMPILER 2635 2636 static int sgr_color(int color) 2637 { 2638 switch (color) 2639 { 2640 case 0: return 30; 2641 case CV_RED: return 31; 2642 case CV_GREEN: return 32; 2643 case CV_RED|CV_GREEN: return 33; 2644 case CV_BLUE: return 34; 2645 case CV_RED|CV_BLUE: return 35; 2646 case CV_GREEN|CV_BLUE: return 36; 2647 case CV_RED|CV_GREEN|CV_BLUE: return 37; 2648 2649 case CV_BRIGHT: return 90; 2650 case CV_RED|CV_BRIGHT: return 91; 2651 case CV_GREEN|CV_BRIGHT: return 92; 2652 case CV_RED|CV_GREEN|CV_BRIGHT: return 93; 2653 case CV_BLUE|CV_BRIGHT: return 94; 2654 case CV_RED|CV_BLUE|CV_BRIGHT: return 95; 2655 case CV_GREEN|CV_BLUE|CV_BRIGHT: return 96; 2656 case CV_RED|CV_GREEN|CV_BLUE|CV_BRIGHT: return 97; 2657 2658 default: return color; 2659 } 2660 } 2661 2662 static void tput_fmt(constant char *fmt, int color, int (*f_putc)(int)) 2663 { 2664 char buf[INT_STRLEN_BOUND(int)+16]; 2665 if (color == attrcolor) 2666 return; 2667 SNPRINTF1(buf, sizeof(buf), fmt, color); 2668 ltputs(buf, 1, f_putc); 2669 attrcolor = color; 2670 } 2671 2672 static void tput_char_cattr(CHAR_ATTR cattr, int (*f_putc)(int)) 2673 { 2674 if (cattr & CATTR_UNDERLINE) 2675 ltputs(sc_u_in, 1, f_putc); 2676 if (cattr & CATTR_BOLD) 2677 ltputs(sc_b_in, 1, f_putc); 2678 if (cattr & CATTR_BLINK) 2679 ltputs(sc_bl_in, 1, f_putc); 2680 if (cattr & CATTR_STANDOUT) 2681 ltputs(sc_s_in, 1, f_putc); 2682 } 2683 2684 static void tput_color(constant char *str, int (*f_putc)(int)) 2685 { 2686 int fg; 2687 int bg; 2688 CHAR_ATTR cattr; 2689 2690 if (str != NULL && strcmp(str, "*") == 0) 2691 { 2692 /* Special case: reset to normal */ 2693 tput_fmt(ESCS"[m", -1, f_putc); 2694 return; 2695 } 2696 switch (parse_color(str, &fg, &bg, &cattr)) 2697 { 2698 case CT_4BIT: 2699 if (fg >= 0) 2700 tput_fmt(ESCS"[%dm", sgr_color(fg), f_putc); 2701 if (bg >= 0) 2702 tput_fmt(ESCS"[%dm", sgr_color(bg)+10, f_putc); 2703 tput_char_cattr(cattr, f_putc); 2704 break; 2705 case CT_6BIT: 2706 if (fg >= 0) 2707 tput_fmt(ESCS"[38;5;%dm", fg, f_putc); 2708 if (bg >= 0) 2709 tput_fmt(ESCS"[48;5;%dm", bg, f_putc); 2710 tput_char_cattr(cattr, f_putc); 2711 break; 2712 default: 2713 break; 2714 } 2715 } 2716 2717 static void tput_inmode(constant char *mode_str, int attr, int attr_bit, int (*f_putc)(int)) 2718 { 2719 constant char *color_str; 2720 if ((attr & attr_bit) == 0) 2721 return; 2722 color_str = get_color_map(attr_bit); 2723 if (color_str == NULL || *color_str == '\0' || *color_str == '+') 2724 { 2725 ltputs(mode_str, 1, f_putc); 2726 if (color_str == NULL || *color_str++ != '+') 2727 return; 2728 } 2729 /* Color overrides mode string */ 2730 tput_color(color_str, f_putc); 2731 } 2732 2733 static void tput_outmode(constant char *mode_str, int attr_bit, int (*f_putc)(int)) 2734 { 2735 if ((attrmode & attr_bit) == 0) 2736 return; 2737 ltputs(mode_str, 1, f_putc); 2738 } 2739 2740 #else /* MSDOS_COMPILER */ 2741 2742 #if MSDOS_COMPILER==WIN32C 2743 static lbool WIN32put_fmt(constant char *fmt, int color) 2744 { 2745 char buf[INT_STRLEN_BOUND(int)+16]; 2746 int len = (size_t) SNPRINTF1(buf, sizeof(buf), fmt, color); 2747 if (len > 0) 2748 WIN32textout(buf, (size_t) len); 2749 return TRUE; 2750 } 2751 2752 static void win_set_cattr(CHAR_ATTR cattr) 2753 { 2754 if (cattr & CATTR_UNDERLINE) 2755 WIN32textout(ESCS"[4m", 4); 2756 if (cattr & CATTR_BOLD) 2757 WIN32textout(ESCS"[1m", 4); 2758 if (cattr & CATTR_BLINK) 2759 WIN32textout(ESCS"[5m", 4); 2760 if (cattr & CATTR_STANDOUT) 2761 WIN32textout(ESCS"[7m", 4); 2762 } 2763 #endif 2764 2765 static lbool win_set_color(int attr) 2766 { 2767 int fg; 2768 int bg; 2769 CHAR_ATTR cattr; 2770 lbool out = FALSE; 2771 constant char *str = get_color_map(attr); 2772 if (str == NULL || str[0] == '\0') 2773 return FALSE; 2774 switch (parse_color(str, &fg, &bg, &cattr)) 2775 { 2776 case CT_4BIT: 2777 if (fg >= 0 && bg >= 0) 2778 { 2779 SETCOLORS(fg, bg); 2780 out = TRUE; 2781 } else if (fg >= 0) 2782 { 2783 SET_FG_COLOR(fg); 2784 out = TRUE; 2785 } else if (bg >= 0) 2786 { 2787 SET_BG_COLOR(bg); 2788 out = TRUE; 2789 } 2790 #if MSDOS_COMPILER==WIN32C 2791 if (vt_enabled) 2792 win_set_cattr(cattr); 2793 #endif 2794 break; 2795 #if MSDOS_COMPILER==WIN32C 2796 case CT_6BIT: 2797 if (vt_enabled) 2798 { 2799 if (fg > 0) 2800 out = WIN32put_fmt(ESCS"[38;5;%dm", fg); 2801 if (bg > 0) 2802 out = WIN32put_fmt(ESCS"[48;5;%dm", bg); 2803 win_set_cattr(cattr); 2804 } 2805 break; 2806 #endif 2807 default: 2808 break; 2809 } 2810 return out; 2811 } 2812 2813 #endif /* MSDOS_COMPILER */ 2814 2815 public void at_enter(int attr) 2816 { 2817 attr = apply_at_specials(attr); 2818 #if !MSDOS_COMPILER 2819 /* The one with the most priority is last. */ 2820 tput_inmode(sc_u_in, attr, AT_UNDERLINE, putchr); 2821 tput_inmode(sc_b_in, attr, AT_BOLD, putchr); 2822 tput_inmode(sc_bl_in, attr, AT_BLINK, putchr); 2823 /* Don't use standout and color at the same time. */ 2824 if (use_color && (attr & AT_COLOR)) 2825 tput_color(get_color_map(attr), putchr); 2826 else 2827 tput_inmode(sc_s_in, attr, AT_STANDOUT, putchr); 2828 #else 2829 flush(); 2830 /* The one with the most priority is first. */ 2831 if ((attr & AT_COLOR) && use_color) 2832 { 2833 win_set_color(attr); 2834 } else if (attr & AT_STANDOUT) 2835 { 2836 SETCOLORS(so_fg_color, so_bg_color); 2837 } else if (attr & AT_BLINK) 2838 { 2839 SETCOLORS(bl_fg_color, bl_bg_color); 2840 } else if (attr & AT_BOLD) 2841 { 2842 SETCOLORS(bo_fg_color, bo_bg_color); 2843 } else if (attr & AT_UNDERLINE) 2844 { 2845 SETCOLORS(ul_fg_color, ul_bg_color); 2846 } 2847 #endif 2848 attrmode = attr; 2849 } 2850 2851 public void at_exit(void) 2852 { 2853 #if !MSDOS_COMPILER 2854 /* Undo things in the reverse order we did them. */ 2855 tput_color("*", putchr); 2856 tput_outmode(sc_s_out, AT_STANDOUT, putchr); 2857 tput_outmode(sc_bl_out, AT_BLINK, putchr); 2858 tput_outmode(sc_b_out, AT_BOLD, putchr); 2859 tput_outmode(sc_u_out, AT_UNDERLINE, putchr); 2860 #else 2861 flush(); 2862 SETCOLORS(nm_fg_color, nm_bg_color); 2863 #endif 2864 attrmode = AT_NORMAL; 2865 } 2866 2867 public void at_switch(int attr) 2868 { 2869 int new_attrmode = apply_at_specials(attr); 2870 int ignore_modes = AT_ANSI; 2871 2872 if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) 2873 { 2874 at_exit(); 2875 at_enter(attr); 2876 } 2877 } 2878 2879 public lbool is_at_equiv(int attr1, int attr2) 2880 { 2881 attr1 = apply_at_specials(attr1); 2882 attr2 = apply_at_specials(attr2); 2883 2884 return (attr1 == attr2); 2885 } 2886 2887 public int apply_at_specials(int attr) 2888 { 2889 if (attr & AT_BINARY) 2890 attr |= binattr; 2891 if (attr & AT_HILITE) 2892 attr |= AT_STANDOUT; 2893 attr &= ~(AT_BINARY|AT_HILITE); 2894 2895 return attr; 2896 } 2897 2898 /* 2899 * Output a plain backspace, without erasing the previous char. 2900 */ 2901 public void putbs(void) 2902 { 2903 if (termcap_debug) 2904 putstr("<bs>"); 2905 else 2906 { 2907 #if !MSDOS_COMPILER 2908 ltputs(sc_backspace, 1, putchr); 2909 #else 2910 int row, col; 2911 2912 flush(); 2913 { 2914 #if MSDOS_COMPILER==MSOFTC 2915 struct rccoord tpos; 2916 tpos = _gettextposition(); 2917 row = tpos.row; 2918 col = tpos.col; 2919 #else 2920 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 2921 row = wherey(); 2922 col = wherex(); 2923 #else 2924 #if MSDOS_COMPILER==WIN32C 2925 CONSOLE_SCREEN_BUFFER_INFO scr; 2926 GetConsoleScreenBufferInfo(con_out, &scr); 2927 row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; 2928 col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; 2929 #endif 2930 #endif 2931 #endif 2932 } 2933 if (col <= 1) 2934 return; 2935 _settextposition(row, col-1); 2936 #endif /* MSDOS_COMPILER */ 2937 } 2938 } 2939 2940 #if MSDOS_COMPILER==WIN32C 2941 2942 #define WIN32_MAX_REPEAT 3 2943 #define LAST_DOWN_COUNT 8 2944 static LWCHAR last_downs[LAST_DOWN_COUNT] = { 0 }; 2945 static int last_down_index = 0; 2946 static LWCHAR hi_surr = 0; 2947 2948 typedef struct XINPUT_RECORD { 2949 INPUT_RECORD ir; 2950 LWCHAR ichar; /* because ir...UnicodeChar is only 16 bits */ 2951 } XINPUT_RECORD; 2952 2953 typedef struct WIN32_CHAR { 2954 struct WIN32_CHAR *wc_next; 2955 char wc_ch; 2956 } WIN32_CHAR; 2957 2958 static WIN32_CHAR *win32_queue = NULL; 2959 2960 /* 2961 * Is the win32_queue nonempty? 2962 */ 2963 static int win32_queued_char(void) 2964 { 2965 return (win32_queue != NULL); 2966 } 2967 2968 /* 2969 * Push a char onto the back of the win32_queue. 2970 */ 2971 static void win32_enqueue(char ch) 2972 { 2973 WIN32_CHAR *wch = (WIN32_CHAR *) ecalloc(1, sizeof(WIN32_CHAR)); 2974 wch->wc_ch = ch; 2975 wch->wc_next = NULL; 2976 if (win32_queue == NULL) 2977 win32_queue = wch; 2978 else 2979 { 2980 WIN32_CHAR *pch; 2981 for (pch = win32_queue; pch->wc_next != NULL; pch = pch->wc_next) 2982 continue; 2983 pch->wc_next = wch; 2984 } 2985 } 2986 2987 /* 2988 * Push a char onto the front of the win32_queue. 2989 * Makes the next call to WIN32getch return ch. 2990 */ 2991 public void WIN32ungetch(int ch) 2992 { 2993 WIN32_CHAR *wch = (WIN32_CHAR *) ecalloc(1, sizeof(WIN32_CHAR)); 2994 wch->wc_ch = ch; 2995 wch->wc_next = win32_queue; 2996 win32_queue = wch; 2997 } 2998 2999 /* 3000 * Get a char from the front of the win32_queue. 3001 */ 3002 static char win32_get_queue(void) 3003 { 3004 WIN32_CHAR *wch = win32_queue; 3005 char ch = wch->wc_ch; 3006 win32_queue = wch->wc_next; 3007 free(wch); 3008 return ch; 3009 } 3010 3011 /* 3012 * Handle a mouse input event. 3013 */ 3014 static lbool win32_mouse_event(XINPUT_RECORD *xip) 3015 { 3016 char b; 3017 3018 if (!mousecap || xip->ir.EventType != MOUSE_EVENT) 3019 return (FALSE); 3020 3021 /* Generate an X11 mouse sequence from the mouse event. */ 3022 /* TODO: switch to the 1006 protocol to allow specific-button-up reports */ 3023 switch (xip->ir.Event.MouseEvent.dwEventFlags) 3024 { 3025 case 0: /* press or release */ 3026 if (xip->ir.Event.MouseEvent.dwButtonState == 0) 3027 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON_REL; 3028 else if (xip->ir.Event.MouseEvent.dwButtonState == 1) /* leftmost */ 3029 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON1; 3030 else if (xip->ir.Event.MouseEvent.dwButtonState == 2) /* rightmost */ 3031 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON3; 3032 else if (xip->ir.Event.MouseEvent.dwButtonState == 4) /* middle ("next-to-leftmost") */ 3033 b = X11MOUSE_OFFSET + X11MOUSE_BUTTON2; 3034 else /* don't bother to figure out what changed */ 3035 return (FALSE); 3036 break; 3037 case MOUSE_WHEELED: 3038 b = X11MOUSE_OFFSET + (((int)xip->ir.Event.MouseEvent.dwButtonState < 0) ? X11MOUSE_WHEEL_DOWN : X11MOUSE_WHEEL_UP); 3039 break; 3040 case MOUSE_MOVED: 3041 if (xip->ir.Event.MouseEvent.dwButtonState != 1) 3042 return (FALSE); 3043 /* Drag with left button down. */ 3044 b = X11MOUSE_OFFSET + X11MOUSE_DRAG; 3045 break; 3046 default: 3047 return (FALSE); 3048 } 3049 /* {{ TODO: change to X11 1006 format. }} */ 3050 win32_enqueue(ESC); 3051 win32_enqueue('['); 3052 win32_enqueue('M'); 3053 win32_enqueue(b); 3054 win32_enqueue(X11MOUSE_OFFSET + xip->ir.Event.MouseEvent.dwMousePosition.X + 1); 3055 win32_enqueue(X11MOUSE_OFFSET + xip->ir.Event.MouseEvent.dwMousePosition.Y + 1); 3056 return (TRUE); 3057 } 3058 3059 static void set_last_down(LWCHAR ch) 3060 { 3061 if (ch == 0) return; 3062 last_downs[last_down_index] = ch; 3063 if (++last_down_index >= LAST_DOWN_COUNT) 3064 last_down_index = 0; 3065 } 3066 3067 static LWCHAR *find_last_down(LWCHAR ch) 3068 { 3069 int i; 3070 for (i = 0; i < LAST_DOWN_COUNT; ++i) 3071 if (last_downs[i] == ch) 3072 return &last_downs[i]; 3073 return NULL; 3074 } 3075 3076 /* 3077 * Get an input char from an INPUT_RECORD and store in xip->ichar. 3078 * Handles surrogate chars, and KeyUp without previous corresponding KeyDown. 3079 */ 3080 static lbool win32_get_ichar(XINPUT_RECORD *xip) 3081 { 3082 LWCHAR ch = xip->ir.Event.KeyEvent.uChar.UnicodeChar; 3083 xip->ichar = ch; 3084 if (!is_ascii_char(ch)) 3085 { 3086 int is_down = xip->ir.Event.KeyEvent.bKeyDown; 3087 LWCHAR *last_down = find_last_down(ch); 3088 if (last_down == NULL) { /* key was up */ 3089 if (is_down) { /* key was up, now is down */ 3090 set_last_down(ch); 3091 } else { /* key up without previous down: pretend this is a down. */ 3092 xip->ir.Event.KeyEvent.bKeyDown = 1; 3093 } 3094 } else if (!is_down) { /* key was down, now is up */ 3095 *last_down = 0; /* use this last_down only once */ 3096 } 3097 3098 if (ch >= 0xD800 && ch < 0xDC00) { /* high surrogate */ 3099 hi_surr = 0x10000 + ((ch - 0xD800) << 10); 3100 return (FALSE); /* get next input, which should be the low surrogate */ 3101 } 3102 if (ch >= 0xDC00 && ch < 0xE000) { /* low surrogate */ 3103 xip->ichar = hi_surr + (ch - 0xDC00); 3104 hi_surr = 0; 3105 } 3106 } 3107 return (TRUE); 3108 } 3109 3110 /* 3111 * Handle a scan code (non-ASCII) key input. 3112 */ 3113 static lbool win32_scan_code(XINPUT_RECORD *xip) 3114 { 3115 int scan = -1; 3116 if (xip->ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) 3117 { 3118 switch (xip->ir.Event.KeyEvent.wVirtualScanCode) 3119 { 3120 case PCK_RIGHT: /* right arrow */ 3121 scan = PCK_CTL_RIGHT; 3122 break; 3123 case PCK_LEFT: /* left arrow */ 3124 scan = PCK_CTL_LEFT; 3125 break; 3126 case PCK_DELETE: /* delete */ 3127 scan = PCK_CTL_DELETE; 3128 break; 3129 } 3130 } else if (xip->ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) 3131 { 3132 if (xip->ichar == '\t') 3133 scan = PCK_SHIFT_TAB; 3134 } 3135 if (scan < 0 && xip->ichar == 0) 3136 scan = xip->ir.Event.KeyEvent.wVirtualScanCode; 3137 if (scan < 0) 3138 return (FALSE); 3139 /* 3140 * An extended key returns a 2 byte sequence consisting of 3141 * a zero byte followed by the scan code. 3142 */ 3143 win32_enqueue('\0'); 3144 win32_enqueue(scan); 3145 return (TRUE); 3146 } 3147 3148 /* 3149 * Handle a key input event. 3150 */ 3151 static lbool win32_key_event(XINPUT_RECORD *xip) 3152 { 3153 int repeat; 3154 char utf8[UTF8_MAX_LENGTH]; 3155 char *up; 3156 3157 if (xip->ir.EventType != KEY_EVENT || 3158 ((xip->ir.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED)) == (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED) && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3159 (xip->ir.Event.KeyEvent.wVirtualScanCode == 0 && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3160 xip->ir.Event.KeyEvent.wVirtualScanCode == PCK_CAPS_LOCK || 3161 xip->ir.Event.KeyEvent.wVirtualScanCode == PCK_NUM_LOCK || 3162 (xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU && xip->ir.Event.KeyEvent.uChar.UnicodeChar == 0) || 3163 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_KANJI || 3164 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || 3165 xip->ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL) 3166 return (FALSE); 3167 3168 if (!win32_get_ichar(xip)) 3169 return (FALSE); 3170 if (!xip->ir.Event.KeyEvent.bKeyDown) 3171 return (FALSE); 3172 3173 if (win32_scan_code(xip)) 3174 return (TRUE); 3175 3176 repeat = xip->ir.Event.KeyEvent.wRepeatCount; 3177 if (repeat > WIN32_MAX_REPEAT) 3178 repeat = WIN32_MAX_REPEAT; 3179 up = utf8; 3180 put_wchar(&up, xip->ichar); 3181 for (; repeat > 0; --repeat) 3182 { 3183 constant char *p; 3184 for (p = utf8; p < up; ++p) 3185 win32_enqueue(*p); 3186 } 3187 return (TRUE); 3188 } 3189 3190 /* 3191 * Determine whether an input character is waiting to be read. 3192 */ 3193 public lbool win32_kbhit2(lbool no_queued) 3194 { 3195 XINPUT_RECORD xip; 3196 3197 if (!no_queued && win32_queued_char()) 3198 return (TRUE); 3199 3200 for (;;) 3201 { 3202 DWORD nread; 3203 DWORD console_input_mode; 3204 /* 3205 * When an input pipe closes, cmd may reset the console mode, 3206 * so set the mode every time we read input. 3207 */ 3208 if (GetConsoleMode(tty, &console_input_mode) && console_input_mode != curr_console_input_mode) 3209 SetConsoleMode(tty, curr_console_input_mode); 3210 PeekConsoleInputW(tty, &xip.ir, 1, &nread); 3211 if (nread == 0) 3212 return (FALSE); 3213 ReadConsoleInputW(tty, &xip.ir, 1, &nread); 3214 if (nread == 0) 3215 return (FALSE); 3216 if (win32_mouse_event(&xip) || win32_key_event(&xip)) 3217 break; 3218 } 3219 return (TRUE); 3220 } 3221 3222 public lbool win32_kbhit(void) 3223 { 3224 return win32_kbhit2(FALSE); 3225 } 3226 3227 /* 3228 * Read a character from the keyboard. 3229 */ 3230 public char WIN32getch(void) 3231 { 3232 while (!win32_kbhit()) 3233 { 3234 Sleep(20); 3235 if (ABORT_SIGS()) 3236 return ('\003'); 3237 } 3238 return (win32_get_queue()); 3239 } 3240 3241 public void win32_getch_clear(void) 3242 { 3243 while (win32_kbhit()) 3244 (void) WIN32getch(); 3245 } 3246 3247 #endif /* MSDOS_COMPILER==WIN32C */ 3248 3249 #if MSDOS_COMPILER 3250 /* 3251 */ 3252 public void WIN32setcolors(int fg, int bg) 3253 { 3254 SETCOLORS(fg, bg); 3255 } 3256 3257 /* 3258 */ 3259 public void WIN32textout(constant char *text, size_t len) 3260 { 3261 #if MSDOS_COMPILER==WIN32C 3262 DWORD written; 3263 if (utf_mode == 2) 3264 { 3265 /* 3266 * We've got UTF-8 text in a non-UTF-8 console. Convert it to 3267 * wide and use WriteConsoleW. 3268 * Biggest input len is OUTBUF_SIZE of obuf from win_flush, 3269 * which is also the biggest output count if it's ASCII. 3270 * "static" wtext is not a state - only avoid 16K on stack. 3271 */ 3272 static WCHAR wtext[OUTBUF_SIZE]; 3273 len = MultiByteToWideChar(CP_UTF8, 0, text, len, wtext, countof(wtext)); 3274 WriteConsoleW(con_out, wtext, len, &written, NULL); 3275 } else 3276 WriteConsole(con_out, text, (DWORD) len, &written, NULL); 3277 #else 3278 char buf[2048]; 3279 if (len >= sizeof(buf)) 3280 len = sizeof(buf) - 1; 3281 memcpy(buf, text, len); 3282 buf[len] = 0; 3283 cputs(buf); 3284 #endif 3285 } 3286 #endif 3287