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