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