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