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