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