1/* 2** Copyright (C) 1991, 1997 Free Software Foundation, Inc. 3** 4** This file is part of TACK. 5** 6** TACK is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2, or (at your option) 9** any later version. 10** 11** TACK is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with TACK; see the file COPYING. If not, write to 18** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19** Boston, MA 02110-1301, USA 20*/ 21 22#include <tack.h> 23 24MODULE_ID("$Id: color.c,v 1.6 2005/09/17 19:49:16 tom Exp $") 25 26/* 27 * Color terminal tests. Has only one entry point: test_color(). 28 */ 29 30static void color_check(struct test_list *, int *, int *); 31static void color_setf(struct test_list *, int *, int *); 32static void color_matrix(struct test_list *, int *, int *); 33static void color_ncv(struct test_list *, int *, int *); 34static void color_ccc(struct test_list *, int *, int *); 35static void color_bce(struct test_list *, int *, int *); 36 37struct test_list color_test_list[] = { 38 {0, 0, 0, 0, "e) edit terminfo", 0, &edit_menu}, 39 {MENU_NEXT, 2, "colors) (pairs", 0, 0, color_check, 0}, 40 {MENU_NEXT, 12, "setf) (setb) (scp", 0, 0, color_setf, 0}, 41 {MENU_NEXT, 24, "op", 0, 0, color_matrix, 0}, 42 {MENU_NEXT, 16, "ncv", 0, 0, color_ncv, 0}, 43 {MENU_NEXT, 0, "bce", 0, 0, color_bce, 0}, 44 {MENU_NEXT | MENU_CLEAR, 0, "ccc) (initc) (initp", "hls op oc", 0, color_ccc, 0}, 45 {MENU_LAST, 0, 0, 0, 0, 0, 0} 46}; 47 48#ifndef COLOR_BLACK 49#define COLOR_BLACK 0 50#define COLOR_BLUE 1 51#define COLOR_GREEN 2 52#define COLOR_CYAN 3 53#define COLOR_RED 4 54#define COLOR_MAGENTA 5 55#define COLOR_YELLOW 6 56#define COLOR_WHITE 7 57#endif 58 59struct color_table { 60 const char *name; 61 int index; 62 int r, g, b; 63 int h, l, s; 64}; 65 66static struct color_table def_colors[8] = { 67 {"black ", COLOR_BLACK, 0, 0, 0, 0, 0, 0}, 68 {"blue ", COLOR_BLUE, 0, 0, 1000, 330, 50, 100}, 69 {"green ", COLOR_GREEN, 0, 1000, 0, 240, 50, 100}, 70 {"cyan ", COLOR_CYAN, 0, 1000, 1000, 300, 50, 100}, 71 {"red ", COLOR_RED, 1000, 0, 0, 120, 50, 100}, 72 {"magenta", COLOR_MAGENTA, 1000, 0, 1000, 60, 50, 100}, 73 {"yellow ", COLOR_YELLOW, 1000, 1000, 0, 180, 50, 100}, 74 {"white ", COLOR_WHITE, 1000, 1000, 1000, 0, 100, 0} 75}; 76 77#define MAX_PAIR 256 78static int fg_color[MAX_PAIR] = {COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, 79COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE}; 80static int bg_color[MAX_PAIR] = {COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, 81COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK}; 82static int pairs_used = 8; 83static int a_bright_color, bright_value; 84static int cookie_monster, color_step, colors_per_line; 85static int R, G, B; 86 87static void reset_colors(void) 88{ 89 tc_putp(orig_colors); 90 tc_putp(tparm(orig_pair)); 91} 92 93static int 94color_trans(int c) 95{ /* translate or load the color */ 96 int i; 97 98 for (i = 0; i < pairs_used; i++) { 99 if (fg_color[i] == c) { 100 return i; 101 } 102 } 103 if (!can_change) { 104 return 0; 105 } 106 if (pairs_used > max_colors || pairs_used >= MAX_PAIR) { 107 pairs_used = 0; 108 ptextln("Ran out of colors"); 109 } 110 fg_color[pairs_used] = c; 111 bg_color[pairs_used] = c; 112 if (hue_lightness_saturation) { 113 tc_putp(tparm(initialize_color, pairs_used, 114 def_colors[c].h, def_colors[c].l, def_colors[c].s)); 115 } else { 116 tc_putp(tparm(initialize_color, pairs_used, 117 def_colors[c].r, def_colors[c].g, def_colors[c].b)); 118 } 119 return pairs_used++; 120} 121 122static void 123new_color( 124 int fg, 125 int bg, 126 int hungry) 127{ /* change the color to fg and bg. */ 128 int i; 129 130 if (hungry) { 131 eat_cookie(); 132 } 133 if (set_a_foreground) { 134 /* set ANSI color (setaf) (setab) */ 135 tc_putp(tparm(set_a_foreground, fg)); 136 tc_putp(tparm(set_a_background, bg)); 137 } else if (set_foreground) { 138 /* make sure black is zero */ 139 (void) color_trans(COLOR_BLACK); 140 tc_putp(tparm(set_foreground, color_trans(fg))); 141 tc_putp(tparm(set_background, color_trans(bg))); 142 } else { /* set color pair */ 143 for (i = 0; i < pairs_used; i++) { 144 if (fg_color[i] == fg && bg_color[i] == bg) { 145 tc_putp(tparm(set_color_pair, i)); 146 if (hungry) { 147 eat_cookie(); 148 } 149 return; 150 } 151 } 152 if (!can_change) { 153 /* try to set just the foreground */ 154 for (i = pairs_used - 1; i; i--) { 155 if (fg_color[i] == fg) 156 break; 157 } 158 tc_putp(tparm(set_color_pair, i)); 159 if (hungry) { 160 eat_cookie(); 161 } 162 return; 163 } 164 if (pairs_used > max_pairs || pairs_used >= MAX_PAIR) { 165 pairs_used = 0; 166 ptextln("Ran out of color pairs"); 167 } 168 fg_color[pairs_used] = fg; 169 bg_color[pairs_used] = bg; 170 if (hue_lightness_saturation) { 171 tc_putp(tparm(initialize_pair, pairs_used, 172 def_colors[fg].h, def_colors[fg].l, def_colors[fg].s, 173 def_colors[bg].h, def_colors[bg].l, def_colors[bg].s)); 174 } else { 175 tc_putp(tparm(initialize_pair, pairs_used, 176 def_colors[fg].r, def_colors[fg].g, def_colors[fg].b, 177 def_colors[bg].r, def_colors[bg].g, def_colors[bg].b)); 178 } 179 tc_putp(tparm(set_color_pair, pairs_used)); 180 pairs_used++; 181 } 182 if (hungry) { 183 eat_cookie(); 184 } 185} 186 187 188static void 189set_color_step(void) 190{ /* set the color_step for the (ccc) display */ 191 int i; 192 193 for (i = 2; i < 1000; i++) { 194 if ((i * i * i) >= max_colors) { 195 break; 196 } 197 } 198 color_step = 1000 / (i - 1); 199} 200 201 202static void 203rgb_2_hls(int r, int g, int b, int *h, int *l, int *s) 204{ /* convert RGB to HLS system */ 205 int min, max, t; 206 207 if ((min = g < r ? g : r) > b) { 208 min = b; 209 } 210 if ((max = g > r ? g : r) < b) { 211 max = b; 212 } 213 214 /* calculate lightness */ 215 *l = (min + max) / 20; 216 217 if (min == max) { /* black, white and all shades of gray */ 218 *h = 0; 219 *s = 0; 220 return; 221 } 222 /* calculate saturation */ 223 if (*l < 50) { 224 *s = ((max - min) * 100) / (max + min); 225 } else { 226 *s = ((max - min) * 100) / (2000 - max - min); 227 } 228 229 /* calculate hue */ 230 if (r == max) { 231 t = 120 + ((g - b) * 60) / (max - min); 232 } else if (g == max) { 233 t = 240 + ((b - r) * 60) / (max - min); 234 } else { 235 t = 360 + ((r - g) * 60) / (max - min); 236 } 237 *h = t % 360; 238} 239 240 241static void 242send_color(int p, int r, int g, int b) 243{ /* send the initialize_color (initc) command */ 244 int h, l, s; 245 246 if (hue_lightness_saturation) { 247 rgb_2_hls(r, g, b, &h, &l, &s); 248 tc_putp(tparm(initialize_color, p, h, l, s)); 249 } else { 250 tc_putp(tparm(initialize_color, p, r, g, b)); 251 } 252} 253 254 255static void 256send_pair(int p, int fr, int fg, int fb, int br, int bg, int bb) 257{ /* send the initialize_pair (initp) command */ 258 int fh, fl, fs, bh, bl, bs; 259 260 if (hue_lightness_saturation) { 261 rgb_2_hls(fr, fg, fb, &fh, &fl, &fs); 262 rgb_2_hls(br, bg, bb, &bh, &bl, &bs); 263 tc_putp(tparm(initialize_pair, p, fh, fl, fs, bh, bl, bs)); 264 } else { 265 tc_putp(tparm(initialize_pair, p, fr, fg, fb, bb, bg, bb)); 266 } 267} 268 269 270static int 271load_palette(int n) 272{ /* load the color palette */ 273 int rgb; 274 275 for (;;) { 276 if (pairs_used >= n) { 277 return FALSE; 278 } 279 if (set_a_foreground || set_foreground) { 280 if (pairs_used >= max_colors) { 281 return FALSE; 282 } 283 send_color(pairs_used, R, G, B); 284 rgb = R + G + B; 285 if (rgb > bright_value) { 286 bright_value = rgb; 287 a_bright_color = pairs_used; 288 } 289 } else { 290 if (pairs_used >= max_pairs) { 291 return FALSE; 292 } 293 if (pairs_used == 0) { 294 send_pair(pairs_used, 1000, 1000, 1000, R, G, B); 295 } else { 296 send_pair(pairs_used, R, G, B, R, G, B); 297 } 298 } 299 pairs_used++; 300 if ((B += color_step) > 1000) { 301 B = 0; 302 if ((G += color_step) > 1000) { 303 G = 0; 304 if ((R += color_step) > 1000) { 305 return TRUE; 306 } 307 } 308 } 309 } 310} 311 312 313static int 314rainbow(int n) 315{ /* print the programmable color display */ 316 int i, c, d, palette_full, initial_pair; 317 static const struct { 318 const char *name; 319 char ch; 320 } splat[] = { 321 {"Bg normal", ' '}, 322 {"Fg normal", ' '}, 323 {0, 0} 324 }; 325 326 if ((set_a_foreground || set_foreground) 327 ? pairs_used >= max_colors 328 : pairs_used >= max_pairs) { 329 ptext("New palette: "); 330 (void) wait_here(); 331 initial_pair = pairs_used = 1; 332 bright_value = 0; 333 } else if (line_count + 3 >= lines) { 334 ptext("Go: "); 335 (void) wait_here(); 336 put_clear(); 337 initial_pair = pairs_used = 1; 338 bright_value = 0; 339 n++; 340 } else { 341 initial_pair = pairs_used; 342 n += initial_pair; 343 } 344 palette_full = load_palette(n); 345 for (d = 0; splat[d].name; d++) { 346 c = splat[d].ch; 347 if (d == 1) { 348 put_mode(enter_reverse_mode); 349 } 350 for (i = initial_pair; i < n; i++) { 351 if (i >= pairs_used) { 352 break; 353 } 354 if (set_a_foreground) { 355 if (i >= max_colors) { 356 break; 357 } 358 tc_putp(tparm(set_a_foreground, i)); 359 tc_putp(tparm(set_a_background, i)); 360 } else if (set_foreground) { 361 if (i >= max_colors) { 362 break; 363 } 364 tc_putp(tparm(set_foreground, i)); 365 tc_putp(tparm(set_background, i)); 366 } else { 367 if (i >= max_pairs) { 368 break; 369 } 370 tc_putp(tparm(set_color_pair, i)); 371 } 372 putchp(c); 373 } 374 if (d == 1) { 375 put_mode(exit_attribute_mode); 376 } 377 if (set_a_foreground) { 378 tc_putp(tparm(set_a_foreground, a_bright_color)); 379 tc_putp(tparm(set_a_background, 0)); 380 } else if (set_foreground) { 381 tc_putp(tparm(set_foreground, a_bright_color)); 382 tc_putp(tparm(set_background, 0)); 383 } else { 384 tc_putp(tparm(set_color_pair, 0)); 385 } 386 put_str(" "); 387 put_str(splat[d].name); 388 put_crlf(); 389 } 390 return palette_full; 391} 392 393 394static void 395ncv_display(int m) 396{ /* print the no_color_video (ncv) test line */ 397 putchp('0' + m); 398 putchp(' '); 399 eat_cookie(); 400 set_attr(1 << m); 401 sprintf(temp, "%-11s", alt_modes[m].name); 402 put_str(temp); 403 404 new_color(COLOR_BLUE, COLOR_BLACK, TRUE); 405 put_str("blue"); 406 407 new_color(COLOR_BLACK, COLOR_GREEN, TRUE); 408 put_str("green"); 409 410 new_color(COLOR_WHITE, COLOR_BLACK, TRUE); 411 put_str(alt_modes[m].name); 412 eat_cookie(); 413 set_attr(0); 414 reset_colors(); 415 put_crlf(); 416} 417 418 419static void 420dump_colors(void) 421{ /* display the colors in some esthetic 422 pattern */ 423 static int xmap[8] = {0, 3, 4, 7, 1, 2, 5, 6}; 424 int i, j, k, xi, xj, width, p, cs; 425 int found_one; 426 427 cs = color_step <= 125 ? 125 : color_step; 428 width = (1000 / cs) + 1; 429 for (xi = 0; xi < 16; xi++) { 430 i = (xi & 8) ? xi ^ 15 : xi; 431 R = i * cs; 432 if (R <= 1000) { 433 found_one = FALSE; 434 for (xj = 0; xj < 32; xj++) { 435 j = ((xj & 8) ? xj ^ 15 : xj) & 7; 436 k = xmap[((xi >> 1) & 4) + (xj >> 3)]; 437 G = j * cs; 438 B = k * cs; 439 if (G <= 1000 && B <= 1000) { 440 p = (k * width + j) * width + i; 441 if (set_a_background) { 442 if (p >= max_colors) { 443 continue; 444 } 445 send_color(p, R, G, B); 446 tc_putp(tparm(set_a_background, p)); 447 } else if (set_background) { 448 if (p >= max_colors) { 449 continue; 450 } 451 send_color(p, R, G, B); 452 tc_putp(tparm(set_background, p)); 453 } else { 454 if (p >= max_pairs) { 455 continue; 456 } 457 send_pair(p, R, G, B, R, G, B); 458 tc_putp(tparm(set_color_pair, p)); 459 } 460 found_one = TRUE; 461 putchp(' '); 462 putchp(' '); 463 } 464 } 465 if (found_one) { 466 put_crlf(); 467 } 468 } 469 } 470} 471 472/* 473** color_check(test_list, status, ch) 474** 475** test (colors) and (pairs) 476*/ 477static void 478color_check( 479 struct test_list *t, 480 int *state, 481 int *ch) 482{ 483 if (max_colors <= 0 && max_pairs <= 0) { 484 ptext("This is not a color terminal; (colors) and (pairs) are missing. "); 485 *state |= MENU_STOP; 486 } else { 487 sprintf(temp, "This terminal can display %d colors and %d color pairs. (colors) (pairs)", 488 max_colors, max_pairs); 489 ptextln(temp); 490 } 491 generic_done_message(t, state, ch); 492} 493 494/* 495** color_setf(test_list, status, ch) 496** 497** test (setf) (setb) and (scp) 498*/ 499static void 500color_setf( 501 struct test_list *t, 502 int *state, 503 int *ch) 504{ 505 int i, j; 506 507 if (max_colors <= 0 && max_pairs <= 0) { 508 ptext("This is not a color terminal; (colors) and (pairs) are missing. "); 509 generic_done_message(t, state, ch); 510 *state |= MENU_STOP; 511 return; 512 } 513 if ((set_a_foreground == NULL || set_a_background == NULL) 514 && (set_foreground == NULL || set_background == NULL) 515 && set_color_pair == NULL) { 516 ptextln("Both set foreground (setaf/setf) and set color pair (scp) are not present."); 517 if (!set_a_background || !set_background) { 518 ptextln("(setab/setb) set background not present"); 519 } 520 ptext("These must be defined for color testing. "); 521 generic_done_message(t, state, ch); 522 *state |= MENU_STOP; 523 return; 524 } 525 /* initialize the color palette */ 526 pairs_used = max_colors >= 8 ? 8 : max_colors; 527 reset_colors(); 528 new_color(COLOR_WHITE, COLOR_BLACK, FALSE); 529 530 ptextln("(setf) (setb) (scp) The following colors are predefined:"); 531 ptextln("\n Foreground Background"); 532 put_crlf(); 533 j = max_colors > 8 ? 8 : max_colors; 534 /* 535 * the black on white test is the same as the white on black test. 536 */ 537 for (i = 1; i < j; i++) { 538 putchp('0' + def_colors[i].index); 539 putchp(' '); 540 sprintf(temp, " %s ", def_colors[i].name); 541 542 new_color(def_colors[i].index, COLOR_BLACK, TRUE); 543 put_str(temp); 544 545 new_color(COLOR_BLACK, COLOR_BLACK, TRUE); 546 put_str(" "); 547 548 new_color(COLOR_BLACK, def_colors[i].index, TRUE); 549 put_str(temp); 550 551 new_color(COLOR_WHITE, COLOR_BLACK, FALSE); 552 put_crlf(); 553 } 554 reset_colors(); 555 put_crlf(); 556 generic_done_message(t, state, ch); 557} 558 559/* 560** color_matrix(test_list, status, ch) 561** 562** test (pairs) (op) 563*/ 564static void 565color_matrix( 566 struct test_list *t, 567 int *state, 568 int *ch) 569{ 570 int i, j, matrix_size, matrix_area, brightness; 571 572 matrix_size = max_colors > 8 ? 8 : max_colors; 573 574 sprintf(temp, "(pairs) There are %d color pairs.", max_pairs); 575 ptextln(temp); 576 577 for ( ; matrix_size; matrix_size--) { 578 if (matrix_size * matrix_size <= max_pairs) { 579 break; 580 } 581 } 582 matrix_area = matrix_size * matrix_size; 583 for (brightness = 0; brightness < 2; brightness++) { 584 put_crlf(); 585 sprintf(temp, 586 "%dx%d matrix of foreground/background colors, bright *o%s*", 587 matrix_size, matrix_size, brightness ? "n" : "ff"); 588 put_str(temp); 589 590 put_str("\n "); 591 for (i = 0; i < matrix_size; i++) { 592 (void) sprintf(temp, "%-8s", def_colors[i].name); 593 put_str(temp); 594 } 595 for (j = 0; j < matrix_area; j++) { 596 if (j % matrix_size == 0) { 597 reset_colors(); 598 put_crlf(); 599 if (brightness) { 600 tc_putp(exit_attribute_mode); 601 } 602 (void) sprintf(temp, "%-8s", def_colors[j / matrix_size].name); 603 put_str(temp); 604 if (brightness) { 605 put_mode(enter_bold_mode); 606 } 607 } 608 new_color(def_colors[j % matrix_size].index, 609 def_colors[j / matrix_size].index, 610 FALSE); 611 put_str(" Hello "); 612 } 613 reset_colors(); 614 if (brightness) { 615 tc_putp(exit_attribute_mode); 616 } 617 put_crlf(); 618 } 619 generic_done_message(t, state, ch); 620} 621 622/* 623** color_ncv(test_list, status, ch) 624** 625** test (ncv) 626*/ 627static void 628color_ncv( 629 struct test_list *t, 630 int *state, 631 int *ch) 632{ 633 int i; 634 635 if (no_color_video == -1) { 636 /* I have no idea what this means */ 637 return; 638 } 639 sprintf(temp, "According to no_color_video (ncv) which is %d, the following attributes should work correctly with color.", no_color_video); 640 ptextln(temp); 641 put_crlf(); 642 set_attr(0); 643 ncv_display(0); 644 for (i = 1; i <= 9; i++) { 645 if (((no_color_video >> (mode_map[i] - 1)) & 1) == 0) { 646 ncv_display(mode_map[i]); 647 } 648 } 649 if (no_color_video & 0x3ff) { 650 ptextln("\nThe following attributes should not work correctly with color. (ncv)\n"); 651 for (i = 1; i <= 9; i++) { 652 if ((no_color_video >> (mode_map[i] - 1)) & 1) { 653 ncv_display(mode_map[i]); 654 } 655 } 656 } 657 reset_colors(); 658 put_crlf(); 659 generic_done_message(t, state, ch); 660} 661 662/* 663** color_bce(test_list, status, ch) 664** 665** test (bce) background color erase 666*/ 667static void 668color_bce( 669 struct test_list *t, 670 int *state, 671 int *ch) 672{ 673 new_color(COLOR_CYAN, COLOR_BLUE, FALSE); 674 put_clear(); 675 put_newlines(2); 676 reset_colors(); 677 ptextln("If the two lines above are blue then back_color_erase (bce) should be true."); 678 sprintf(temp, "(bce) is %s in the data base.", back_color_erase ? "true" : "false"); 679 ptextln(temp); 680 generic_done_message(t, state, ch); 681} 682 683/* 684** color_ccc(test_list, status, ch) 685** 686** test (ccc) color palette test (oc) (op) (initc) (initp) 687*/ 688static void 689color_ccc( 690 struct test_list *t, 691 int *state, 692 int *ch) 693{ 694 int i, j; 695 696 if (!can_change) { 697 ptextln("Terminal can not change colors (ccc)"); 698 generic_done_message(t, state, ch); 699 return; 700 } 701 reset_colors(); 702 pairs_used = 0; 703 new_color(COLOR_WHITE, COLOR_BLACK, FALSE); 704 sprintf(temp, "Reloading colors (init%c) using %s method", 705 set_foreground ? 'c' : 'p', 706 hue_lightness_saturation ? "HLS" : "RGB"); 707 ptextln(temp); 708 put_crlf(); 709 j = max_colors > 7 ? 7 : max_colors; 710 /* redisplay the above test with reinitialized colors */ 711 /* If these colors don't look right to you... */ 712 for (i = 0; i < j; i++) { 713 sprintf(temp, " %s ", def_colors[i ^ 7].name); 714 715 new_color(i ^ 7, COLOR_BLACK, TRUE); 716 put_str(temp); 717 718 new_color(COLOR_BLACK, COLOR_BLACK, TRUE); 719 put_str(" "); 720 721 new_color(COLOR_BLACK, i ^ 7, TRUE); 722 put_str(temp); 723 724 new_color(COLOR_WHITE, COLOR_BLACK, FALSE); 725 put_crlf(); 726 } 727 generic_done_message(t, state, ch); 728 if (*ch != 0 && *ch != 'n') { 729 reset_colors(); 730 return; 731 } 732 733 pairs_used = 0; 734 cookie_monster = 0; 735 if (magic_cookie_glitch > 0) { 736 cookie_monster = 737 ((set_a_foreground || set_foreground) 738 ? magic_cookie_glitch : 0) + 739 ((set_a_background || set_background) 740 ? magic_cookie_glitch : 0) + 741 (set_color_pair ? magic_cookie_glitch : 0); 742 } 743 set_color_step(); 744 colors_per_line = max_colors > max_pairs 745 ? max_pairs : max_colors; 746 j = (columns - 14) / (cookie_monster + 1); 747 if (colors_per_line > j) { 748 colors_per_line = (j / i) * i; 749 } 750 sprintf(temp, "RGB color step %d, cookies %d", color_step, 751 cookie_monster); 752 ptextln(temp); 753 754 R = G = B = 0; 755 pairs_used = 0; 756 for (;;) { 757 if (rainbow(colors_per_line)) { 758 break; 759 } 760 } 761 generic_done_message(t, state, ch); 762 if (*ch != 0 && *ch != 'n') { 763 reset_colors(); 764 return; 765 } 766 dump_colors(); 767 reset_colors(); 768 generic_done_message(t, state, ch); 769} 770