1 /* $NetBSD: color.c,v 1.29 2004/03/22 18:57:38 jdc Exp $ */ 2 3 /* 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julian Coleman. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __RCSID("$NetBSD: color.c,v 1.29 2004/03/22 18:57:38 jdc Exp $"); 42 #endif /* not lint */ 43 44 #include "curses.h" 45 #include "curses_private.h" 46 47 /* Have we initialised colours? */ 48 int __using_color = 0; 49 50 /* Default colour number */ 51 attr_t __default_color = 0; 52 53 /* Default colour pair values - white on black. */ 54 struct __pair __default_pair = {COLOR_WHITE, COLOR_BLACK, 0}; 55 56 /* Default colour values */ 57 /* Flags for colours and pairs */ 58 #define __USED 0x01 59 60 static void 61 __change_pair(short); 62 63 /* 64 * has_colors -- 65 * Check if terminal has colours. 66 */ 67 bool 68 has_colors(void) 69 { 70 if (__tc_Co > 0 && __tc_pa > 0 && ((__tc_AF != NULL && 71 __tc_AB != NULL) || __tc_Ip != NULL || __tc_Ic != NULL || 72 (__tc_Sb != NULL && __tc_Sf != NULL))) 73 return(TRUE); 74 else 75 return(FALSE); 76 } 77 78 /* 79 * can_change_color -- 80 * Check if terminal can change colours. 81 */ 82 bool 83 can_change_color(void) 84 { 85 if (__tc_cc) 86 return(TRUE); 87 else 88 return(FALSE); 89 } 90 91 /* 92 * start_color -- 93 * Initialise colour support. 94 */ 95 int 96 start_color(void) 97 { 98 int i; 99 attr_t temp_nc; 100 struct __winlist *wlp; 101 WINDOW *win; 102 int y, x; 103 104 if (has_colors() == FALSE) 105 return(ERR); 106 107 /* Max colours and colour pairs */ 108 if (__tc_Co == -1) 109 COLORS = 0; 110 else { 111 COLORS = __tc_Co > MAX_COLORS ? MAX_COLORS : __tc_Co; 112 if (__tc_pa == -1) { 113 COLOR_PAIRS = 0; 114 COLORS = 0; 115 } else { 116 COLOR_PAIRS = (__tc_pa > MAX_PAIRS - 1 ? 117 MAX_PAIRS - 1 : __tc_pa); 118 /* Use the last colour pair for curses default. */ 119 __default_color = COLOR_PAIR(MAX_PAIRS - 1); 120 } 121 } 122 if (!COLORS) 123 return (ERR); 124 125 _cursesi_screen->COLORS = COLORS; 126 _cursesi_screen->COLOR_PAIRS = COLOR_PAIRS; 127 128 /* Reset terminal colour and colour pairs. */ 129 if (__tc_oc != NULL) 130 tputs(__tc_oc, 0, __cputchar); 131 if (__tc_op != NULL) { 132 tputs(__tc_op, 0, __cputchar); 133 curscr->wattr &= _cursesi_screen->mask_op; 134 } 135 136 /* Type of colour manipulation - ANSI/TEK/HP/other */ 137 if (__tc_AF != NULL && __tc_AB != NULL) 138 _cursesi_screen->color_type = COLOR_ANSI; 139 else if (__tc_Ip != NULL) 140 _cursesi_screen->color_type = COLOR_HP; 141 else if (__tc_Ic != NULL) 142 _cursesi_screen->color_type = COLOR_TEK; 143 else if (__tc_Sb != NULL && __tc_Sf != NULL) 144 _cursesi_screen->color_type = COLOR_OTHER; 145 else 146 return(ERR); /* Unsupported colour method */ 147 148 #ifdef DEBUG 149 __CTRACE("start_color: COLORS = %d, COLOR_PAIRS = %d", 150 COLORS, COLOR_PAIRS); 151 switch (_cursesi_screen->color_type) { 152 case COLOR_ANSI: 153 __CTRACE(" (ANSI style)\n"); 154 break; 155 case COLOR_HP: 156 __CTRACE(" (HP style)\n"); 157 break; 158 case COLOR_TEK: 159 __CTRACE(" (Tektronics style)\n"); 160 break; 161 case COLOR_OTHER: 162 __CTRACE(" (Other style)\n"); 163 break; 164 } 165 #endif 166 167 /* 168 * Attributes that cannot be used with color. 169 * Store these in an attr_t for wattrset()/wattron(). 170 */ 171 _cursesi_screen->nca = __NORMAL; 172 if (__tc_NC != -1) { 173 temp_nc = (attr_t) t_getnum(_cursesi_screen->cursesi_genbuf, "NC"); 174 if (temp_nc & 0x0001) 175 _cursesi_screen->nca |= __STANDOUT; 176 if (temp_nc & 0x0002) 177 _cursesi_screen->nca |= __UNDERSCORE; 178 if (temp_nc & 0x0004) 179 _cursesi_screen->nca |= __REVERSE; 180 if (temp_nc & 0x0008) 181 _cursesi_screen->nca |= __BLINK; 182 if (temp_nc & 0x0010) 183 _cursesi_screen->nca |= __DIM; 184 if (temp_nc & 0x0020) 185 _cursesi_screen->nca |= __BOLD; 186 if (temp_nc & 0x0040) 187 _cursesi_screen->nca |= __BLANK; 188 if (temp_nc & 0x0080) 189 _cursesi_screen->nca |= __PROTECT; 190 if (temp_nc & 0x0100) 191 _cursesi_screen->nca |= __ALTCHARSET; 192 } 193 #ifdef DEBUG 194 __CTRACE ("start_color: _cursesi_screen->nca = %08x\n", 195 _cursesi_screen->nca); 196 #endif 197 198 /* Set up initial 8 colours */ 199 if (COLORS >= COLOR_BLACK) 200 (void) init_color(COLOR_BLACK, 0, 0, 0); 201 if (COLORS >= COLOR_RED) 202 (void) init_color(COLOR_RED, 1000, 0, 0); 203 if (COLORS >= COLOR_GREEN) 204 (void) init_color(COLOR_GREEN, 0, 1000, 0); 205 if (COLORS >= COLOR_YELLOW) 206 (void) init_color(COLOR_YELLOW, 1000, 1000, 0); 207 if (COLORS >= COLOR_BLUE) 208 (void) init_color(COLOR_BLUE, 0, 0, 1000); 209 if (COLORS >= COLOR_MAGENTA) 210 (void) init_color(COLOR_MAGENTA, 1000, 0, 1000); 211 if (COLORS >= COLOR_CYAN) 212 (void) init_color(COLOR_CYAN, 0, 1000, 1000); 213 if (COLORS >= COLOR_WHITE) 214 (void) init_color(COLOR_WHITE, 1000, 1000, 1000); 215 216 /* Initialise other colours */ 217 for (i = 8; i < COLORS; i++) { 218 _cursesi_screen->colours[i].red = 0; 219 _cursesi_screen->colours[i].green = 0; 220 _cursesi_screen->colours[i].blue = 0; 221 _cursesi_screen->colours[i].flags = 0; 222 } 223 224 /* Initialise pair 0 to default colours. */ 225 _cursesi_screen->colour_pairs[0].fore = -1; 226 _cursesi_screen->colour_pairs[0].back = -1; 227 _cursesi_screen->colour_pairs[0].flags = 0; 228 229 /* Initialise user colour pairs to default (white on black) */ 230 for (i = 0; i < COLOR_PAIRS; i++) { 231 _cursesi_screen->colour_pairs[i].fore = COLOR_WHITE; 232 _cursesi_screen->colour_pairs[i].back = COLOR_BLACK; 233 _cursesi_screen->colour_pairs[i].flags = 0; 234 } 235 236 /* Initialise default colour pair. */ 237 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = 238 __default_pair.fore; 239 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = 240 __default_pair.back; 241 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = 242 __default_pair.flags; 243 244 __using_color = 1; 245 246 /* Set all positions on all windows to curses default colours. */ 247 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) { 248 win = wlp->winp; 249 if (wlp->winp == curscr) { 250 /* Reset colour attribute on curscr */ 251 for (y = 0; y < curscr->maxy; y++) 252 for (x = 0; x < curscr->maxx; x++) { 253 if ((curscr->lines[y]->line[x].battr & __COLOR) == __default_color) 254 curscr->lines[y]->line[x].battr &= ~__COLOR; 255 } 256 } else if (wlp->winp != __virtscr) { 257 /* Set background attribute on other windows */ 258 if (!(win->battr & __COLOR)) 259 win->battr |= __default_color; 260 for (y = 0; y < win->maxy; y++) { 261 for (x = 0; x < win->maxx; x++) 262 if (!(win->lines[y]->line[x].battr & __COLOR)) 263 win->lines[y]->line[x].battr |= __default_color; 264 } 265 __touchwin(win); 266 } 267 } 268 269 return(OK); 270 } 271 272 /* 273 * init_pair -- 274 * Set pair foreground and background colors. 275 * Our default colour ordering is ANSI - 1 = red, 4 = blue, 3 = yellow, 276 * 6 = cyan. The older style (Sb/Sf) uses 1 = blue, 4 = red, 3 = cyan, 277 * 6 = yellow, so we swap them here and in pair_content(). 278 */ 279 int 280 init_pair(short pair, short fore, short back) 281 { 282 int changed; 283 284 #ifdef DEBUG 285 __CTRACE("init_pair: %d, %d, %d\n", pair, fore, back); 286 #endif 287 288 if (pair < 0 || pair >= COLOR_PAIRS) 289 return (ERR); 290 if (fore >= COLORS) 291 return (ERR); 292 if (back >= COLORS) 293 return (ERR); 294 295 /* Swap red/blue and yellow/cyan */ 296 if (_cursesi_screen->color_type == COLOR_OTHER) { 297 switch (fore) { 298 case COLOR_RED: 299 fore = COLOR_BLUE; 300 break; 301 case COLOR_BLUE: 302 fore = COLOR_RED; 303 break; 304 case COLOR_YELLOW: 305 fore = COLOR_CYAN; 306 break; 307 case COLOR_CYAN: 308 fore = COLOR_YELLOW; 309 break; 310 } 311 switch (back) { 312 case COLOR_RED: 313 back = COLOR_BLUE; 314 break; 315 case COLOR_BLUE: 316 back = COLOR_RED; 317 break; 318 case COLOR_YELLOW: 319 back = COLOR_CYAN; 320 break; 321 case COLOR_CYAN: 322 back = COLOR_YELLOW; 323 break; 324 } 325 } 326 327 if ((_cursesi_screen->colour_pairs[pair].flags & __USED) && 328 (fore != _cursesi_screen->colour_pairs[pair].fore || 329 back != _cursesi_screen->colour_pairs[pair].back)) 330 changed = 1; 331 else 332 changed = 0; 333 334 _cursesi_screen->colour_pairs[pair].flags |= __USED; 335 _cursesi_screen->colour_pairs[pair].fore = fore; 336 _cursesi_screen->colour_pairs[pair].back = back; 337 338 /* XXX: need to initialise HP style (Ip) */ 339 340 if (changed) 341 __change_pair(pair); 342 return (OK); 343 } 344 345 /* 346 * pair_content -- 347 * Get pair foreground and background colours. 348 */ 349 int 350 pair_content(short pair, short *forep, short *backp) 351 { 352 if (pair < 0 || pair > _cursesi_screen->COLOR_PAIRS) 353 return(ERR); 354 355 *forep = _cursesi_screen->colour_pairs[pair].fore; 356 *backp = _cursesi_screen->colour_pairs[pair].back; 357 358 /* Swap red/blue and yellow/cyan */ 359 if (_cursesi_screen->color_type == COLOR_OTHER) { 360 switch (*forep) { 361 case COLOR_RED: 362 *forep = COLOR_BLUE; 363 break; 364 case COLOR_BLUE: 365 *forep = COLOR_RED; 366 break; 367 case COLOR_YELLOW: 368 *forep = COLOR_CYAN; 369 break; 370 case COLOR_CYAN: 371 *forep = COLOR_YELLOW; 372 break; 373 } 374 switch (*backp) { 375 case COLOR_RED: 376 *backp = COLOR_BLUE; 377 break; 378 case COLOR_BLUE: 379 *backp = COLOR_RED; 380 break; 381 case COLOR_YELLOW: 382 *backp = COLOR_CYAN; 383 break; 384 case COLOR_CYAN: 385 *backp = COLOR_YELLOW; 386 break; 387 } 388 } 389 return(OK); 390 } 391 392 /* 393 * init_color -- 394 * Set colour red, green and blue values. 395 */ 396 int 397 init_color(short color, short red, short green, short blue) 398 { 399 #ifdef DEBUG 400 __CTRACE("init_color: %d, %d, %d, %d\n", color, red, green, blue); 401 #endif 402 if (color < 0 || color >= _cursesi_screen->COLORS) 403 return(ERR); 404 405 _cursesi_screen->colours[color].red = red; 406 _cursesi_screen->colours[color].green = green; 407 _cursesi_screen->colours[color].blue = blue; 408 /* XXX Not yet implemented */ 409 return(ERR); 410 /* XXX: need to initialise Tek style (Ic) and support HLS */ 411 } 412 413 /* 414 * color_content -- 415 * Get colour red, green and blue values. 416 */ 417 int 418 color_content(short color, short *redp, short *greenp, short *bluep) 419 { 420 if (color < 0 || color >= _cursesi_screen->COLORS) 421 return(ERR); 422 423 *redp = _cursesi_screen->colours[color].red; 424 *greenp = _cursesi_screen->colours[color].green; 425 *bluep = _cursesi_screen->colours[color].blue; 426 return(OK); 427 } 428 429 /* 430 * use_default_colors -- 431 * Use terminal default colours instead of curses default colour. 432 */ 433 int 434 use_default_colors() 435 { 436 #ifdef DEBUG 437 __CTRACE("use_default_colors\n"); 438 #endif 439 440 return(assume_default_colors(-1, -1)); 441 } 442 443 /* 444 * assume_default_colors -- 445 * Set the default foreground and background colours. 446 */ 447 int 448 assume_default_colors(short fore, short back) 449 { 450 #ifdef DEBUG 451 __CTRACE("assume_default_colors: %d, %d\n", fore, back); 452 #endif 453 /* Swap red/blue and yellow/cyan */ 454 if (_cursesi_screen->color_type == COLOR_OTHER) { 455 switch (fore) { 456 case COLOR_RED: 457 fore = COLOR_BLUE; 458 break; 459 case COLOR_BLUE: 460 fore = COLOR_RED; 461 break; 462 case COLOR_YELLOW: 463 fore = COLOR_CYAN; 464 break; 465 case COLOR_CYAN: 466 fore = COLOR_YELLOW; 467 break; 468 } 469 switch (back) { 470 case COLOR_RED: 471 back = COLOR_BLUE; 472 break; 473 case COLOR_BLUE: 474 back = COLOR_RED; 475 break; 476 case COLOR_YELLOW: 477 back = COLOR_CYAN; 478 break; 479 case COLOR_CYAN: 480 back = COLOR_YELLOW; 481 break; 482 } 483 } 484 __default_pair.fore = fore; 485 __default_pair.back = back; 486 __default_pair.flags = __USED; 487 488 if (COLOR_PAIRS) { 489 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore; 490 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back; 491 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED; 492 } 493 494 /* 495 * If we've already called start_color(), make sure all instances 496 * of the curses default colour pair are dirty. 497 */ 498 if (__using_color) 499 __change_pair(PAIR_NUMBER(__default_color)); 500 501 return(OK); 502 } 503 504 /* 505 * no_color_video -- 506 * Return attributes that cannot be combined with color. 507 */ 508 attr_t 509 no_color_video(void) 510 { 511 return(_cursesi_screen->nca); 512 } 513 514 /* 515 * __set_color -- 516 * Set terminal foreground and background colours. 517 */ 518 void 519 __set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr) 520 { 521 short pair; 522 523 if ((curscr->wattr & __COLOR) == (attr & __COLOR)) 524 return; 525 526 pair = PAIR_NUMBER((u_int32_t)attr); 527 #ifdef DEBUG 528 __CTRACE("__set_color: %d, %d, %d\n", pair, 529 _cursesi_screen->colour_pairs[pair].fore, 530 _cursesi_screen->colour_pairs[pair].back); 531 #endif 532 switch (_cursesi_screen->color_type) { 533 /* Set ANSI forground and background colours */ 534 case COLOR_ANSI: 535 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 536 _cursesi_screen->colour_pairs[pair].back < 0) 537 __unset_color(curscr); 538 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 539 tputs(__parse_cap(_cursesi_screen->tc_AF, 540 _cursesi_screen->colour_pairs[pair].fore), 541 0, __cputchar); 542 if (_cursesi_screen->colour_pairs[pair].back >= 0) 543 tputs(__parse_cap(_cursesi_screen->tc_AB, 544 _cursesi_screen->colour_pairs[pair].back), 545 0, __cputchar); 546 break; 547 case COLOR_HP: 548 /* XXX: need to support HP style */ 549 break; 550 case COLOR_TEK: 551 /* XXX: need to support Tek style */ 552 break; 553 case COLOR_OTHER: 554 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 555 _cursesi_screen->colour_pairs[pair].back < 0) 556 __unset_color(curscr); 557 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 558 tputs(__parse_cap(_cursesi_screen->tc_Sf, 559 _cursesi_screen->colour_pairs[pair].fore), 560 0, __cputchar); 561 if (_cursesi_screen->colour_pairs[pair].back >= 0) 562 tputs(__parse_cap(_cursesi_screen->tc_Sb, 563 _cursesi_screen->colour_pairs[pair].back), 564 0, __cputchar); 565 break; 566 } 567 curscr->wattr &= ~__COLOR; 568 curscr->wattr |= attr & __COLOR; 569 } 570 571 /* 572 * __unset_color -- 573 * Clear terminal foreground and background colours. 574 */ 575 void 576 __unset_color(WINDOW *win) 577 { 578 #ifdef DEBUG 579 __CTRACE("__unset_color\n"); 580 #endif 581 switch (_cursesi_screen->color_type) { 582 /* Clear ANSI forground and background colours */ 583 case COLOR_ANSI: 584 if (__tc_op != NULL) { 585 tputs(__tc_op, 0, __cputchar); 586 win->wattr &= __mask_op; 587 } 588 break; 589 case COLOR_HP: 590 /* XXX: need to support HP style */ 591 break; 592 case COLOR_TEK: 593 /* XXX: need to support Tek style */ 594 break; 595 case COLOR_OTHER: 596 if (__tc_op != NULL) { 597 tputs(__tc_op, 0, __cputchar); 598 win->wattr &= __mask_op; 599 } 600 break; 601 } 602 } 603 604 /* 605 * __restore_colors -- 606 * Redo color definitions after restarting 'curses' mode. 607 */ 608 void 609 __restore_colors(void) 610 { 611 if (__tc_cc != 0) 612 switch (_cursesi_screen->color_type) { 613 case COLOR_HP: 614 /* XXX: need to re-initialise HP style (Ip) */ 615 break; 616 case COLOR_TEK: 617 /* XXX: need to re-initialise Tek style (Ic) */ 618 break; 619 } 620 } 621 622 /* 623 * __change_pair -- 624 * Mark dirty all positions using pair. 625 */ 626 void 627 __change_pair(short pair) 628 { 629 struct __winlist *wlp; 630 WINDOW *win; 631 int y, x; 632 __LINE *lp; 633 uint32_t cl = COLOR_PAIR(pair); 634 635 636 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) { 637 #ifdef DEBUG 638 __CTRACE("__change_pair: win = %p\n", wlp->winp); 639 #endif 640 win = wlp->winp; 641 if (win == __virtscr) 642 continue; 643 644 if (win == curscr) { 645 /* Reset colour attribute on curscr */ 646 #ifdef DEBUG 647 __CTRACE("__change_pair: win == curscr\n"); 648 #endif 649 for (y = 0; y < curscr->maxy; y++) { 650 lp = curscr->lines[y]; 651 for (x = 0; x < curscr->maxx; x++) { 652 if ((lp->line[x].attr & __COLOR) == cl) 653 lp->line[x].attr &= ~__COLOR; 654 if ((lp->line[x].battr & __COLOR) == cl) 655 lp->line[x].battr &= ~__COLOR; 656 } 657 } 658 continue; 659 } 660 661 /* Mark dirty those positions with colour pair "pair" */ 662 for (y = 0; y < win->maxy; y++) { 663 lp = curscr->lines[y]; 664 for (x = 0; x < win->maxx; x++) 665 if ((lp->line[x].attr & __COLOR) == cl || 666 (lp->line[x].battr & __COLOR) == cl) { 667 if (!(lp->flags & __ISDIRTY)) 668 lp->flags |= __ISDIRTY; 669 /* 670 * firstchp/lastchp are shared 671 * between parent window and 672 * sub-window. 673 */ 674 if (*lp->firstchp > x) 675 *lp->firstchp = x; 676 if (*lp->lastchp < x) 677 *lp->lastchp = x; 678 } 679 #ifdef DEBUG 680 if ((win->lines[y]->flags & __ISDIRTY)) 681 __CTRACE("__change_pair: first = %d, last = %d\n", *win->lines[y]->firstchp, *win->lines[y]->lastchp); 682 #endif 683 } 684 } 685 } 686