1 /**************************************************************************** 2 * Copyright (c) 2008-2012,2013 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Juergen Pfeifer * 31 * * 32 ****************************************************************************/ 33 34 #include <curses.priv.h> 35 #define CUR ((TERMINAL*)TCB)->type. 36 #include <tic.h> 37 38 #if HAVE_NANOSLEEP 39 #include <time.h> 40 #if HAVE_SYS_TIME_H 41 #include <sys/time.h> /* needed for MacOS X DP3 */ 42 #endif 43 #endif 44 45 #if HAVE_SIZECHANGE 46 # if !defined(sun) || !TERMIOS 47 # if HAVE_SYS_IOCTL_H 48 # include <sys/ioctl.h> 49 # endif 50 # endif 51 #endif 52 53 MODULE_ID("$Id: tinfo_driver.c,v 1.32 2013/08/31 15:22:46 tom Exp $") 54 55 /* 56 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS, 57 * Solaris, IRIX) define TIOCGWINSZ and struct winsize. 58 */ 59 #ifdef TIOCGSIZE 60 # define IOCTL_WINSIZE TIOCGSIZE 61 # define STRUCT_WINSIZE struct ttysize 62 # define WINSIZE_ROWS(n) (int)n.ts_lines 63 # define WINSIZE_COLS(n) (int)n.ts_cols 64 #else 65 # ifdef TIOCGWINSZ 66 # define IOCTL_WINSIZE TIOCGWINSZ 67 # define STRUCT_WINSIZE struct winsize 68 # define WINSIZE_ROWS(n) (int)n.ws_row 69 # define WINSIZE_COLS(n) (int)n.ws_col 70 # endif 71 #endif 72 73 /* 74 * These should be screen structure members. They need to be globals for 75 * historical reasons. So we assign them in start_color() and also in 76 * set_term()'s screen-switching logic. 77 */ 78 #if USE_REENTRANT 79 NCURSES_EXPORT(int) 80 NCURSES_PUBLIC_VAR(COLOR_PAIRS) (void) 81 { 82 return CURRENT_SCREEN ? CURRENT_SCREEN->_pair_count : -1; 83 } 84 NCURSES_EXPORT(int) 85 NCURSES_PUBLIC_VAR(COLORS) (void) 86 { 87 return CURRENT_SCREEN ? CURRENT_SCREEN->_color_count : -1; 88 } 89 #else 90 NCURSES_EXPORT_VAR(int) COLOR_PAIRS = 0; 91 NCURSES_EXPORT_VAR(int) COLORS = 0; 92 #endif 93 94 #define TCBMAGIC NCDRV_MAGIC(NCDRV_TINFO) 95 #define AssertTCB() assert(TCB!=0 && TCB->magic==TCBMAGIC) 96 #define SetSP() assert(TCB->csp!=0); sp = TCB->csp; (void) sp 97 98 /* 99 * This routine needs to do all the work to make curscr look 100 * like newscr. 101 */ 102 static int 103 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB) 104 { 105 AssertTCB(); 106 return TINFO_DOUPDATE(TCB->csp); 107 } 108 109 static bool 110 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, const char *tname, int *errret) 111 { 112 bool result = FALSE; 113 int status; 114 TERMINAL *termp; 115 SCREEN *sp; 116 117 assert(TCB != 0 && tname != 0); 118 termp = (TERMINAL *) TCB; 119 sp = TCB->csp; 120 TCB->magic = TCBMAGIC; 121 122 #if (NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP) 123 status = _nc_setup_tinfo(tname, &termp->type); 124 #else 125 status = TGETENT_NO; 126 #endif 127 128 /* try fallback list if entry on disk */ 129 if (status != TGETENT_YES) { 130 const TERMTYPE *fallback = _nc_fallback(tname); 131 132 if (fallback) { 133 termp->type = *fallback; 134 status = TGETENT_YES; 135 } 136 } 137 138 if (status != TGETENT_YES) { 139 NCURSES_SP_NAME(del_curterm) (NCURSES_SP_ARGx termp); 140 if (status == TGETENT_ERR) { 141 ret_error0(status, "terminals database is inaccessible\n"); 142 } else if (status == TGETENT_NO) { 143 ret_error1(status, "unknown terminal type.\n", tname); 144 } 145 } 146 result = TRUE; 147 #if !USE_REENTRANT 148 strncpy(ttytype, termp->type.term_names, (size_t) NAMESIZE - 1); 149 ttytype[NAMESIZE - 1] = '\0'; 150 #endif 151 152 if (command_character) 153 _nc_tinfo_cmdch(termp, *command_character); 154 155 if (generic_type) { 156 /* 157 * BSD 4.3's termcap contains mis-typed "gn" for wy99. Do a sanity 158 * check before giving up. 159 */ 160 if ((VALID_STRING(cursor_address) 161 || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home))) 162 && VALID_STRING(clear_screen)) { 163 ret_error1(TGETENT_YES, "terminal is not really generic.\n", tname); 164 } else { 165 ret_error1(TGETENT_NO, "I need something more specific.\n", tname); 166 } 167 } 168 if (hard_copy) { 169 ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n", tname); 170 } 171 172 return result; 173 } 174 175 static int 176 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB, int beepFlag) 177 { 178 SCREEN *sp; 179 int res = ERR; 180 181 AssertTCB(); 182 SetSP(); 183 184 /* FIXME: should make sure that we are not in altchar mode */ 185 if (beepFlag) { 186 if (bell) { 187 res = NCURSES_PUTP2("bell", bell); 188 NCURSES_SP_NAME(_nc_flush) (sp); 189 } else if (flash_screen) { 190 res = NCURSES_PUTP2("flash_screen", flash_screen); 191 NCURSES_SP_NAME(_nc_flush) (sp); 192 } 193 } else { 194 if (flash_screen) { 195 res = NCURSES_PUTP2("flash_screen", flash_screen); 196 NCURSES_SP_NAME(_nc_flush) (sp); 197 } else if (bell) { 198 res = NCURSES_PUTP2("bell", bell); 199 NCURSES_SP_NAME(_nc_flush) (sp); 200 } 201 } 202 return res; 203 } 204 205 /* 206 * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly 207 * to maintain compatibility with a pre-ANSI scheme. The same scheme is 208 * also used in the FreeBSD syscons. 209 */ 210 static int 211 toggled_colors(int c) 212 { 213 if (c < 16) { 214 static const int table[] = 215 {0, 4, 2, 6, 1, 5, 3, 7, 216 8, 12, 10, 14, 9, 13, 11, 15}; 217 c = table[c]; 218 } 219 return c; 220 } 221 222 static int 223 drv_print(TERMINAL_CONTROL_BLOCK * TCB, char *data, int len) 224 { 225 SCREEN *sp; 226 227 AssertTCB(); 228 SetSP(); 229 #if NCURSES_EXT_FUNCS 230 return NCURSES_SP_NAME(mcprint) (TCB->csp, data, len); 231 #else 232 return ERR; 233 #endif 234 } 235 236 static int 237 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB, int fg, int bg) 238 { 239 SCREEN *sp; 240 int code = ERR; 241 242 AssertTCB(); 243 SetSP(); 244 245 if (sp != 0 && orig_pair && orig_colors && (initialize_pair != 0)) { 246 #if NCURSES_EXT_FUNCS 247 sp->_default_color = isDefaultColor(fg) || isDefaultColor(bg); 248 sp->_has_sgr_39_49 = (NCURSES_SP_NAME(tigetflag) (NCURSES_SP_ARGx 249 "AX") 250 == TRUE); 251 sp->_default_fg = isDefaultColor(fg) ? COLOR_DEFAULT : (fg & C_MASK); 252 sp->_default_bg = isDefaultColor(bg) ? COLOR_DEFAULT : (bg & C_MASK); 253 if (sp->_color_pairs != 0) { 254 bool save = sp->_default_color; 255 sp->_default_color = TRUE; 256 NCURSES_SP_NAME(init_pair) (NCURSES_SP_ARGx 257 0, 258 (short)fg, 259 (short)bg); 260 sp->_default_color = save; 261 } 262 #endif 263 code = OK; 264 } 265 return (code); 266 } 267 268 static void 269 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB, 270 int fore, 271 int color, 272 NCURSES_SP_OUTC outc) 273 { 274 SCREEN *sp; 275 276 AssertTCB(); 277 SetSP(); 278 279 if (fore) { 280 if (set_a_foreground) { 281 TPUTS_TRACE("set_a_foreground"); 282 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 283 TPARM_1(set_a_foreground, color), 1, outc); 284 } else { 285 TPUTS_TRACE("set_foreground"); 286 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 287 TPARM_1(set_foreground, 288 toggled_colors(color)), 1, outc); 289 } 290 } else { 291 if (set_a_background) { 292 TPUTS_TRACE("set_a_background"); 293 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 294 TPARM_1(set_a_background, color), 1, outc); 295 } else { 296 TPUTS_TRACE("set_background"); 297 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 298 TPARM_1(set_background, 299 toggled_colors(color)), 1, outc); 300 } 301 } 302 } 303 304 static bool 305 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB) 306 { 307 bool result = FALSE; 308 SCREEN *sp; 309 310 AssertTCB(); 311 SetSP(); 312 313 if (orig_pair != 0) { 314 NCURSES_PUTP2("orig_pair", orig_pair); 315 result = TRUE; 316 } 317 return result; 318 } 319 320 static bool 321 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB) 322 { 323 int result = FALSE; 324 SCREEN *sp; 325 326 AssertTCB(); 327 SetSP(); 328 329 if (orig_colors != 0) { 330 NCURSES_PUTP2("orig_colors", orig_colors); 331 result = TRUE; 332 } 333 return result; 334 } 335 336 static int 337 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *linep, int *colp) 338 { 339 SCREEN *sp; 340 bool useEnv = TRUE; 341 bool useTioctl = TRUE; 342 343 AssertTCB(); 344 sp = TCB->csp; /* can be null here */ 345 346 if (sp) { 347 useEnv = sp->_use_env; 348 useTioctl = sp->_use_tioctl; 349 } else { 350 useEnv = _nc_prescreen.use_env; 351 useTioctl = _nc_prescreen.use_tioctl; 352 } 353 354 /* figure out the size of the screen */ 355 T(("screen size: terminfo lines = %d columns = %d", lines, columns)); 356 357 *linep = (int) lines; 358 *colp = (int) columns; 359 360 if (useEnv || useTioctl) { 361 int value; 362 363 #ifdef __EMX__ 364 { 365 int screendata[2]; 366 _scrsize(screendata); 367 *colp = screendata[0]; 368 *linep = ((sp != 0 && sp->_filtered) 369 ? 1 370 : screendata[1]); 371 T(("EMX screen size: environment LINES = %d COLUMNS = %d", 372 *linep, *colp)); 373 } 374 #endif 375 #if HAVE_SIZECHANGE 376 /* try asking the OS */ 377 { 378 TERMINAL *termp = (TERMINAL *) TCB; 379 if (isatty(termp->Filedes)) { 380 STRUCT_WINSIZE size; 381 382 errno = 0; 383 do { 384 if (ioctl(termp->Filedes, IOCTL_WINSIZE, &size) >= 0) { 385 *linep = ((sp != 0 && sp->_filtered) 386 ? 1 387 : WINSIZE_ROWS(size)); 388 *colp = WINSIZE_COLS(size); 389 T(("SYS screen size: environment LINES = %d COLUMNS = %d", 390 *linep, *colp)); 391 break; 392 } 393 } while 394 (errno == EINTR); 395 } 396 } 397 #endif /* HAVE_SIZECHANGE */ 398 399 if (useEnv) { 400 if (useTioctl) { 401 /* 402 * If environment variables are used, update them. 403 */ 404 if ((sp == 0 || !sp->_filtered) && _nc_getenv_num("LINES") > 0) { 405 _nc_setenv_num("LINES", *linep); 406 } 407 if (_nc_getenv_num("COLUMNS") > 0) { 408 _nc_setenv_num("COLUMNS", *colp); 409 } 410 } 411 412 /* 413 * Finally, look for environment variables. 414 * 415 * Solaris lets users override either dimension with an environment 416 * variable. 417 */ 418 if ((value = _nc_getenv_num("LINES")) > 0) { 419 *linep = value; 420 T(("screen size: environment LINES = %d", *linep)); 421 } 422 if ((value = _nc_getenv_num("COLUMNS")) > 0) { 423 *colp = value; 424 T(("screen size: environment COLUMNS = %d", *colp)); 425 } 426 } 427 428 /* if we can't get dynamic info about the size, use static */ 429 if (*linep <= 0) { 430 *linep = (int) lines; 431 } 432 if (*colp <= 0) { 433 *colp = (int) columns; 434 } 435 436 /* the ultimate fallback, assume fixed 24x80 size */ 437 if (*linep <= 0) { 438 *linep = 24; 439 } 440 if (*colp <= 0) { 441 *colp = 80; 442 } 443 444 /* 445 * Put the derived values back in the screen-size caps, so 446 * tigetnum() and tgetnum() will do the right thing. 447 */ 448 lines = (short) (*linep); 449 columns = (short) (*colp); 450 } 451 452 T(("screen size is %dx%d", *linep, *colp)); 453 return OK; 454 } 455 456 static int 457 drv_getsize(TERMINAL_CONTROL_BLOCK * TCB, int *l, int *c) 458 { 459 AssertTCB(); 460 assert(l != 0 && c != 0); 461 *l = lines; 462 *c = columns; 463 return OK; 464 } 465 466 static int 467 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB, int l, int c) 468 { 469 AssertTCB(); 470 lines = (short) l; 471 columns = (short) c; 472 return OK; 473 } 474 475 static int 476 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf) 477 { 478 SCREEN *sp = TCB->csp; 479 TERMINAL *_term = (TERMINAL *) TCB; 480 int result = OK; 481 482 AssertTCB(); 483 if (setFlag) { 484 for (;;) { 485 if (SET_TTY(_term->Filedes, buf) != 0) { 486 if (errno == EINTR) 487 continue; 488 if (errno == ENOTTY) { 489 if (sp) 490 sp->_notty = TRUE; 491 } 492 result = ERR; 493 } 494 break; 495 } 496 } else { 497 for (;;) { 498 if (GET_TTY(_term->Filedes, buf) != 0) { 499 if (errno == EINTR) 500 continue; 501 result = ERR; 502 } 503 break; 504 } 505 } 506 return result; 507 } 508 509 static int 510 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag) 511 { 512 SCREEN *sp; 513 TERMINAL *_term = (TERMINAL *) TCB; 514 int code = ERR; 515 516 AssertTCB(); 517 sp = TCB->csp; 518 519 if (progFlag) /* prog mode */ 520 { 521 if (defFlag) { 522 /* def_prog_mode */ 523 /* 524 * Turn off the XTABS bit in the tty structure if it was on. 525 */ 526 if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) { 527 #ifdef TERMIOS 528 _term->Nttyb.c_oflag &= (unsigned) ~OFLAGS_TABS; 529 #else 530 _term->Nttyb.sg_flags &= (unsigned) ~XTABS; 531 #endif 532 code = OK; 533 } 534 } else { 535 /* reset_prog_mode */ 536 if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) { 537 if (sp) { 538 if (sp->_keypad_on) 539 _nc_keypad(sp, TRUE); 540 NC_BUFFERED(sp, TRUE); 541 } 542 code = OK; 543 } 544 } 545 } else { /* shell mode */ 546 if (defFlag) { 547 /* def_shell_mode */ 548 /* 549 * If XTABS was on, remove the tab and backtab capabilities. 550 */ 551 if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) { 552 #ifdef TERMIOS 553 if (_term->Ottyb.c_oflag & OFLAGS_TABS) 554 tab = back_tab = NULL; 555 #else 556 if (_term->Ottyb.sg_flags & XTABS) 557 tab = back_tab = NULL; 558 #endif 559 code = OK; 560 } 561 } else { 562 /* reset_shell_mode */ 563 if (sp) { 564 _nc_keypad(sp, FALSE); 565 NCURSES_SP_NAME(_nc_flush) (sp); 566 NC_BUFFERED(sp, FALSE); 567 } 568 code = drv_sgmode(TCB, TRUE, &(_term->Ottyb)); 569 } 570 } 571 return (code); 572 } 573 574 static void 575 drv_wrap(SCREEN *sp) 576 { 577 if (sp) { 578 sp->_mouse_wrap(sp); 579 NCURSES_SP_NAME(_nc_screen_wrap) (sp); 580 NCURSES_SP_NAME(_nc_mvcur_wrap) (sp); /* wrap up cursor addressing */ 581 } 582 } 583 584 static void 585 drv_release(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED) 586 { 587 } 588 589 # define SGR0_TEST(mode) (mode != 0) && (exit_attribute_mode == 0 || strcmp(mode, exit_attribute_mode)) 590 591 static void 592 drv_screen_init(SCREEN *sp) 593 { 594 TERMINAL_CONTROL_BLOCK *TCB = TCBOf(sp); 595 596 AssertTCB(); 597 598 /* 599 * Check for mismatched graphic-rendition capabilities. Most SVr4 600 * terminfo trees contain entries that have rmul or rmso equated to 601 * sgr0 (Solaris curses copes with those entries). We do this only 602 * for curses, since many termcap applications assume that 603 * smso/rmso and smul/rmul are paired, and will not function 604 * properly if we remove rmso or rmul. Curses applications 605 * shouldn't be looking at this detail. 606 */ 607 sp->_use_rmso = SGR0_TEST(exit_standout_mode); 608 sp->_use_rmul = SGR0_TEST(exit_underline_mode); 609 610 /* 611 * Check whether we can optimize scrolling under dumb terminals in 612 * case we do not have any of these capabilities, scrolling 613 * optimization will be useless. 614 */ 615 sp->_scrolling = ((scroll_forward && scroll_reverse) || 616 ((parm_rindex || 617 parm_insert_line || 618 insert_line) && 619 (parm_index || 620 parm_delete_line || 621 delete_line))); 622 623 NCURSES_SP_NAME(baudrate) (sp); 624 625 NCURSES_SP_NAME(_nc_mvcur_init) (sp); 626 /* initialize terminal to a sane state */ 627 NCURSES_SP_NAME(_nc_screen_init) (sp); 628 } 629 630 static void 631 drv_init(TERMINAL_CONTROL_BLOCK * TCB) 632 { 633 TERMINAL *trm; 634 635 AssertTCB(); 636 637 trm = (TERMINAL *) TCB; 638 639 TCB->info.initcolor = VALID_STRING(initialize_color); 640 TCB->info.canchange = can_change; 641 TCB->info.hascolor = ((VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs) 642 && (((set_foreground != NULL) 643 && (set_background != NULL)) 644 || ((set_a_foreground != NULL) 645 && (set_a_background != NULL)) 646 || set_color_pair)) ? TRUE : FALSE); 647 648 TCB->info.caninit = !(exit_ca_mode && non_rev_rmcup); 649 650 TCB->info.maxpairs = VALID_NUMERIC(max_pairs) ? max_pairs : 0; 651 TCB->info.maxcolors = VALID_NUMERIC(max_colors) ? max_colors : 0; 652 TCB->info.numlabels = VALID_NUMERIC(num_labels) ? num_labels : 0; 653 TCB->info.labelwidth = VALID_NUMERIC(label_width) ? label_width : 0; 654 TCB->info.labelheight = VALID_NUMERIC(label_height) ? label_height : 0; 655 TCB->info.nocolorvideo = VALID_NUMERIC(no_color_video) ? no_color_video 656 : 0; 657 TCB->info.tabsize = VALID_NUMERIC(init_tabs) ? (int) init_tabs : 8; 658 659 TCB->info.defaultPalette = hue_lightness_saturation ? _nc_hls_palette : _nc_cga_palette; 660 661 /* 662 * If an application calls setupterm() rather than initscr() or 663 * newterm(), we will not have the def_prog_mode() call in 664 * _nc_setupscreen(). Do it now anyway, so we can initialize the 665 * baudrate. 666 */ 667 if (isatty(trm->Filedes)) { 668 TCB->drv->mode(TCB, TRUE, TRUE); 669 } 670 } 671 672 #define MAX_PALETTE 8 673 #define InPalette(n) ((n) >= 0 && (n) < MAX_PALETTE) 674 675 static void 676 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB, int pair, int f, int b) 677 { 678 SCREEN *sp; 679 680 AssertTCB(); 681 SetSP(); 682 683 if ((initialize_pair != NULL) && InPalette(f) && InPalette(b)) { 684 const color_t *tp = InfoOf(sp).defaultPalette; 685 686 TR(TRACE_ATTRS, 687 ("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)", 688 pair, 689 tp[f].red, tp[f].green, tp[f].blue, 690 tp[b].red, tp[b].green, tp[b].blue)); 691 692 NCURSES_PUTP2("initialize_pair", 693 TPARM_7(initialize_pair, 694 pair, 695 tp[f].red, tp[f].green, tp[f].blue, 696 tp[b].red, tp[b].green, tp[b].blue)); 697 } 698 } 699 700 static int 701 default_fg(SCREEN *sp) 702 { 703 #if NCURSES_EXT_FUNCS 704 return (sp != 0) ? sp->_default_fg : COLOR_WHITE; 705 #else 706 return COLOR_WHITE; 707 #endif 708 } 709 710 static int 711 default_bg(SCREEN *sp) 712 { 713 #if NCURSES_EXT_FUNCS 714 return sp != 0 ? sp->_default_bg : COLOR_BLACK; 715 #else 716 return COLOR_BLACK; 717 #endif 718 } 719 720 static void 721 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB, 722 int color, int r, int g, int b) 723 { 724 SCREEN *sp = TCB->csp; 725 726 AssertTCB(); 727 if (initialize_color != NULL) { 728 NCURSES_PUTP2("initialize_color", 729 TPARM_4(initialize_color, color, r, g, b)); 730 } 731 } 732 733 static void 734 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB, 735 int old_pair, 736 int pair, 737 int reverse, 738 NCURSES_SP_OUTC outc) 739 { 740 SCREEN *sp = TCB->csp; 741 NCURSES_COLOR_T fg = COLOR_DEFAULT; 742 NCURSES_COLOR_T bg = COLOR_DEFAULT; 743 NCURSES_COLOR_T old_fg, old_bg; 744 745 AssertTCB(); 746 if (sp == 0) 747 return; 748 749 if (pair < 0 || pair >= COLOR_PAIRS) { 750 return; 751 } else if (pair != 0) { 752 if (set_color_pair) { 753 TPUTS_TRACE("set_color_pair"); 754 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx 755 TPARM_1(set_color_pair, pair), 1, outc); 756 return; 757 } else if (sp != 0) { 758 NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx 759 (short) pair, 760 &fg, 761 &bg); 762 } 763 } 764 765 if (old_pair >= 0 766 && sp != 0 767 && NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx 768 (short) old_pair, 769 &old_fg, 770 &old_bg) !=ERR) { 771 if ((isDefaultColor(fg) && !isDefaultColor(old_fg)) 772 || (isDefaultColor(bg) && !isDefaultColor(old_bg))) { 773 #if NCURSES_EXT_FUNCS 774 /* 775 * A minor optimization - but extension. If "AX" is specified in 776 * the terminal description, treat it as screen's indicator of ECMA 777 * SGR 39 and SGR 49, and assume the two sequences are independent. 778 */ 779 if (sp->_has_sgr_39_49 780 && isDefaultColor(old_bg) 781 && !isDefaultColor(old_fg)) { 782 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[39m", 1, outc); 783 } else if (sp->_has_sgr_39_49 784 && isDefaultColor(old_fg) 785 && !isDefaultColor(old_bg)) { 786 NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[49m", 1, outc); 787 } else 788 #endif 789 drv_rescol(TCB); 790 } 791 } else { 792 drv_rescol(TCB); 793 if (old_pair < 0) 794 return; 795 } 796 797 #if NCURSES_EXT_FUNCS 798 if (isDefaultColor(fg)) 799 fg = (NCURSES_COLOR_T) default_fg(sp); 800 if (isDefaultColor(bg)) 801 bg = (NCURSES_COLOR_T) default_bg(sp); 802 #endif 803 804 if (reverse) { 805 NCURSES_COLOR_T xx = fg; 806 fg = bg; 807 bg = xx; 808 } 809 810 TR(TRACE_ATTRS, ("setting colors: pair = %d, fg = %d, bg = %d", pair, 811 fg, bg)); 812 813 if (!isDefaultColor(fg)) { 814 drv_setcolor(TCB, TRUE, fg, outc); 815 } 816 if (!isDefaultColor(bg)) { 817 drv_setcolor(TCB, FALSE, bg, outc); 818 } 819 } 820 821 #define xterm_kmous "\033[M" 822 static void 823 init_xterm_mouse(SCREEN *sp) 824 { 825 sp->_mouse_type = M_XTERM; 826 sp->_mouse_xtermcap = NCURSES_SP_NAME(tigetstr) (NCURSES_SP_ARGx "XM"); 827 if (!VALID_STRING(sp->_mouse_xtermcap)) 828 sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;"; 829 } 830 831 static void 832 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB) 833 { 834 SCREEN *sp; 835 836 AssertTCB(); 837 SetSP(); 838 839 /* we know how to recognize mouse events under "xterm" */ 840 if (sp != 0) { 841 if (key_mouse != 0) { 842 if (!strcmp(key_mouse, xterm_kmous) 843 || strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) { 844 init_xterm_mouse(sp); 845 } 846 } else if (strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) { 847 if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK) 848 init_xterm_mouse(sp); 849 } 850 } 851 } 852 853 static int 854 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB, 855 int delay 856 EVENTLIST_2nd(_nc_eventlist * evl)) 857 { 858 int rc = 0; 859 SCREEN *sp; 860 861 AssertTCB(); 862 SetSP(); 863 864 #if USE_SYSMOUSE 865 if ((sp->_mouse_type == M_SYSMOUSE) 866 && (sp->_sysmouse_head < sp->_sysmouse_tail)) { 867 rc = TW_MOUSE; 868 } else 869 #endif 870 { 871 rc = TCBOf(sp)->drv->twait(TCBOf(sp), 872 TWAIT_MASK, 873 delay, 874 (int *) 0 875 EVENTLIST_2nd(evl)); 876 #if USE_SYSMOUSE 877 if ((sp->_mouse_type == M_SYSMOUSE) 878 && (sp->_sysmouse_head < sp->_sysmouse_tail) 879 && (rc == 0) 880 && (errno == EINTR)) { 881 rc |= TW_MOUSE; 882 } 883 #endif 884 } 885 return rc; 886 } 887 888 static int 889 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB, int yold, int xold, int ynew, int xnew) 890 { 891 SCREEN *sp = TCB->csp; 892 AssertTCB(); 893 return NCURSES_SP_NAME(_nc_mvcur) (sp, yold, xold, ynew, xnew); 894 } 895 896 static void 897 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB, int labnum, char *text) 898 { 899 SCREEN *sp = TCB->csp; 900 901 AssertTCB(); 902 if (labnum > 0 && labnum <= num_labels) { 903 NCURSES_PUTP2("plab_norm", 904 TPARM_2(plab_norm, labnum, text)); 905 } 906 } 907 908 static void 909 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB, int OnFlag) 910 { 911 SCREEN *sp = TCB->csp; 912 913 AssertTCB(); 914 if (OnFlag) { 915 NCURSES_PUTP2("label_on", label_on); 916 } else { 917 NCURSES_PUTP2("label_off", label_off); 918 } 919 } 920 921 static chtype 922 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB) 923 { 924 SCREEN *sp = TCB->csp; 925 chtype attrs = A_NORMAL; 926 927 AssertTCB(); 928 if (enter_alt_charset_mode) 929 attrs |= A_ALTCHARSET; 930 931 if (enter_blink_mode) 932 attrs |= A_BLINK; 933 934 if (enter_bold_mode) 935 attrs |= A_BOLD; 936 937 if (enter_dim_mode) 938 attrs |= A_DIM; 939 940 if (enter_reverse_mode) 941 attrs |= A_REVERSE; 942 943 if (enter_standout_mode) 944 attrs |= A_STANDOUT; 945 946 if (enter_protected_mode) 947 attrs |= A_PROTECT; 948 949 if (enter_secure_mode) 950 attrs |= A_INVIS; 951 952 if (enter_underline_mode) 953 attrs |= A_UNDERLINE; 954 955 if (sp && sp->_coloron) 956 attrs |= A_COLOR; 957 958 #if USE_ITALIC 959 if (enter_italics_mode) 960 attrs |= A_ITALIC; 961 #endif 962 963 return (attrs); 964 } 965 966 static void 967 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB) 968 { 969 AssertTCB(); 970 971 clear_screen = 0; 972 cursor_down = parm_down_cursor = 0; 973 cursor_address = 0; 974 cursor_up = parm_up_cursor = 0; 975 row_address = 0; 976 cursor_home = carriage_return; 977 } 978 979 static void 980 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB, chtype *real_map, chtype *fake_map) 981 { 982 SCREEN *sp = TCB->csp; 983 984 AssertTCB(); 985 assert(sp != 0); 986 if (ena_acs != NULL) { 987 NCURSES_PUTP2("ena_acs", ena_acs); 988 } 989 #if NCURSES_EXT_FUNCS 990 /* 991 * Linux console "supports" the "PC ROM" character set by the coincidence 992 * that smpch/rmpch and smacs/rmacs have the same values. ncurses has 993 * no codepage support (see SCO Merge for an example). Outside of the 994 * values defined in acsc, there are no definitions for the "PC ROM" 995 * character set (assumed by some applications to be codepage 437), but we 996 * allow those applications to use those codepoints. 997 * 998 * test/blue.c uses this feature. 999 */ 1000 #define PCH_KLUDGE(a,b) (a != 0 && b != 0 && !strcmp(a,b)) 1001 if (PCH_KLUDGE(enter_pc_charset_mode, enter_alt_charset_mode) && 1002 PCH_KLUDGE(exit_pc_charset_mode, exit_alt_charset_mode)) { 1003 size_t i; 1004 for (i = 1; i < ACS_LEN; ++i) { 1005 if (real_map[i] == 0) { 1006 real_map[i] = i; 1007 if (real_map != fake_map) { 1008 if (sp != 0) 1009 sp->_screen_acs_map[i] = TRUE; 1010 } 1011 } 1012 } 1013 } 1014 #endif 1015 1016 if (acs_chars != NULL) { 1017 size_t i = 0; 1018 size_t length = strlen(acs_chars); 1019 1020 while (i + 1 < length) { 1021 if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) { 1022 real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET; 1023 if (sp != 0) 1024 sp->_screen_acs_map[UChar(acs_chars[i])] = TRUE; 1025 } 1026 i += 2; 1027 } 1028 } 1029 #ifdef TRACE 1030 /* Show the equivalent mapping, noting if it does not match the 1031 * given attribute, whether by re-ordering or duplication. 1032 */ 1033 if (USE_TRACEF(TRACE_CALLS)) { 1034 size_t n, m; 1035 char show[ACS_LEN * 2 + 1]; 1036 for (n = 1, m = 0; n < ACS_LEN; n++) { 1037 if (real_map[n] != 0) { 1038 show[m++] = (char) n; 1039 show[m++] = (char) ChCharOf(real_map[n]); 1040 } 1041 } 1042 show[m] = 0; 1043 if (acs_chars == NULL || strcmp(acs_chars, show)) 1044 _tracef("%s acs_chars %s", 1045 (acs_chars == NULL) ? "NULL" : "READ", 1046 _nc_visbuf(acs_chars)); 1047 _tracef("%s acs_chars %s", 1048 (acs_chars == NULL) 1049 ? "NULL" 1050 : (strcmp(acs_chars, show) 1051 ? "DIFF" 1052 : "SAME"), 1053 _nc_visbuf(show)); 1054 1055 _nc_unlock_global(tracef); 1056 } 1057 #endif /* TRACE */ 1058 } 1059 1060 #define ENSURE_TINFO(sp) (TCBOf(sp)->drv->isTerminfo) 1061 1062 NCURSES_EXPORT(void) 1063 _nc_cookie_init(SCREEN *sp) 1064 { 1065 bool support_cookies = USE_XMC_SUPPORT; 1066 TERMINAL_CONTROL_BLOCK *TCB = (TERMINAL_CONTROL_BLOCK *) (sp->_term); 1067 1068 if (sp == 0 || !ENSURE_TINFO(sp)) 1069 return; 1070 1071 #if USE_XMC_SUPPORT 1072 /* 1073 * If we have no magic-cookie support compiled-in, or if it is suppressed 1074 * in the environment, reset the support-flag. 1075 */ 1076 if (magic_cookie_glitch >= 0) { 1077 if (getenv("NCURSES_NO_MAGIC_COOKIE") != 0) { 1078 support_cookies = FALSE; 1079 } 1080 } 1081 #endif 1082 1083 if (!support_cookies && magic_cookie_glitch >= 0) { 1084 T(("will disable attributes to work w/o magic cookies")); 1085 } 1086 1087 if (magic_cookie_glitch > 0) { /* tvi, wyse */ 1088 1089 sp->_xmc_triggers = sp->_ok_attributes & XMC_CONFLICT; 1090 #if 0 1091 /* 1092 * We "should" treat colors as an attribute. The wyse350 (and its 1093 * clones) appear to be the only ones that have both colors and magic 1094 * cookies. 1095 */ 1096 if (has_colors()) { 1097 sp->_xmc_triggers |= A_COLOR; 1098 } 1099 #endif 1100 sp->_xmc_suppress = sp->_xmc_triggers & (chtype) ~(A_BOLD); 1101 1102 T(("magic cookie attributes %s", _traceattr(sp->_xmc_suppress))); 1103 /* 1104 * Supporting line-drawing may be possible. But make the regular 1105 * video attributes work first. 1106 */ 1107 acs_chars = ABSENT_STRING; 1108 ena_acs = ABSENT_STRING; 1109 enter_alt_charset_mode = ABSENT_STRING; 1110 exit_alt_charset_mode = ABSENT_STRING; 1111 #if USE_XMC_SUPPORT 1112 /* 1113 * To keep the cookie support simple, suppress all of the optimization 1114 * hooks except for clear_screen and the cursor addressing. 1115 */ 1116 if (support_cookies) { 1117 clr_eol = ABSENT_STRING; 1118 clr_eos = ABSENT_STRING; 1119 set_attributes = ABSENT_STRING; 1120 } 1121 #endif 1122 } else if (magic_cookie_glitch == 0) { /* hpterm */ 1123 } 1124 1125 /* 1126 * If magic cookies are not supported, cancel the strings that set 1127 * video attributes. 1128 */ 1129 if (!support_cookies && magic_cookie_glitch >= 0) { 1130 magic_cookie_glitch = ABSENT_NUMERIC; 1131 set_attributes = ABSENT_STRING; 1132 enter_blink_mode = ABSENT_STRING; 1133 enter_bold_mode = ABSENT_STRING; 1134 enter_dim_mode = ABSENT_STRING; 1135 enter_reverse_mode = ABSENT_STRING; 1136 enter_standout_mode = ABSENT_STRING; 1137 enter_underline_mode = ABSENT_STRING; 1138 } 1139 1140 /* initialize normal acs before wide, since we use mapping in the latter */ 1141 #if !USE_WIDEC_SUPPORT 1142 if (_nc_unicode_locale() && _nc_locale_breaks_acs(sp->_term)) { 1143 acs_chars = NULL; 1144 ena_acs = NULL; 1145 enter_alt_charset_mode = NULL; 1146 exit_alt_charset_mode = NULL; 1147 set_attributes = NULL; 1148 } 1149 #endif 1150 } 1151 1152 static int 1153 drv_twait(TERMINAL_CONTROL_BLOCK * TCB, 1154 int mode, 1155 int milliseconds, 1156 int *timeleft 1157 EVENTLIST_2nd(_nc_eventlist * evl)) 1158 { 1159 SCREEN *sp; 1160 1161 AssertTCB(); 1162 SetSP(); 1163 1164 return _nc_timed_wait(sp, mode, milliseconds, timeleft EVENTLIST_2nd(evl)); 1165 } 1166 1167 static int 1168 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf) 1169 { 1170 SCREEN *sp; 1171 unsigned char c2 = 0; 1172 int n; 1173 1174 AssertTCB(); 1175 assert(buf); 1176 SetSP(); 1177 1178 # if USE_PTHREADS_EINTR 1179 if ((pthread_self) && (pthread_kill) && (pthread_equal)) 1180 _nc_globals.read_thread = pthread_self(); 1181 # endif 1182 n = read(sp->_ifd, &c2, (size_t) 1); 1183 #if USE_PTHREADS_EINTR 1184 _nc_globals.read_thread = 0; 1185 #endif 1186 *buf = (int) c2; 1187 return n; 1188 } 1189 1190 static int 1191 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms) 1192 { 1193 #if HAVE_NANOSLEEP 1194 { 1195 struct timespec request, remaining; 1196 request.tv_sec = ms / 1000; 1197 request.tv_nsec = (ms % 1000) * 1000000; 1198 while (nanosleep(&request, &remaining) == -1 1199 && errno == EINTR) { 1200 request = remaining; 1201 } 1202 } 1203 #else 1204 _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0)); 1205 #endif 1206 return OK; 1207 } 1208 1209 static int 1210 __nc_putp(SCREEN *sp, const char *name GCC_UNUSED, const char *value) 1211 { 1212 int rc = ERR; 1213 1214 if (value) { 1215 rc = NCURSES_PUTP2(name, value); 1216 } 1217 return rc; 1218 } 1219 1220 static int 1221 __nc_putp_flush(SCREEN *sp, const char *name, const char *value) 1222 { 1223 int rc = __nc_putp(sp, name, value); 1224 if (rc != ERR) { 1225 NCURSES_SP_NAME(_nc_flush) (sp); 1226 } 1227 return rc; 1228 } 1229 1230 static int 1231 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag) 1232 { 1233 int ret = ERR; 1234 SCREEN *sp; 1235 1236 AssertTCB(); 1237 1238 sp = TCB->csp; 1239 1240 if (sp) { 1241 if (flag) { 1242 (void) __nc_putp_flush(sp, "keypad_xmit", keypad_xmit); 1243 } else if (!flag && keypad_local) { 1244 (void) __nc_putp_flush(sp, "keypad_local", keypad_local); 1245 } 1246 if (flag && !sp->_tried) { 1247 _nc_init_keytry(sp); 1248 sp->_tried = TRUE; 1249 } 1250 ret = OK; 1251 } 1252 1253 return ret; 1254 } 1255 1256 static int 1257 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int c, int flag) 1258 { 1259 SCREEN *sp; 1260 int code = ERR; 1261 int count = 0; 1262 char *s; 1263 1264 AssertTCB(); 1265 SetSP(); 1266 1267 if (c >= 0) { 1268 unsigned ch = (unsigned) c; 1269 if (flag) { 1270 while ((s = _nc_expand_try(sp->_key_ok, 1271 ch, &count, (size_t) 0)) != 0 1272 && _nc_remove_key(&(sp->_key_ok), ch)) { 1273 code = _nc_add_to_try(&(sp->_keytry), s, ch); 1274 free(s); 1275 count = 0; 1276 if (code != OK) 1277 break; 1278 } 1279 } else { 1280 while ((s = _nc_expand_try(sp->_keytry, 1281 ch, &count, (size_t) 0)) != 0 1282 && _nc_remove_key(&(sp->_keytry), ch)) { 1283 code = _nc_add_to_try(&(sp->_key_ok), s, ch); 1284 free(s); 1285 count = 0; 1286 if (code != OK) 1287 break; 1288 } 1289 } 1290 } 1291 return (code); 1292 } 1293 1294 static bool 1295 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int key) 1296 { 1297 bool res = FALSE; 1298 1299 AssertTCB(); 1300 if (TCB->csp) 1301 res = TINFO_HAS_KEY(TCB->csp, key) == 0 ? FALSE : TRUE; 1302 1303 return res; 1304 } 1305 1306 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_TINFO_DRIVER = { 1307 TRUE, 1308 drv_CanHandle, /* CanHandle */ 1309 drv_init, /* init */ 1310 drv_release, /* release */ 1311 drv_size, /* size */ 1312 drv_sgmode, /* sgmode */ 1313 drv_conattr, /* conattr */ 1314 drv_mvcur, /* hwcur */ 1315 drv_mode, /* mode */ 1316 drv_rescol, /* rescol */ 1317 drv_rescolors, /* rescolors */ 1318 drv_setcolor, /* color */ 1319 drv_dobeepflash, /* doBeepOrFlash */ 1320 drv_initpair, /* initpair */ 1321 drv_initcolor, /* initcolor */ 1322 drv_do_color, /* docolor */ 1323 drv_initmouse, /* initmouse */ 1324 drv_testmouse, /* testmouse */ 1325 drv_setfilter, /* setfilter */ 1326 drv_hwlabel, /* hwlabel */ 1327 drv_hwlabelOnOff, /* hwlabelOnOff */ 1328 drv_doupdate, /* update */ 1329 drv_defaultcolors, /* defaultcolors */ 1330 drv_print, /* print */ 1331 drv_getsize, /* getsize */ 1332 drv_setsize, /* setsize */ 1333 drv_initacs, /* initacs */ 1334 drv_screen_init, /* scinit */ 1335 drv_wrap, /* scexit */ 1336 drv_twait, /* twait */ 1337 drv_read, /* read */ 1338 drv_nap, /* nap */ 1339 drv_kpad, /* kpad */ 1340 drv_keyok, /* kyOk */ 1341 drv_kyExist /* kyExist */ 1342 }; 1343