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