color.c revision 1.30 1 /* $NetBSD: color.c,v 1.30 2006/01/15 11:43:54 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.30 2006/01/15 11:43:54 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("start_color: COLORS = %d, COLOR_PAIRS = %d",
150 COLORS, COLOR_PAIRS);
151 switch (_cursesi_screen->color_type) {
152 case COLOR_ANSI:
153 __CTRACE(" (ANSI style)\n");
154 break;
155 case COLOR_HP:
156 __CTRACE(" (HP style)\n");
157 break;
158 case COLOR_TEK:
159 __CTRACE(" (Tektronics style)\n");
160 break;
161 case COLOR_OTHER:
162 __CTRACE(" (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 ("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("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("init_color: %d, %d, %d, %d\n", color, red, green, blue);
394 #endif
395 if (color < 0 || color >= _cursesi_screen->COLORS)
396 return(ERR);
397
398 _cursesi_screen->colours[color].red = red;
399 _cursesi_screen->colours[color].green = green;
400 _cursesi_screen->colours[color].blue = blue;
401 /* XXX Not yet implemented */
402 return(ERR);
403 /* XXX: need to initialise Tek style (Ic) and support HLS */
404 }
405
406 /*
407 * color_content --
408 * Get colour red, green and blue values.
409 */
410 int
411 color_content(short color, short *redp, short *greenp, short *bluep)
412 {
413 if (color < 0 || color >= _cursesi_screen->COLORS)
414 return(ERR);
415
416 *redp = _cursesi_screen->colours[color].red;
417 *greenp = _cursesi_screen->colours[color].green;
418 *bluep = _cursesi_screen->colours[color].blue;
419 return(OK);
420 }
421
422 /*
423 * use_default_colors --
424 * Use terminal default colours instead of curses default colour.
425 */
426 int
427 use_default_colors()
428 {
429 #ifdef DEBUG
430 __CTRACE("use_default_colors\n");
431 #endif
432
433 return(assume_default_colors(-1, -1));
434 }
435
436 /*
437 * assume_default_colors --
438 * Set the default foreground and background colours.
439 */
440 int
441 assume_default_colors(short fore, short back)
442 {
443 #ifdef DEBUG
444 __CTRACE("assume_default_colors: %d, %d\n", fore, back);
445 #endif
446 /* Swap red/blue and yellow/cyan */
447 if (_cursesi_screen->color_type == COLOR_OTHER) {
448 switch (fore) {
449 case COLOR_RED:
450 fore = COLOR_BLUE;
451 break;
452 case COLOR_BLUE:
453 fore = COLOR_RED;
454 break;
455 case COLOR_YELLOW:
456 fore = COLOR_CYAN;
457 break;
458 case COLOR_CYAN:
459 fore = COLOR_YELLOW;
460 break;
461 }
462 switch (back) {
463 case COLOR_RED:
464 back = COLOR_BLUE;
465 break;
466 case COLOR_BLUE:
467 back = COLOR_RED;
468 break;
469 case COLOR_YELLOW:
470 back = COLOR_CYAN;
471 break;
472 case COLOR_CYAN:
473 back = COLOR_YELLOW;
474 break;
475 }
476 }
477 __default_pair.fore = fore;
478 __default_pair.back = back;
479 __default_pair.flags = __USED;
480
481 if (COLOR_PAIRS) {
482 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore;
483 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back;
484 _cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED;
485 }
486
487 /*
488 * If we've already called start_color(), make sure all instances
489 * of the curses default colour pair are dirty.
490 */
491 if (__using_color)
492 __change_pair(PAIR_NUMBER(__default_color));
493
494 return(OK);
495 }
496
497 /*
498 * no_color_video --
499 * Return attributes that cannot be combined with color.
500 */
501 attr_t
502 no_color_video(void)
503 {
504 return(_cursesi_screen->nca);
505 }
506
507 /*
508 * __set_color --
509 * Set terminal foreground and background colours.
510 */
511 void
512 __set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr)
513 {
514 short pair;
515
516 if ((curscr->wattr & __COLOR) == (attr & __COLOR))
517 return;
518
519 pair = PAIR_NUMBER((u_int32_t)attr);
520 #ifdef DEBUG
521 __CTRACE("__set_color: %d, %d, %d\n", pair,
522 _cursesi_screen->colour_pairs[pair].fore,
523 _cursesi_screen->colour_pairs[pair].back);
524 #endif
525 switch (_cursesi_screen->color_type) {
526 /* Set ANSI forground and background colours */
527 case COLOR_ANSI:
528 if (_cursesi_screen->colour_pairs[pair].fore < 0 ||
529 _cursesi_screen->colour_pairs[pair].back < 0)
530 __unset_color(curscr);
531 if (_cursesi_screen->colour_pairs[pair].fore >= 0)
532 tputs(__parse_cap(_cursesi_screen->tc_AF,
533 _cursesi_screen->colour_pairs[pair].fore),
534 0, __cputchar);
535 if (_cursesi_screen->colour_pairs[pair].back >= 0)
536 tputs(__parse_cap(_cursesi_screen->tc_AB,
537 _cursesi_screen->colour_pairs[pair].back),
538 0, __cputchar);
539 break;
540 case COLOR_HP:
541 /* XXX: need to support HP style */
542 break;
543 case COLOR_TEK:
544 /* XXX: need to support Tek style */
545 break;
546 case COLOR_OTHER:
547 if (_cursesi_screen->colour_pairs[pair].fore < 0 ||
548 _cursesi_screen->colour_pairs[pair].back < 0)
549 __unset_color(curscr);
550 if (_cursesi_screen->colour_pairs[pair].fore >= 0)
551 tputs(__parse_cap(_cursesi_screen->tc_Sf,
552 _cursesi_screen->colour_pairs[pair].fore),
553 0, __cputchar);
554 if (_cursesi_screen->colour_pairs[pair].back >= 0)
555 tputs(__parse_cap(_cursesi_screen->tc_Sb,
556 _cursesi_screen->colour_pairs[pair].back),
557 0, __cputchar);
558 break;
559 }
560 curscr->wattr &= ~__COLOR;
561 curscr->wattr |= attr & __COLOR;
562 }
563
564 /*
565 * __unset_color --
566 * Clear terminal foreground and background colours.
567 */
568 void
569 __unset_color(WINDOW *win)
570 {
571 #ifdef DEBUG
572 __CTRACE("__unset_color\n");
573 #endif
574 switch (_cursesi_screen->color_type) {
575 /* Clear ANSI forground and background colours */
576 case COLOR_ANSI:
577 if (__tc_op != NULL) {
578 tputs(__tc_op, 0, __cputchar);
579 win->wattr &= __mask_op;
580 }
581 break;
582 case COLOR_HP:
583 /* XXX: need to support HP style */
584 break;
585 case COLOR_TEK:
586 /* XXX: need to support Tek style */
587 break;
588 case COLOR_OTHER:
589 if (__tc_op != NULL) {
590 tputs(__tc_op, 0, __cputchar);
591 win->wattr &= __mask_op;
592 }
593 break;
594 }
595 }
596
597 /*
598 * __restore_colors --
599 * Redo color definitions after restarting 'curses' mode.
600 */
601 void
602 __restore_colors(void)
603 {
604 if (__tc_cc != 0)
605 switch (_cursesi_screen->color_type) {
606 case COLOR_HP:
607 /* XXX: need to re-initialise HP style (Ip) */
608 break;
609 case COLOR_TEK:
610 /* XXX: need to re-initialise Tek style (Ic) */
611 break;
612 }
613 }
614
615 /*
616 * __change_pair --
617 * Mark dirty all positions using pair.
618 */
619 void
620 __change_pair(short pair)
621 {
622 struct __winlist *wlp;
623 WINDOW *win;
624 int y, x;
625 __LINE *lp;
626 uint32_t cl = COLOR_PAIR(pair);
627
628
629 for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) {
630 #ifdef DEBUG
631 __CTRACE("__change_pair: win = %p\n", wlp->winp);
632 #endif
633 win = wlp->winp;
634 if (win == __virtscr)
635 continue;
636 else if (win == curscr) {
637 /* Reset colour attribute on curscr */
638 #ifdef DEBUG
639 __CTRACE("__change_pair: win == curscr\n");
640 #endif
641 for (y = 0; y < curscr->maxy; y++) {
642 lp = curscr->lines[y];
643 for (x = 0; x < curscr->maxx; x++) {
644 if ((lp->line[x].attr & __COLOR) == cl)
645 lp->line[x].attr &= ~__COLOR;
646 }
647 }
648 } else {
649 /* Mark dirty those positions with colour pair "pair" */
650 for (y = 0; y < win->maxy; y++) {
651 lp = curscr->lines[y];
652 for (x = 0; x < win->maxx; x++)
653 if ((lp->line[x].attr &
654 __COLOR) == cl) {
655 if (!(lp->flags & __ISDIRTY))
656 lp->flags |= __ISDIRTY;
657 /*
658 * firstchp/lastchp are shared
659 * between parent window and
660 * sub-window.
661 */
662 if (*lp->firstchp > x)
663 *lp->firstchp = x;
664 if (*lp->lastchp < x)
665 *lp->lastchp = x;
666 }
667 #ifdef DEBUG
668 if ((win->lines[y]->flags & __ISDIRTY))
669 __CTRACE("__change_pair: first = %d, last = %d\n", *win->lines[y]->firstchp, *win->lines[y]->lastchp);
670 #endif
671 }
672 }
673 }
674 }
675