xref: /netbsd-src/lib/libcurses/color.c (revision bcc8ec9959e7b01e313d813067bfb43a3ad70551)
1 /*	$NetBSD: color.c,v 1.14 2001/01/05 22:57:56 christos 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.14 2001/01/05 22:57:56 christos Exp $");
42 #endif				/* not lint */
43 
44 #include "curses.h"
45 #include "curses_private.h"
46 
47 /* the following is defined and set up in setterm.c */
48 extern struct tinfo *_cursesi_genbuf;
49 
50 /* Maximum colours */
51 #define	MAX_COLORS	64
52 /* Maximum colour pairs - determined by number of colour bits in attr_t */
53 #define	MAX_PAIRS	64	/* PAIR_NUMBER(__COLOR) + 1 */
54 
55 /* Flags for colours and pairs */
56 #define	__USED		0x01
57 
58 /* List of colours */
59 struct color {
60 	short	num;
61 	short	red;
62 	short	green;
63 	short	blue;
64 	int	flags;
65 } colors[MAX_COLORS];
66 
67 /* List of colour pairs */
68 struct pair {
69 	short	fore;
70 	short	back;
71 	int	flags;
72 } pairs[MAX_PAIRS];
73 
74 /* Attributes that clash with colours */
75 attr_t	__nca;
76 
77 /* Style of colour manipulation */
78 #define COLOR_NONE	0
79 #define COLOR_ANSI	1	/* ANSI/DEC-style colour manipulation */
80 #define COLOR_HP	2	/* HP-style colour manipulation */
81 #define COLOR_TEK	3	/* Tektronix-style colour manipulation */
82 #define COLOR_OTHER	4	/* None of the others but can set fore/back */
83 int	__color_type = COLOR_NONE;
84 
85 static void
86 __change_pair __P((short));
87 /*
88  * has_colors --
89  *	Check if terminal has colours.
90  */
91 bool
92 has_colors(void)
93 {
94 	if (__tc_Co > 0 && __tc_pa > 0 && ((__tc_AF != NULL &&
95 	    __tc_AB != NULL) || __tc_Ip != NULL || __tc_Ic != NULL ||
96 	    (__tc_Sb != NULL && __tc_Sf != NULL)))
97 		return(TRUE);
98 	else
99 		return(FALSE);
100 }
101 
102 /*
103  * can_change_colors --
104  *	Check if terminal can change colours.
105  */
106 bool
107 can_change_colors(void)
108 {
109 	if (__tc_cc)
110 		return(TRUE);
111 	else
112 		return(FALSE);
113 }
114 
115 /*
116  * start_color --
117  *	Initialise colour support.
118  */
119 int
120 start_color(void)
121 {
122 	int	i;
123 	attr_t	temp_nc;
124 
125 	if (has_colors() == FALSE)
126 		return(ERR);
127 
128 	/* Max colours and colour pairs */
129 	if (__tc_Co == -1)
130 		COLORS = 0;
131 	else {
132 		COLORS = __tc_Co > MAX_COLORS ? MAX_COLORS : __tc_Co;
133 		if (__tc_pa == -1) {
134 			COLOR_PAIRS = 0;
135 			COLORS = 0;
136 		} else {
137 			COLOR_PAIRS = __tc_pa > MAX_PAIRS ? MAX_PAIRS : __tc_pa;
138 		}
139 	}
140 	if (!COLORS)
141 		return (ERR);
142 
143 	/* Reset terminal colour and colour pairs. */
144 	if (__tc_oc != NULL)
145 		tputs(__tc_oc, 0, __cputchar);
146 	if (__tc_op != NULL) {
147 		tputs(__tc_op, 0, __cputchar);
148 		curscr->wattr &= __mask_op;
149 	}
150 
151 	/* Type of colour manipulation - ANSI/TEK/HP/other */
152 	if (__tc_AF != NULL && __tc_AB != NULL)
153 		__color_type = COLOR_ANSI;
154 	else if (__tc_Ip != NULL)
155 		__color_type = COLOR_HP;
156 	else if (__tc_Ic != NULL)
157 		__color_type = COLOR_TEK;
158 	else if (__tc_Sb != NULL && __tc_Sf != NULL)
159 		__color_type = COLOR_OTHER;
160 	else
161 		return(ERR);		/* Unsupported colour method */
162 
163 #ifdef DEBUG
164 	__CTRACE("start_color: COLORS = %d, COLOR_PAIRS = %d",
165 	    COLORS, COLOR_PAIRS);
166 	switch (__color_type) {
167 	case COLOR_ANSI:
168 		__CTRACE(" (ANSI style)\n");
169 		break;
170 	case COLOR_HP:
171 		__CTRACE(" (HP style)\n");
172 		break;
173 	case COLOR_TEK:
174 		__CTRACE(" (Tektronics style)\n");
175 		break;
176 	case COLOR_OTHER:
177 		__CTRACE(" (Other style)\n");
178 		break;
179 	}
180 #endif
181 
182 	/*
183 	 * Attributes that cannot be used with color.
184 	 * Store these in an attr_t for wattrset()/wattron().
185 	 */
186 	__nca = __NORMAL;
187 	if (__tc_NC != -1) {
188 		temp_nc = (attr_t) t_getnum(_cursesi_genbuf, "NC");
189 		if (temp_nc & 0x0001)
190 			__nca |= __STANDOUT;
191 		if (temp_nc & 0x0002)
192 			__nca |= __UNDERSCORE;
193 		if (temp_nc & 0x0004)
194 			__nca |= __REVERSE;
195 		if (temp_nc & 0x0008)
196 			__nca |= __BLINK;
197 		if (temp_nc & 0x0010)
198 			__nca |= __DIM;
199 		if (temp_nc & 0x0020)
200 			__nca |= __BOLD;
201 		if (temp_nc & 0x0040)
202 			__nca |= __BLANK;
203 		if (temp_nc & 0x0080)
204 			__nca |= __PROTECT;
205 		if (temp_nc & 0x0100)
206 			__nca |= __ALTCHARSET;
207 	}
208 #ifdef DEBUG
209 	__CTRACE ("start_color: __nca = %d\n", __nca);
210 #endif
211 
212 	/* Set up initial 8 colours */
213 	if (COLORS >= COLOR_BLACK)
214 		(void) init_color(COLOR_BLACK, 0, 0, 0);
215 	if (COLORS >= COLOR_RED)
216 		(void) init_color(COLOR_RED, 1000, 0, 0);
217 	if (COLORS >= COLOR_GREEN)
218 		(void) init_color(COLOR_GREEN, 0, 1000, 0);
219 	if (COLORS >= COLOR_YELLOW)
220 		(void) init_color(COLOR_YELLOW, 1000, 1000, 0);
221 	if (COLORS >= COLOR_BLUE)
222 		(void) init_color(COLOR_BLUE, 0, 0, 1000);
223 	if (COLORS >= COLOR_MAGENTA)
224 		(void) init_color(COLOR_MAGENTA, 1000, 0, 1000);
225 	if (COLORS >= COLOR_CYAN)
226 		(void) init_color(COLOR_CYAN, 0, 1000, 1000);
227 	if (COLORS >= COLOR_WHITE)
228 		(void) init_color(COLOR_WHITE, 1000, 1000, 1000);
229 
230 	/* Initialise other colours */
231 	for (i = 8; i < COLORS; i++) {
232 		colors[i].red = 0;
233 		colors[i].green = 0;
234 		colors[i].blue = 0;
235 		colors[i].flags = 0;
236 	}
237 
238 	/* Initialise colour pairs to default (white on black) */
239 	for (i = 0; i < COLOR_PAIRS; i++) {
240 		pairs[i].fore = COLOR_WHITE;
241 		pairs[i].back = COLOR_BLACK;
242 		pairs[i].flags = 0;
243 	}
244 
245 	return(OK);
246 }
247 
248 /*
249  * init_pair --
250  *	Set pair foreground and background colors.
251  */
252 int
253 init_pair(short pair, short fore, short back)
254 {
255 	int	changed;
256 
257 #ifdef DEBUG
258 	__CTRACE("init_pair: %d, %d, %d\n", pair, fore, back);
259 #endif
260 
261 	if (pair < 0 || pair >= COLOR_PAIRS)
262 		return (ERR);
263 	if (fore < 0 || fore >= COLORS)
264 		return (ERR);
265 	if (back < 0 || back >= COLORS)
266 		return (ERR);
267 
268 	if ((pairs[pair].flags & __USED) && (fore != pairs[pair].fore ||
269 	    back != pairs[pair].back))
270 		changed = 1;
271 	else
272 		changed = 0;
273 
274 	pairs[pair].flags |= __USED;
275 	pairs[pair].fore = fore;
276 	pairs[pair].back = back;
277 
278 	/* XXX: need to initialise HP style (Ip) */
279 
280 	if (changed)
281 		__change_pair(pair);
282 	return (OK);
283 }
284 
285 /*
286  * pair_content --
287  *	Get pair foreground and background colours.
288  */
289 int
290 pair_content(short pair, short *forep, short *backp)
291 {
292 	if (pair < 0 || pair >= COLOR_PAIRS)
293 		return(ERR);
294 
295 	*forep = pairs[pair].fore;
296 	*backp = pairs[pair].back;
297 	return(OK);
298 }
299 
300 /*
301  * init_color --
302  *	Set colour red, green and blue values.
303  */
304 int
305 init_color(short color, short red, short green, short blue)
306 {
307 #ifdef DEBUG
308 	__CTRACE("init_color: %d, %d, %d, %d\n", color, red, green, blue);
309 #endif
310 	if (color < 0 || color >= COLORS)
311 		return(ERR);
312 
313 	colors[color].red = red;
314 	colors[color].green = green;
315 	colors[color].blue = blue;
316 	/* XXX Not yet implemented */
317 	return(ERR);
318 	/* XXX: need to initialise Tek style (Ic) and support HLS */
319 }
320 
321 /*
322  * color_content --
323  *	Get colour red, green and blue values.
324  */
325 int
326 color_content(short color, short *redp, short *greenp, short *bluep)
327 {
328 	if (color < 0 || color >= COLORS)
329 		return(ERR);
330 
331 	*redp = colors[color].red;
332 	*greenp = colors[color].green;
333 	*bluep = colors[color].blue;
334 	return(OK);
335 }
336 
337 /*
338  * __set_color --
339  *	Set terminal foreground and background colours.
340  */
341 void
342 __set_color(attr_t attr)
343 {
344 	short	pair;
345 
346 	pair = PAIR_NUMBER((u_int32_t)attr);
347 #ifdef DEBUG
348 	__CTRACE("__set_color: %d, %d, %d\n", pair, pairs[pair].fore,
349 	    pairs[pair].back);
350 #endif
351 	switch (__color_type) {
352 	/* Set ANSI forground and background colours */
353 	case COLOR_ANSI:
354 		tputs(__parse_cap(__tc_AF, pairs[pair].fore), 0, __cputchar);
355 		tputs(__parse_cap(__tc_AB, pairs[pair].back), 0, __cputchar);
356 		break;
357 	case COLOR_HP:
358 		/* XXX: need to support HP style */
359 		break;
360 	case COLOR_TEK:
361 		/* XXX: need to support Tek style */
362 		break;
363 	case COLOR_OTHER:
364 		tputs(__parse_cap(__tc_Sf, pairs[pair].fore), 0, __cputchar);
365 		tputs(__parse_cap(__tc_Sb, pairs[pair].back), 0, __cputchar);
366 		break;
367 	}
368 }
369 
370 /*
371  * __restore_colors --
372  *	Redo color definitions after restarting 'curses' mode.
373  */
374 void
375 __restore_colors(void)
376 {
377 	if (__tc_cc != NULL)
378 		switch (__color_type) {
379 		case COLOR_HP:
380 			/* XXX: need to re-initialise HP style (Ip) */
381 			break;
382 		case COLOR_TEK:
383 			/* XXX: need to re-initialise Tek style (Ic) */
384 			break;
385 		}
386 }
387 
388 /*
389  * __change_pair --
390  *	Mark dirty all positions using pair.
391  */
392 void
393 __change_pair(short pair)
394 {
395 	struct __winlist	*wlp;
396 	WINDOW			*win;
397 	int			 y, x;
398 
399 
400 	for (wlp = __winlistp; wlp != NULL; wlp = wlp->nextp) {
401 #ifdef DEBUG
402 		__CTRACE("__change_pair: win = %0.2o\n", wlp->winp);
403 #endif
404 		if (wlp->winp == curscr) {
405 			/* Reset colour attribute on curscr */
406 #ifdef DEBUG
407 			__CTRACE("__change_pair: win == curscr\n");
408 #endif
409 			for (y = 0; y < curscr->maxy; y++)
410 				for (x = 0; x < curscr->maxx; x++)
411 					if ((curscr->lines[y]->line[x].attr &
412 					    __COLOR) == COLOR_PAIR(pair))
413 						curscr->lines[y]->line[x].attr
414 						    &= ~__COLOR;
415 		} else {
416 			/* Mark dirty those positions with color pair "pair" */
417 			win = wlp->winp;
418 			for (y = 0; y < win->maxy; y++) {
419 				for (x = 0; x < win->maxx; x++)
420 					if ((win->lines[y]->line[x].attr &
421 					    __COLOR) == COLOR_PAIR(pair)) {
422 						if (!(win->lines[y]->flags &
423 						    __ISDIRTY))
424 							win->lines[y]->flags |=
425 							    __ISDIRTY;
426 						/*
427 						 * firstchp/lastchp are shared
428 						 * between parent window and
429 						 * sub-window.
430 						 */
431 						if (*win->lines[y]->firstchp >
432 						    x)
433 							*win->lines[y]->firstchp
434 							    = x;
435 						if (*win->lines[y]->lastchp < x)
436 							*win->lines[y]->lastchp
437 							    = x;
438 					}
439 #ifdef DEBUG
440 				if ((win->lines[y]->flags & __ISDIRTY))
441 					__CTRACE("__change_pair: first = %d, last = %d\n", *win->lines[y]->firstchp, *win->lines[y]->lastchp);
442 #endif
443 			}
444 		}
445 	}
446 }
447