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