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