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