1 /* $NetBSD: color.c,v 1.36 2010/12/25 10:05:08 blymn 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.36 2010/12/25 10:05:08 blymn 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 (max_colors > 0 && max_pairs > 0 && 64 ((set_a_foreground != NULL && set_a_background != NULL) || 65 initialize_pair != NULL || initialize_color != NULL || 66 (set_background != NULL && set_foreground != NULL))) 67 return(TRUE); 68 else 69 return(FALSE); 70 } 71 72 /* 73 * can_change_color -- 74 * Check if terminal can change colours. 75 */ 76 bool 77 can_change_color(void) 78 { 79 if (can_change) 80 return(TRUE); 81 else 82 return(FALSE); 83 } 84 85 /* 86 * start_color -- 87 * Initialise colour support. 88 */ 89 int 90 start_color(void) 91 { 92 int i; 93 attr_t temp_nc; 94 struct __winlist *wlp; 95 WINDOW *win; 96 int y, x; 97 98 if (has_colors() == FALSE) 99 return(ERR); 100 101 /* Max colours and colour pairs */ 102 if (max_colors == -1) 103 COLORS = 0; 104 else { 105 COLORS = max_colors > MAX_COLORS ? MAX_COLORS : max_colors; 106 if (max_pairs == -1) { 107 COLOR_PAIRS = 0; 108 COLORS = 0; 109 } else { 110 COLOR_PAIRS = (max_pairs > MAX_PAIRS - 1 ? 111 MAX_PAIRS - 1 : max_pairs); 112 /* Use the last colour pair for curses default. */ 113 __default_color = COLOR_PAIR(MAX_PAIRS - 1); 114 } 115 } 116 if (!COLORS) 117 return (ERR); 118 119 _cursesi_screen->COLORS = COLORS; 120 _cursesi_screen->COLOR_PAIRS = COLOR_PAIRS; 121 122 /* Reset terminal colour and colour pairs. */ 123 if (orig_colors != NULL) 124 tputs(orig_colors, 0, __cputchar); 125 if (orig_pair != NULL) { 126 tputs(orig_pair, 0, __cputchar); 127 curscr->wattr &= _cursesi_screen->mask_op; 128 } 129 130 /* Type of colour manipulation - ANSI/TEK/HP/other */ 131 if (set_a_foreground != NULL && set_a_background != NULL) 132 _cursesi_screen->color_type = COLOR_ANSI; 133 else if (initialize_pair != NULL) 134 _cursesi_screen->color_type = COLOR_HP; 135 else if (initialize_color != NULL) 136 _cursesi_screen->color_type = COLOR_TEK; 137 else if (set_foreground != NULL && set_background != NULL) 138 _cursesi_screen->color_type = COLOR_OTHER; 139 else 140 return(ERR); /* Unsupported colour method */ 141 142 #ifdef DEBUG 143 __CTRACE(__CTRACE_COLOR, "start_color: COLORS = %d, COLOR_PAIRS = %d", 144 COLORS, COLOR_PAIRS); 145 switch (_cursesi_screen->color_type) { 146 case COLOR_ANSI: 147 __CTRACE(__CTRACE_COLOR, " (ANSI style)\n"); 148 break; 149 case COLOR_HP: 150 __CTRACE(__CTRACE_COLOR, " (HP style)\n"); 151 break; 152 case COLOR_TEK: 153 __CTRACE(__CTRACE_COLOR, " (Tektronics style)\n"); 154 break; 155 case COLOR_OTHER: 156 __CTRACE(__CTRACE_COLOR, " (Other style)\n"); 157 break; 158 } 159 #endif 160 161 /* 162 * Attributes that cannot be used with color. 163 * Store these in an attr_t for wattrset()/wattron(). 164 */ 165 _cursesi_screen->nca = __NORMAL; 166 if (no_color_video != -1) { 167 temp_nc = (attr_t) t_no_color_video(_cursesi_screen->term); 168 if (temp_nc & 0x0001) 169 _cursesi_screen->nca |= __STANDOUT; 170 if (temp_nc & 0x0002) 171 _cursesi_screen->nca |= __UNDERSCORE; 172 if (temp_nc & 0x0004) 173 _cursesi_screen->nca |= __REVERSE; 174 if (temp_nc & 0x0008) 175 _cursesi_screen->nca |= __BLINK; 176 if (temp_nc & 0x0010) 177 _cursesi_screen->nca |= __DIM; 178 if (temp_nc & 0x0020) 179 _cursesi_screen->nca |= __BOLD; 180 if (temp_nc & 0x0040) 181 _cursesi_screen->nca |= __BLANK; 182 if (temp_nc & 0x0080) 183 _cursesi_screen->nca |= __PROTECT; 184 if (temp_nc & 0x0100) 185 _cursesi_screen->nca |= __ALTCHARSET; 186 } 187 #ifdef DEBUG 188 __CTRACE(__CTRACE_COLOR, "start_color: _cursesi_screen->nca = %08x\n", 189 _cursesi_screen->nca); 190 #endif 191 192 /* Set up initial 8 colours */ 193 if (COLORS >= COLOR_BLACK) 194 (void) init_color(COLOR_BLACK, 0, 0, 0); 195 if (COLORS >= COLOR_RED) 196 (void) init_color(COLOR_RED, 1000, 0, 0); 197 if (COLORS >= COLOR_GREEN) 198 (void) init_color(COLOR_GREEN, 0, 1000, 0); 199 if (COLORS >= COLOR_YELLOW) 200 (void) init_color(COLOR_YELLOW, 1000, 1000, 0); 201 if (COLORS >= COLOR_BLUE) 202 (void) init_color(COLOR_BLUE, 0, 0, 1000); 203 if (COLORS >= COLOR_MAGENTA) 204 (void) init_color(COLOR_MAGENTA, 1000, 0, 1000); 205 if (COLORS >= COLOR_CYAN) 206 (void) init_color(COLOR_CYAN, 0, 1000, 1000); 207 if (COLORS >= COLOR_WHITE) 208 (void) init_color(COLOR_WHITE, 1000, 1000, 1000); 209 210 /* Initialise other colours */ 211 for (i = 8; i < COLORS; i++) { 212 _cursesi_screen->colours[i].red = 0; 213 _cursesi_screen->colours[i].green = 0; 214 _cursesi_screen->colours[i].blue = 0; 215 _cursesi_screen->colours[i].flags = 0; 216 } 217 218 /* Initialise pair 0 to default colours. */ 219 _cursesi_screen->colour_pairs[0].fore = -1; 220 _cursesi_screen->colour_pairs[0].back = -1; 221 _cursesi_screen->colour_pairs[0].flags = 0; 222 223 /* Initialise user colour pairs to default (white on black) */ 224 for (i = 0; i < COLOR_PAIRS; i++) { 225 _cursesi_screen->colour_pairs[i].fore = COLOR_WHITE; 226 _cursesi_screen->colour_pairs[i].back = COLOR_BLACK; 227 _cursesi_screen->colour_pairs[i].flags = 0; 228 } 229 230 /* Initialise default colour pair. */ 231 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = 232 __default_pair.fore; 233 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = 234 __default_pair.back; 235 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = 236 __default_pair.flags; 237 238 __using_color = 1; 239 240 /* Set all positions on all windows to curses default colours. */ 241 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) { 242 win = wlp->winp; 243 if (wlp->winp != __virtscr && wlp->winp != curscr) { 244 /* Set color attribute on other windows */ 245 win->battr |= __default_color; 246 for (y = 0; y < win->maxy; y++) { 247 for (x = 0; x < win->maxx; x++) { 248 win->alines[y]->line[x].attr &= ~__COLOR; 249 win->alines[y]->line[x].attr |= __default_color; 250 } 251 } 252 __touchwin(win); 253 } 254 } 255 256 return(OK); 257 } 258 259 /* 260 * init_pair -- 261 * Set pair foreground and background colors. 262 * Our default colour ordering is ANSI - 1 = red, 4 = blue, 3 = yellow, 263 * 6 = cyan. The older style (Sb/Sf) uses 1 = blue, 4 = red, 3 = cyan, 264 * 6 = yellow, so we swap them here and in pair_content(). 265 */ 266 int 267 init_pair(short pair, short fore, short back) 268 { 269 int changed; 270 271 #ifdef DEBUG 272 __CTRACE(__CTRACE_COLOR, "init_pair: %d, %d, %d\n", pair, fore, back); 273 #endif 274 275 if (pair < 0 || pair >= COLOR_PAIRS) 276 return (ERR); 277 278 if (pair == 0) /* Ignore request for pair 0, it is default. */ 279 return OK; 280 281 if (fore >= COLORS) 282 return (ERR); 283 if (back >= COLORS) 284 return (ERR); 285 286 /* Swap red/blue and yellow/cyan */ 287 if (_cursesi_screen->color_type == COLOR_OTHER) { 288 switch (fore) { 289 case COLOR_RED: 290 fore = COLOR_BLUE; 291 break; 292 case COLOR_BLUE: 293 fore = COLOR_RED; 294 break; 295 case COLOR_YELLOW: 296 fore = COLOR_CYAN; 297 break; 298 case COLOR_CYAN: 299 fore = COLOR_YELLOW; 300 break; 301 } 302 switch (back) { 303 case COLOR_RED: 304 back = COLOR_BLUE; 305 break; 306 case COLOR_BLUE: 307 back = COLOR_RED; 308 break; 309 case COLOR_YELLOW: 310 back = COLOR_CYAN; 311 break; 312 case COLOR_CYAN: 313 back = COLOR_YELLOW; 314 break; 315 } 316 } 317 318 if ((_cursesi_screen->colour_pairs[pair].flags & __USED) && 319 (fore != _cursesi_screen->colour_pairs[pair].fore || 320 back != _cursesi_screen->colour_pairs[pair].back)) 321 changed = 1; 322 else 323 changed = 0; 324 325 _cursesi_screen->colour_pairs[pair].flags |= __USED; 326 _cursesi_screen->colour_pairs[pair].fore = fore; 327 _cursesi_screen->colour_pairs[pair].back = back; 328 329 /* XXX: need to initialise HP style (Ip) */ 330 331 if (changed) 332 __change_pair(pair); 333 return (OK); 334 } 335 336 /* 337 * pair_content -- 338 * Get pair foreground and background colours. 339 */ 340 int 341 pair_content(short pair, short *forep, short *backp) 342 { 343 if (pair < 0 || pair > _cursesi_screen->COLOR_PAIRS) 344 return(ERR); 345 346 *forep = _cursesi_screen->colour_pairs[pair].fore; 347 *backp = _cursesi_screen->colour_pairs[pair].back; 348 349 /* Swap red/blue and yellow/cyan */ 350 if (_cursesi_screen->color_type == COLOR_OTHER) { 351 switch (*forep) { 352 case COLOR_RED: 353 *forep = COLOR_BLUE; 354 break; 355 case COLOR_BLUE: 356 *forep = COLOR_RED; 357 break; 358 case COLOR_YELLOW: 359 *forep = COLOR_CYAN; 360 break; 361 case COLOR_CYAN: 362 *forep = COLOR_YELLOW; 363 break; 364 } 365 switch (*backp) { 366 case COLOR_RED: 367 *backp = COLOR_BLUE; 368 break; 369 case COLOR_BLUE: 370 *backp = COLOR_RED; 371 break; 372 case COLOR_YELLOW: 373 *backp = COLOR_CYAN; 374 break; 375 case COLOR_CYAN: 376 *backp = COLOR_YELLOW; 377 break; 378 } 379 } 380 return(OK); 381 } 382 383 /* 384 * init_color -- 385 * Set colour red, green and blue values. 386 */ 387 int 388 init_color(short color, short red, short green, short blue) 389 { 390 #ifdef DEBUG 391 __CTRACE(__CTRACE_COLOR, "init_color: %d, %d, %d, %d\n", 392 color, red, green, blue); 393 #endif 394 if (color < 0 || color >= _cursesi_screen->COLORS) 395 return(ERR); 396 397 _cursesi_screen->colours[color].red = red; 398 _cursesi_screen->colours[color].green = green; 399 _cursesi_screen->colours[color].blue = blue; 400 /* XXX Not yet implemented */ 401 return(ERR); 402 /* XXX: need to initialise Tek style (Ic) and support HLS */ 403 } 404 405 /* 406 * color_content -- 407 * Get colour red, green and blue values. 408 */ 409 int 410 color_content(short color, short *redp, short *greenp, short *bluep) 411 { 412 if (color < 0 || color >= _cursesi_screen->COLORS) 413 return(ERR); 414 415 *redp = _cursesi_screen->colours[color].red; 416 *greenp = _cursesi_screen->colours[color].green; 417 *bluep = _cursesi_screen->colours[color].blue; 418 return(OK); 419 } 420 421 /* 422 * use_default_colors -- 423 * Use terminal default colours instead of curses default colour. 424 */ 425 int 426 use_default_colors() 427 { 428 #ifdef DEBUG 429 __CTRACE(__CTRACE_COLOR, "use_default_colors\n"); 430 #endif 431 432 return(assume_default_colors(-1, -1)); 433 } 434 435 /* 436 * assume_default_colors -- 437 * Set the default foreground and background colours. 438 */ 439 int 440 assume_default_colors(short arg_fore, short arg_back) 441 { 442 short fore, back; 443 444 fore = arg_fore; 445 back = arg_back; 446 447 #ifdef DEBUG 448 __CTRACE(__CTRACE_COLOR, "assume_default_colors: %d, %d\n", 449 fore, back); 450 __CTRACE(__CTRACE_COLOR, "assume_default_colors: default_colour = %d, pair_number = %d\n", __default_color, PAIR_NUMBER(__default_color)); 451 #endif 452 453 if (arg_fore == -1) 454 fore = COLOR_WHITE; 455 if (arg_back == -1) 456 back = COLOR_BLACK; 457 458 /* Swap red/blue and yellow/cyan */ 459 if (_cursesi_screen->color_type == COLOR_OTHER) { 460 switch (fore) { 461 case COLOR_RED: 462 fore = COLOR_BLUE; 463 break; 464 case COLOR_BLUE: 465 fore = COLOR_RED; 466 break; 467 case COLOR_YELLOW: 468 fore = COLOR_CYAN; 469 break; 470 case COLOR_CYAN: 471 fore = COLOR_YELLOW; 472 break; 473 } 474 switch (back) { 475 case COLOR_RED: 476 back = COLOR_BLUE; 477 break; 478 case COLOR_BLUE: 479 back = COLOR_RED; 480 break; 481 case COLOR_YELLOW: 482 back = COLOR_CYAN; 483 break; 484 case COLOR_CYAN: 485 back = COLOR_YELLOW; 486 break; 487 } 488 } 489 __default_pair.fore = fore; 490 __default_pair.back = back; 491 __default_pair.flags = __USED; 492 493 if (COLOR_PAIRS) { 494 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore; 495 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back; 496 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED; 497 } 498 499 /* 500 * If we've already called start_color(), make sure all instances 501 * of the curses default colour pair are dirty. 502 */ 503 if (__using_color) 504 __change_pair(PAIR_NUMBER(__default_color)); 505 506 return(OK); 507 } 508 509 /* no_color_video is a terminfo macro, but we need to retain binary compat */ 510 #ifdef __strong_alias 511 #undef no_color_video 512 __strong_alias(no_color_video, no_color_attributes) 513 #endif 514 /* 515 * no_color_attributes -- 516 * Return attributes that cannot be combined with color. 517 */ 518 attr_t 519 no_color_attributes(void) 520 { 521 return(_cursesi_screen->nca); 522 } 523 524 /* 525 * __set_color -- 526 * Set terminal foreground and background colours. 527 */ 528 void 529 __set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr) 530 { 531 short pair; 532 533 if ((curscr->wattr & __COLOR) == (attr & __COLOR)) 534 return; 535 536 pair = PAIR_NUMBER((u_int32_t)attr); 537 #ifdef DEBUG 538 __CTRACE(__CTRACE_COLOR, "__set_color: %d, %d, %d\n", pair, 539 _cursesi_screen->colour_pairs[pair].fore, 540 _cursesi_screen->colour_pairs[pair].back); 541 #endif 542 switch (_cursesi_screen->color_type) { 543 /* Set ANSI forground and background colours */ 544 case COLOR_ANSI: 545 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 546 _cursesi_screen->colour_pairs[pair].back < 0) 547 __unset_color(curscr); 548 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 549 tputs(vtparm(t_set_a_foreground(_cursesi_screen->term), 550 _cursesi_screen->colour_pairs[pair].fore), 551 0, __cputchar); 552 if (_cursesi_screen->colour_pairs[pair].back >= 0) 553 tputs(vtparm(t_set_a_background(_cursesi_screen->term), 554 _cursesi_screen->colour_pairs[pair].back), 555 0, __cputchar); 556 break; 557 case COLOR_HP: 558 /* XXX: need to support HP style */ 559 break; 560 case COLOR_TEK: 561 /* XXX: need to support Tek style */ 562 break; 563 case COLOR_OTHER: 564 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 565 _cursesi_screen->colour_pairs[pair].back < 0) 566 __unset_color(curscr); 567 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 568 tputs(vtparm(t_set_foreground(_cursesi_screen->term), 569 _cursesi_screen->colour_pairs[pair].fore), 570 0, __cputchar); 571 if (_cursesi_screen->colour_pairs[pair].back >= 0) 572 tputs(vtparm(t_set_background(_cursesi_screen->term), 573 _cursesi_screen->colour_pairs[pair].back), 574 0, __cputchar); 575 break; 576 } 577 curscr->wattr &= ~__COLOR; 578 curscr->wattr |= attr & __COLOR; 579 } 580 581 /* 582 * __unset_color -- 583 * Clear terminal foreground and background colours. 584 */ 585 void 586 __unset_color(WINDOW *win) 587 { 588 #ifdef DEBUG 589 __CTRACE(__CTRACE_COLOR, "__unset_color\n"); 590 #endif 591 switch (_cursesi_screen->color_type) { 592 /* Clear ANSI forground and background colours */ 593 case COLOR_ANSI: 594 if (orig_pair != NULL) { 595 tputs(orig_pair, 0, __cputchar); 596 win->wattr &= __mask_op; 597 } 598 break; 599 case COLOR_HP: 600 /* XXX: need to support HP style */ 601 break; 602 case COLOR_TEK: 603 /* XXX: need to support Tek style */ 604 break; 605 case COLOR_OTHER: 606 if (orig_pair != NULL) { 607 tputs(orig_pair, 0, __cputchar); 608 win->wattr &= __mask_op; 609 } 610 break; 611 } 612 } 613 614 /* 615 * __restore_colors -- 616 * Redo color definitions after restarting 'curses' mode. 617 */ 618 void 619 __restore_colors(void) 620 { 621 if (can_change != 0) 622 switch (_cursesi_screen->color_type) { 623 case COLOR_HP: 624 /* XXX: need to re-initialise HP style (Ip) */ 625 break; 626 case COLOR_TEK: 627 /* XXX: need to re-initialise Tek style (Ic) */ 628 break; 629 } 630 } 631 632 /* 633 * __change_pair -- 634 * Mark dirty all positions using pair. 635 */ 636 void 637 __change_pair(short pair) 638 { 639 struct __winlist *wlp; 640 WINDOW *win; 641 int y, x; 642 __LINE *lp; 643 uint32_t cl = COLOR_PAIR(pair); 644 645 646 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) { 647 #ifdef DEBUG 648 __CTRACE(__CTRACE_COLOR, "__change_pair: win = %p\n", 649 wlp->winp); 650 #endif 651 win = wlp->winp; 652 if (win == __virtscr) 653 continue; 654 else if (win == curscr) { 655 /* Reset colour attribute on curscr */ 656 #ifdef DEBUG 657 __CTRACE(__CTRACE_COLOR, 658 "__change_pair: win == curscr\n"); 659 #endif 660 for (y = 0; y < curscr->maxy; y++) { 661 lp = curscr->alines[y]; 662 for (x = 0; x < curscr->maxx; x++) { 663 if ((lp->line[x].attr & __COLOR) == cl) 664 lp->line[x].attr &= ~__COLOR; 665 } 666 } 667 } else { 668 /* Mark dirty those positions with colour pair "pair" */ 669 for (y = 0; y < win->maxy; y++) { 670 lp = win->alines[y]; 671 for (x = 0; x < win->maxx; x++) 672 if ((lp->line[x].attr & 673 __COLOR) == cl) { 674 if (!(lp->flags & __ISDIRTY)) 675 lp->flags |= __ISDIRTY; 676 /* 677 * firstchp/lastchp are shared 678 * between parent window and 679 * sub-window. 680 */ 681 if (*lp->firstchp > x) 682 *lp->firstchp = x; 683 if (*lp->lastchp < x) 684 *lp->lastchp = x; 685 } 686 #ifdef DEBUG 687 if ((win->alines[y]->flags & __ISDIRTY)) 688 __CTRACE(__CTRACE_COLOR, 689 "__change_pair: first = %d, " 690 "last = %d\n", 691 *win->alines[y]->firstchp, 692 *win->alines[y]->lastchp); 693 #endif 694 } 695 } 696 } 697 } 698