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