1 /* $NetBSD: color.c,v 1.45 2021/12/17 03:50:18 uwe 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.45 2021/12/17 03:50:18 uwe 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 static int 57 init_color_value(short, short, short, short); 58 59 /* 60 * has_colors -- 61 * Check if terminal has colours. 62 */ 63 bool 64 has_colors(void) 65 { 66 if (max_colors > 0 && max_pairs > 0 && 67 ((set_a_foreground != NULL && set_a_background != NULL) || 68 initialize_pair != NULL || initialize_color != NULL || 69 (set_background != NULL && set_foreground != NULL))) 70 return true; 71 else 72 return false; 73 } 74 75 /* 76 * can_change_color -- 77 * Check if terminal can change colours. 78 */ 79 bool 80 can_change_color(void) 81 { 82 return can_change ? true : 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 __CTRACE(__CTRACE_COLOR, "start_color: _cursesi_screen->nca = %08x\n", 188 _cursesi_screen->nca); 189 190 /* Set up initial 8 colours */ 191 #define RGB_ON 680 /* Allow for bright colours */ 192 if (COLORS >= COLOR_BLACK) 193 (void)init_color_value(COLOR_BLACK, 0, 0, 0); 194 if (COLORS >= COLOR_RED) 195 (void)init_color_value(COLOR_RED, RGB_ON, 0, 0); 196 if (COLORS >= COLOR_GREEN) 197 (void)init_color_value(COLOR_GREEN, 0, RGB_ON, 0); 198 if (COLORS >= COLOR_YELLOW) 199 (void)init_color_value(COLOR_YELLOW, RGB_ON, RGB_ON, 0); 200 if (COLORS >= COLOR_BLUE) 201 (void)init_color_value(COLOR_BLUE, 0, 0, RGB_ON); 202 if (COLORS >= COLOR_MAGENTA) 203 (void)init_color_value(COLOR_MAGENTA, RGB_ON, 0, RGB_ON); 204 if (COLORS >= COLOR_CYAN) 205 (void)init_color_value(COLOR_CYAN, 0, RGB_ON, RGB_ON); 206 if (COLORS >= COLOR_WHITE) 207 (void)init_color_value(COLOR_WHITE, RGB_ON, RGB_ON, RGB_ON); 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->alines[y]->line[x].attr &= ~__COLOR; 248 win->alines[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 __CTRACE(__CTRACE_COLOR, "init_pair: %d, %d, %d\n", pair, fore, back); 271 272 if (pair < 0 || pair >= COLOR_PAIRS) 273 return ERR; 274 275 if (pair == 0) /* Ignore request for pair 0, it is default. */ 276 return OK; 277 278 if (fore >= COLORS) 279 return ERR; 280 if (back >= COLORS) 281 return ERR; 282 283 /* Swap red/blue and yellow/cyan */ 284 if (_cursesi_screen->color_type == COLOR_OTHER) { 285 switch (fore) { 286 case COLOR_RED: 287 fore = COLOR_BLUE; 288 break; 289 case COLOR_BLUE: 290 fore = COLOR_RED; 291 break; 292 case COLOR_YELLOW: 293 fore = COLOR_CYAN; 294 break; 295 case COLOR_CYAN: 296 fore = COLOR_YELLOW; 297 break; 298 } 299 switch (back) { 300 case COLOR_RED: 301 back = COLOR_BLUE; 302 break; 303 case COLOR_BLUE: 304 back = COLOR_RED; 305 break; 306 case COLOR_YELLOW: 307 back = COLOR_CYAN; 308 break; 309 case COLOR_CYAN: 310 back = COLOR_YELLOW; 311 break; 312 } 313 } 314 315 if ((_cursesi_screen->colour_pairs[pair].flags & __USED) && 316 (fore != _cursesi_screen->colour_pairs[pair].fore || 317 back != _cursesi_screen->colour_pairs[pair].back)) 318 changed = 1; 319 else 320 changed = 0; 321 322 _cursesi_screen->colour_pairs[pair].flags |= __USED; 323 _cursesi_screen->colour_pairs[pair].fore = fore; 324 _cursesi_screen->colour_pairs[pair].back = back; 325 326 /* XXX: need to initialise HP style (Ip) */ 327 328 if (changed) 329 __change_pair(pair); 330 return OK; 331 } 332 333 /* 334 * pair_content -- 335 * Get pair foreground and background colours. 336 */ 337 int 338 pair_content(short pair, short *forep, short *backp) 339 { 340 if (pair < 0 || pair > _cursesi_screen->COLOR_PAIRS) 341 return ERR; 342 343 *forep = _cursesi_screen->colour_pairs[pair].fore; 344 *backp = _cursesi_screen->colour_pairs[pair].back; 345 346 /* Swap red/blue and yellow/cyan */ 347 if (_cursesi_screen->color_type == COLOR_OTHER) { 348 switch (*forep) { 349 case COLOR_RED: 350 *forep = COLOR_BLUE; 351 break; 352 case COLOR_BLUE: 353 *forep = COLOR_RED; 354 break; 355 case COLOR_YELLOW: 356 *forep = COLOR_CYAN; 357 break; 358 case COLOR_CYAN: 359 *forep = COLOR_YELLOW; 360 break; 361 } 362 switch (*backp) { 363 case COLOR_RED: 364 *backp = COLOR_BLUE; 365 break; 366 case COLOR_BLUE: 367 *backp = COLOR_RED; 368 break; 369 case COLOR_YELLOW: 370 *backp = COLOR_CYAN; 371 break; 372 case COLOR_CYAN: 373 *backp = COLOR_YELLOW; 374 break; 375 } 376 } 377 return OK; 378 } 379 380 /* 381 * init_color_Value -- 382 * Set colour red, green and blue values. 383 */ 384 static int 385 init_color_value(short color, short red, short green, short blue) 386 { 387 if (color < 0 || color >= _cursesi_screen->COLORS) 388 return ERR; 389 390 _cursesi_screen->colours[color].red = red; 391 _cursesi_screen->colours[color].green = green; 392 _cursesi_screen->colours[color].blue = blue; 393 return OK; 394 } 395 396 /* 397 * init_color -- 398 * Set colour red, green and blue values. 399 * Change color on screen. 400 */ 401 int 402 init_color(short color, short red, short green, short blue) 403 { 404 __CTRACE(__CTRACE_COLOR, "init_color: %d, %d, %d, %d\n", 405 color, red, green, blue); 406 if (init_color_value(color, red, green, blue) == ERR) 407 return ERR; 408 if (!can_change || t_initialize_color(_cursesi_screen->term) == NULL) 409 return ERR; 410 tputs(tiparm(t_initialize_color(_cursesi_screen->term), 411 color, red, green, blue), 0, __cputchar); 412 return OK; 413 } 414 415 /* 416 * color_content -- 417 * Get colour red, green and blue values. 418 */ 419 int 420 color_content(short color, short *redp, short *greenp, short *bluep) 421 { 422 if (color < 0 || color >= _cursesi_screen->COLORS) 423 return ERR; 424 425 *redp = _cursesi_screen->colours[color].red; 426 *greenp = _cursesi_screen->colours[color].green; 427 *bluep = _cursesi_screen->colours[color].blue; 428 return OK; 429 } 430 431 /* 432 * use_default_colors -- 433 * Use terminal default colours instead of curses default colour. 434 */ 435 int 436 use_default_colors(void) 437 { 438 __CTRACE(__CTRACE_COLOR, "use_default_colors\n"); 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 __CTRACE(__CTRACE_COLOR, "assume_default_colors: %d, %d\n", 451 fore, back); 452 __CTRACE(__CTRACE_COLOR, 453 "assume_default_colors: default_colour = %d, pair_number = %d\n", 454 __default_color, PAIR_NUMBER(__default_color)); 455 456 /* Swap red/blue and yellow/cyan */ 457 if (_cursesi_screen->color_type == COLOR_OTHER) { 458 switch (fore) { 459 case COLOR_RED: 460 fore = COLOR_BLUE; 461 break; 462 case COLOR_BLUE: 463 fore = COLOR_RED; 464 break; 465 case COLOR_YELLOW: 466 fore = COLOR_CYAN; 467 break; 468 case COLOR_CYAN: 469 fore = COLOR_YELLOW; 470 break; 471 } 472 switch (back) { 473 case COLOR_RED: 474 back = COLOR_BLUE; 475 break; 476 case COLOR_BLUE: 477 back = COLOR_RED; 478 break; 479 case COLOR_YELLOW: 480 back = COLOR_CYAN; 481 break; 482 case COLOR_CYAN: 483 back = COLOR_YELLOW; 484 break; 485 } 486 } 487 __default_pair.fore = fore; 488 __default_pair.back = back; 489 __default_pair.flags = __USED; 490 491 if (COLOR_PAIRS) { 492 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore; 493 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back; 494 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED; 495 } 496 497 /* 498 * If we've already called start_color(), make sure all instances 499 * of the curses default colour pair are dirty. 500 */ 501 if (__using_color) 502 __change_pair(PAIR_NUMBER(__default_color)); 503 504 return(OK); 505 } 506 507 /* no_color_video is a terminfo macro, but we need to retain binary compat */ 508 #ifdef __strong_alias 509 #undef no_color_video 510 __strong_alias(no_color_video, no_color_attributes) 511 #endif 512 /* 513 * no_color_attributes -- 514 * Return attributes that cannot be combined with color. 515 */ 516 attr_t 517 no_color_attributes(void) 518 { 519 return(_cursesi_screen->nca); 520 } 521 522 /* 523 * __set_color -- 524 * Set terminal foreground and background colours. 525 */ 526 void 527 __set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr) 528 { 529 short pair; 530 531 if ((curscr->wattr & __COLOR) == (attr & __COLOR)) 532 return; 533 534 pair = PAIR_NUMBER((uint32_t)attr); 535 __CTRACE(__CTRACE_COLOR, "__set_color: %d, %d, %d\n", pair, 536 _cursesi_screen->colour_pairs[pair].fore, 537 _cursesi_screen->colour_pairs[pair].back); 538 switch (_cursesi_screen->color_type) { 539 /* Set ANSI foreground and background colours */ 540 case COLOR_ANSI: 541 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 542 _cursesi_screen->colour_pairs[pair].back < 0) 543 __unset_color(curscr); 544 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 545 tputs(tiparm(t_set_a_foreground(_cursesi_screen->term), 546 (int)_cursesi_screen->colour_pairs[pair].fore), 547 0, __cputchar); 548 if (_cursesi_screen->colour_pairs[pair].back >= 0) 549 tputs(tiparm(t_set_a_background(_cursesi_screen->term), 550 (int)_cursesi_screen->colour_pairs[pair].back), 551 0, __cputchar); 552 break; 553 case COLOR_HP: 554 /* XXX: need to support HP style */ 555 break; 556 case COLOR_TEK: 557 /* XXX: need to support Tek style */ 558 break; 559 case COLOR_OTHER: 560 if (_cursesi_screen->colour_pairs[pair].fore < 0 || 561 _cursesi_screen->colour_pairs[pair].back < 0) 562 __unset_color(curscr); 563 if (_cursesi_screen->colour_pairs[pair].fore >= 0) 564 tputs(tiparm(t_set_foreground(_cursesi_screen->term), 565 (int)_cursesi_screen->colour_pairs[pair].fore), 566 0, __cputchar); 567 if (_cursesi_screen->colour_pairs[pair].back >= 0) 568 tputs(tiparm(t_set_background(_cursesi_screen->term), 569 (int)_cursesi_screen->colour_pairs[pair].back), 570 0, __cputchar); 571 break; 572 } 573 curscr->wattr &= ~__COLOR; 574 curscr->wattr |= attr & __COLOR; 575 } 576 577 /* 578 * __unset_color -- 579 * Clear terminal foreground and background colours. 580 */ 581 void 582 __unset_color(WINDOW *win) 583 { 584 __CTRACE(__CTRACE_COLOR, "__unset_color\n"); 585 switch (_cursesi_screen->color_type) { 586 /* Clear ANSI foreground and background colours */ 587 case COLOR_ANSI: 588 if (orig_pair != NULL) { 589 tputs(orig_pair, 0, __cputchar); 590 win->wattr &= __mask_op; 591 } 592 break; 593 case COLOR_HP: 594 /* XXX: need to support HP style */ 595 break; 596 case COLOR_TEK: 597 /* XXX: need to support Tek style */ 598 break; 599 case COLOR_OTHER: 600 if (orig_pair != NULL) { 601 tputs(orig_pair, 0, __cputchar); 602 win->wattr &= __mask_op; 603 } 604 break; 605 } 606 } 607 608 /* 609 * __restore_colors -- 610 * Redo color definitions after restarting 'curses' mode. 611 */ 612 void 613 __restore_colors(void) 614 { 615 if (can_change != 0) 616 switch (_cursesi_screen->color_type) { 617 case COLOR_HP: 618 /* XXX: need to re-initialise HP style (Ip) */ 619 break; 620 case COLOR_TEK: 621 /* XXX: need to re-initialise Tek style (Ic) */ 622 break; 623 } 624 } 625 626 /* 627 * __change_pair -- 628 * Mark dirty all positions using pair. 629 */ 630 void 631 __change_pair(short pair) 632 { 633 struct __winlist *wlp; 634 WINDOW *win; 635 int y, x; 636 __LINE *lp; 637 uint32_t cl = COLOR_PAIR(pair); 638 639 640 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) { 641 __CTRACE(__CTRACE_COLOR, "__change_pair: win = %p\n", 642 wlp->winp); 643 win = wlp->winp; 644 if (win == __virtscr) 645 continue; 646 else if (win == curscr) { 647 /* Reset colour attribute on curscr */ 648 __CTRACE(__CTRACE_COLOR, 649 "__change_pair: win == curscr\n"); 650 for (y = 0; y < curscr->maxy; y++) { 651 lp = curscr->alines[y]; 652 for (x = 0; x < curscr->maxx; x++) { 653 if ((lp->line[x].attr & __COLOR) == cl) 654 lp->line[x].attr &= ~__COLOR; 655 } 656 } 657 } else { 658 /* Mark dirty those positions with colour pair "pair" */ 659 for (y = 0; y < win->maxy; y++) { 660 lp = win->alines[y]; 661 for (x = 0; x < win->maxx; x++) 662 if ((lp->line[x].attr & 663 __COLOR) == cl) { 664 if (!(lp->flags & __ISDIRTY)) 665 lp->flags |= __ISDIRTY; 666 /* 667 * firstchp/lastchp are shared 668 * between parent window and 669 * sub-window. 670 */ 671 if (*lp->firstchp > x) 672 *lp->firstchp = x; 673 if (*lp->lastchp < x) 674 *lp->lastchp = x; 675 } 676 #ifdef DEBUG 677 if ((win->alines[y]->flags & __ISDIRTY)) 678 __CTRACE(__CTRACE_COLOR, 679 "__change_pair: first = %d, " 680 "last = %d\n", 681 *win->alines[y]->firstchp, 682 *win->alines[y]->lastchp); 683 #endif 684 } 685 } 686 } 687 } 688