1 /* $OpenBSD: lib_color.c,v 1.11 2010/01/12 23:22:05 nicm Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 * and: Thomas E. Dickey 1996-on * 35 ****************************************************************************/ 36 37 /* lib_color.c 38 * 39 * Handles color emulation of SYS V curses 40 */ 41 42 #include <curses.priv.h> 43 44 #include <term.h> 45 #include <tic.h> 46 47 MODULE_ID("$Id: lib_color.c,v 1.11 2010/01/12 23:22:05 nicm Exp $") 48 49 /* 50 * These should be screen structure members. They need to be globals for 51 * historical reasons. So we assign them in start_color() and also in 52 * set_term()'s screen-switching logic. 53 */ 54 #if USE_REENTRANT 55 NCURSES_EXPORT(int) 56 NCURSES_PUBLIC_VAR(COLOR_PAIRS) (void) 57 { 58 return SP ? SP->_pair_count : -1; 59 } 60 NCURSES_EXPORT(int) 61 NCURSES_PUBLIC_VAR(COLORS) (void) 62 { 63 return SP ? SP->_color_count : -1; 64 } 65 #else 66 NCURSES_EXPORT_VAR(int) COLOR_PAIRS = 0; 67 NCURSES_EXPORT_VAR(int) COLORS = 0; 68 #endif 69 70 #define DATA(r,g,b) {r,g,b, 0,0,0, 0} 71 72 #define TYPE_CALLOC(type,elts) typeCalloc(type, (unsigned)(elts)) 73 74 #define MAX_PALETTE 8 75 76 #define OkColorHi(n) (((n) < COLORS) && ((n) < max_colors)) 77 #define InPalette(n) ((n) >= 0 && (n) < MAX_PALETTE) 78 79 /* 80 * Given a RGB range of 0..1000, we'll normally set the individual values 81 * to about 2/3 of the maximum, leaving full-range for bold/bright colors. 82 */ 83 #define RGB_ON 680 84 #define RGB_OFF 0 85 /* *INDENT-OFF* */ 86 static const color_t cga_palette[] = 87 { 88 /* R G B */ 89 DATA(RGB_OFF, RGB_OFF, RGB_OFF), /* COLOR_BLACK */ 90 DATA(RGB_ON, RGB_OFF, RGB_OFF), /* COLOR_RED */ 91 DATA(RGB_OFF, RGB_ON, RGB_OFF), /* COLOR_GREEN */ 92 DATA(RGB_ON, RGB_ON, RGB_OFF), /* COLOR_YELLOW */ 93 DATA(RGB_OFF, RGB_OFF, RGB_ON), /* COLOR_BLUE */ 94 DATA(RGB_ON, RGB_OFF, RGB_ON), /* COLOR_MAGENTA */ 95 DATA(RGB_OFF, RGB_ON, RGB_ON), /* COLOR_CYAN */ 96 DATA(RGB_ON, RGB_ON, RGB_ON), /* COLOR_WHITE */ 97 }; 98 99 static const color_t hls_palette[] = 100 { 101 /* H L S */ 102 DATA( 0, 0, 0), /* COLOR_BLACK */ 103 DATA( 120, 50, 100), /* COLOR_RED */ 104 DATA( 240, 50, 100), /* COLOR_GREEN */ 105 DATA( 180, 50, 100), /* COLOR_YELLOW */ 106 DATA( 330, 50, 100), /* COLOR_BLUE */ 107 DATA( 60, 50, 100), /* COLOR_MAGENTA */ 108 DATA( 300, 50, 100), /* COLOR_CYAN */ 109 DATA( 0, 50, 100), /* COLOR_WHITE */ 110 }; 111 /* *INDENT-ON* */ 112 113 #if NCURSES_EXT_FUNCS 114 /* 115 * These are called from _nc_do_color(), which in turn is called from 116 * vidattr - so we have to assume that SP may be null. 117 */ 118 static int 119 default_fg(void) 120 { 121 return (SP != 0) ? SP->_default_fg : COLOR_WHITE; 122 } 123 124 static int 125 default_bg(void) 126 { 127 return SP != 0 ? SP->_default_bg : COLOR_BLACK; 128 } 129 #else 130 #define default_fg() COLOR_WHITE 131 #define default_bg() COLOR_BLACK 132 #endif 133 134 /* 135 * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly 136 * to maintain compatibility with a pre-ANSI scheme. The same scheme is 137 * also used in the FreeBSD syscons. 138 */ 139 static int 140 toggled_colors(int c) 141 { 142 if (c < 16) { 143 static const int table[] = 144 {0, 4, 2, 6, 1, 5, 3, 7, 145 8, 12, 10, 14, 9, 13, 11, 15}; 146 c = table[c]; 147 } 148 return c; 149 } 150 151 static void 152 set_background_color(int bg, int (*outc) (int)) 153 { 154 if (set_a_background) { 155 TPUTS_TRACE("set_a_background"); 156 tputs(TPARM_1(set_a_background, bg), 1, outc); 157 } else { 158 TPUTS_TRACE("set_background"); 159 tputs(TPARM_1(set_background, toggled_colors(bg)), 1, outc); 160 } 161 } 162 163 static void 164 set_foreground_color(int fg, int (*outc) (int)) 165 { 166 if (set_a_foreground) { 167 TPUTS_TRACE("set_a_foreground"); 168 tputs(TPARM_1(set_a_foreground, fg), 1, outc); 169 } else { 170 TPUTS_TRACE("set_foreground"); 171 tputs(TPARM_1(set_foreground, toggled_colors(fg)), 1, outc); 172 } 173 } 174 175 static void 176 init_color_table(void) 177 { 178 const color_t *tp; 179 int n; 180 181 tp = (hue_lightness_saturation) ? hls_palette : cga_palette; 182 for (n = 0; n < COLORS; n++) { 183 if (InPalette(n)) { 184 SP->_color_table[n] = tp[n]; 185 } else { 186 SP->_color_table[n] = tp[n % MAX_PALETTE]; 187 if (hue_lightness_saturation) { 188 SP->_color_table[n].green = 100; 189 } else { 190 if (SP->_color_table[n].red) 191 SP->_color_table[n].red = 1000; 192 if (SP->_color_table[n].green) 193 SP->_color_table[n].green = 1000; 194 if (SP->_color_table[n].blue) 195 SP->_color_table[n].blue = 1000; 196 } 197 } 198 } 199 } 200 201 /* 202 * Reset the color pair, e.g., to whatever color pair 0 is. 203 */ 204 static bool 205 reset_color_pair(void) 206 { 207 bool result = FALSE; 208 209 if (orig_pair != 0) { 210 TPUTS_TRACE("orig_pair"); 211 putp(orig_pair); 212 result = TRUE; 213 } 214 return result; 215 } 216 217 /* 218 * Reset color pairs and definitions. Actually we do both more to accommodate 219 * badly-written terminal descriptions than for the relatively rare case where 220 * someone has changed the color definitions. 221 */ 222 bool 223 _nc_reset_colors(void) 224 { 225 int result = FALSE; 226 227 T((T_CALLED("_nc_reset_colors()"))); 228 if (SP->_color_defs > 0) 229 SP->_color_defs = -(SP->_color_defs); 230 231 if (reset_color_pair()) 232 result = TRUE; 233 if (orig_colors != 0) { 234 TPUTS_TRACE("orig_colors"); 235 putp(orig_colors); 236 result = TRUE; 237 } 238 returnBool(result); 239 } 240 241 NCURSES_EXPORT(int) 242 start_color(void) 243 { 244 int result = ERR; 245 246 T((T_CALLED("start_color()"))); 247 248 if (SP == 0) { 249 result = ERR; 250 } else if (SP->_coloron) { 251 result = OK; 252 } else { 253 254 if (reset_color_pair() != TRUE) { 255 set_foreground_color(default_fg(), _nc_outch); 256 set_background_color(default_bg(), _nc_outch); 257 } 258 259 if (max_pairs > 0 && max_colors > 0) { 260 SP->_pair_count = max_pairs; 261 SP->_color_count = max_colors; 262 #if !USE_REENTRANT 263 COLOR_PAIRS = max_pairs; 264 COLORS = max_colors; 265 #endif 266 267 if ((SP->_color_pairs = TYPE_CALLOC(colorpair_t, 268 max_pairs)) != 0) { 269 if ((SP->_color_table = TYPE_CALLOC(color_t, 270 max_colors)) != 0) { 271 SP->_color_pairs[0] = PAIR_OF(default_fg(), default_bg()); 272 init_color_table(); 273 274 T(("started color: COLORS = %d, COLOR_PAIRS = %d", 275 COLORS, COLOR_PAIRS)); 276 277 SP->_coloron = 1; 278 result = OK; 279 } else if (SP->_color_pairs != 0) { 280 FreeAndNull(SP->_color_pairs); 281 } 282 } 283 } else { 284 result = OK; 285 } 286 } 287 returnCode(result); 288 } 289 290 /* This function was originally written by Daniel Weaver <danw@znyx.com> */ 291 static void 292 rgb2hls(short r, short g, short b, short *h, short *l, short *s) 293 /* convert RGB to HLS system */ 294 { 295 short min, max, t; 296 297 if ((min = g < r ? g : r) > b) 298 min = b; 299 if ((max = g > r ? g : r) < b) 300 max = b; 301 302 /* calculate lightness */ 303 *l = (min + max) / 20; 304 305 if (min == max) { /* black, white and all shades of gray */ 306 *h = 0; 307 *s = 0; 308 return; 309 } 310 311 /* calculate saturation */ 312 if (*l < 50) 313 *s = ((max - min) * 100) / (max + min); 314 else 315 *s = ((max - min) * 100) / (2000 - max - min); 316 317 /* calculate hue */ 318 if (r == max) 319 t = 120 + ((g - b) * 60) / (max - min); 320 else if (g == max) 321 t = 240 + ((b - r) * 60) / (max - min); 322 else 323 t = 360 + ((r - g) * 60) / (max - min); 324 325 *h = t % 360; 326 } 327 328 /* 329 * Extension (1997/1/18) - Allow negative f/b values to set default color 330 * values. 331 */ 332 NCURSES_EXPORT(int) 333 init_pair(short pair, short f, short b) 334 { 335 colorpair_t result; 336 337 T((T_CALLED("init_pair(%d,%d,%d)"), pair, f, b)); 338 339 if ((pair < 0) || (pair >= COLOR_PAIRS) || SP == 0 || !SP->_coloron) 340 returnCode(ERR); 341 #if NCURSES_EXT_FUNCS 342 if (SP->_default_color) { 343 if (f < 0) 344 f = COLOR_DEFAULT; 345 if (b < 0) 346 b = COLOR_DEFAULT; 347 if (!OkColorHi(f) && !isDefaultColor(f)) 348 returnCode(ERR); 349 if (!OkColorHi(b) && !isDefaultColor(b)) 350 returnCode(ERR); 351 } else 352 #endif 353 { 354 if ((f < 0) || !OkColorHi(f) 355 || (b < 0) || !OkColorHi(b) 356 || (pair < 1)) 357 returnCode(ERR); 358 } 359 360 /* 361 * When a pair's content is changed, replace its colors (if pair was 362 * initialized before a screen update is performed replacing original 363 * pair colors with the new ones). 364 */ 365 result = PAIR_OF(f, b); 366 if (SP->_color_pairs[pair] != 0 367 && SP->_color_pairs[pair] != result) { 368 int y, x; 369 370 for (y = 0; y <= curscr->_maxy; y++) { 371 struct ldat *ptr = &(curscr->_line[y]); 372 bool changed = FALSE; 373 for (x = 0; x <= curscr->_maxx; x++) { 374 if (GetPair(ptr->text[x]) == pair) { 375 /* Set the old cell to zero to ensure it will be 376 updated on the next doupdate() */ 377 SetChar(ptr->text[x], 0, 0); 378 CHANGED_CELL(ptr, x); 379 changed = TRUE; 380 } 381 } 382 if (changed) 383 _nc_make_oldhash(y); 384 } 385 } 386 SP->_color_pairs[pair] = result; 387 if (GET_SCREEN_PAIR(SP) == pair) 388 SET_SCREEN_PAIR(SP, (chtype) (~0)); /* force attribute update */ 389 390 if (initialize_pair && InPalette(f) && InPalette(b)) { 391 const color_t *tp = hue_lightness_saturation ? hls_palette : cga_palette; 392 393 TR(TRACE_ATTRS, 394 ("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)", 395 pair, 396 tp[f].red, tp[f].green, tp[f].blue, 397 tp[b].red, tp[b].green, tp[b].blue)); 398 399 TPUTS_TRACE("initialize_pair"); 400 putp(TPARM_7(initialize_pair, 401 pair, 402 tp[f].red, tp[f].green, tp[f].blue, 403 tp[b].red, tp[b].green, tp[b].blue)); 404 } 405 406 returnCode(OK); 407 } 408 409 #define okRGB(n) ((n) >= 0 && (n) <= 1000) 410 411 NCURSES_EXPORT(int) 412 init_color(short color, short r, short g, short b) 413 { 414 int result = ERR; 415 416 T((T_CALLED("init_color(%d,%d,%d,%d)"), color, r, g, b)); 417 418 if (initialize_color != NULL 419 && SP != 0 420 && SP->_coloron 421 && (color >= 0 && OkColorHi(color)) 422 && (okRGB(r) && okRGB(g) && okRGB(b))) { 423 424 SP->_color_table[color].init = 1; 425 SP->_color_table[color].r = r; 426 SP->_color_table[color].g = g; 427 SP->_color_table[color].b = b; 428 429 if (hue_lightness_saturation) { 430 rgb2hls(r, g, b, 431 &SP->_color_table[color].red, 432 &SP->_color_table[color].green, 433 &SP->_color_table[color].blue); 434 } else { 435 SP->_color_table[color].red = r; 436 SP->_color_table[color].green = g; 437 SP->_color_table[color].blue = b; 438 } 439 440 TPUTS_TRACE("initialize_color"); 441 putp(TPARM_4(initialize_color, color, r, g, b)); 442 SP->_color_defs = max(color + 1, SP->_color_defs); 443 result = OK; 444 } 445 returnCode(result); 446 } 447 448 NCURSES_EXPORT(bool) 449 can_change_color(void) 450 { 451 T((T_CALLED("can_change_color()"))); 452 returnCode((can_change != 0) ? TRUE : FALSE); 453 } 454 455 NCURSES_EXPORT(bool) 456 has_colors(void) 457 { 458 T((T_CALLED("has_colors()"))); 459 returnCode((VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs) 460 && (((set_foreground != NULL) 461 && (set_background != NULL)) 462 || ((set_a_foreground != NULL) 463 && (set_a_background != NULL)) 464 || set_color_pair)) ? TRUE : FALSE); 465 } 466 467 NCURSES_EXPORT(int) 468 color_content(short color, short *r, short *g, short *b) 469 { 470 int result; 471 472 T((T_CALLED("color_content(%d,%p,%p,%p)"), color, r, g, b)); 473 if (color < 0 || !OkColorHi(color) || SP == 0 || !SP->_coloron) { 474 result = ERR; 475 } else { 476 NCURSES_COLOR_T c_r = SP->_color_table[color].red; 477 NCURSES_COLOR_T c_g = SP->_color_table[color].green; 478 NCURSES_COLOR_T c_b = SP->_color_table[color].blue; 479 480 if (r) 481 *r = c_r; 482 if (g) 483 *g = c_g; 484 if (b) 485 *b = c_b; 486 487 TR(TRACE_ATTRS, ("...color_content(%d,%d,%d,%d)", 488 color, c_r, c_g, c_b)); 489 result = OK; 490 } 491 returnCode(result); 492 } 493 494 NCURSES_EXPORT(int) 495 pair_content(short pair, short *f, short *b) 496 { 497 int result; 498 499 T((T_CALLED("pair_content(%d,%p,%p)"), pair, f, b)); 500 501 if ((pair < 0) || (pair >= COLOR_PAIRS) || SP == 0 || !SP->_coloron) { 502 result = ERR; 503 } else { 504 NCURSES_COLOR_T fg = ((SP->_color_pairs[pair] >> C_SHIFT) & C_MASK); 505 NCURSES_COLOR_T bg = (SP->_color_pairs[pair] & C_MASK); 506 507 #if NCURSES_EXT_FUNCS 508 if (fg == COLOR_DEFAULT) 509 fg = -1; 510 if (bg == COLOR_DEFAULT) 511 bg = -1; 512 #endif 513 514 if (f) 515 *f = fg; 516 if (b) 517 *b = bg; 518 519 TR(TRACE_ATTRS, ("...pair_content(%d,%d,%d)", pair, fg, bg)); 520 result = OK; 521 } 522 returnCode(result); 523 } 524 525 NCURSES_EXPORT(void) 526 _nc_do_color(short old_pair, short pair, bool reverse, int (*outc) (int)) 527 { 528 NCURSES_COLOR_T fg = COLOR_DEFAULT; 529 NCURSES_COLOR_T bg = COLOR_DEFAULT; 530 NCURSES_COLOR_T old_fg, old_bg; 531 532 if (pair < 0 || pair >= COLOR_PAIRS) { 533 return; 534 } else if (pair != 0) { 535 if (set_color_pair) { 536 TPUTS_TRACE("set_color_pair"); 537 tputs(TPARM_1(set_color_pair, pair), 1, outc); 538 return; 539 } else if (SP != 0) { 540 pair_content((short) pair, &fg, &bg); 541 } 542 } 543 544 if (old_pair >= 0 545 && SP != 0 546 && pair_content(old_pair, &old_fg, &old_bg) != ERR) { 547 if ((isDefaultColor(fg) && !isDefaultColor(old_fg)) 548 || (isDefaultColor(bg) && !isDefaultColor(old_bg))) { 549 #if NCURSES_EXT_FUNCS 550 /* 551 * A minor optimization - but extension. If "AX" is specified in 552 * the terminal description, treat it as screen's indicator of ECMA 553 * SGR 39 and SGR 49, and assume the two sequences are independent. 554 */ 555 if (SP->_has_sgr_39_49 556 && isDefaultColor(old_bg) 557 && !isDefaultColor(old_fg)) { 558 tputs("\033[39m", 1, outc); 559 } else if (SP->_has_sgr_39_49 560 && isDefaultColor(old_fg) 561 && !isDefaultColor(old_bg)) { 562 tputs("\033[49m", 1, outc); 563 } else 564 #endif 565 reset_color_pair(); 566 } 567 } else { 568 reset_color_pair(); 569 if (old_pair < 0) 570 return; 571 } 572 573 #if NCURSES_EXT_FUNCS 574 if (isDefaultColor(fg)) 575 fg = default_fg(); 576 if (isDefaultColor(bg)) 577 bg = default_bg(); 578 #endif 579 580 if (reverse) { 581 NCURSES_COLOR_T xx = fg; 582 fg = bg; 583 bg = xx; 584 } 585 586 TR(TRACE_ATTRS, ("setting colors: pair = %d, fg = %d, bg = %d", pair, 587 fg, bg)); 588 589 if (!isDefaultColor(fg)) { 590 set_foreground_color(fg, outc); 591 } 592 if (!isDefaultColor(bg)) { 593 set_background_color(bg, outc); 594 } 595 } 596