ncurses 4.2
[ncurses.git] / ncurses / lib_color.c
1 /****************************************************************************
2  * Copyright (c) 1998 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  ****************************************************************************/
33
34 /* lib_color.c
35  *
36  * Handles color emulation of SYS V curses
37  *
38  */
39
40 #include <curses.priv.h>
41
42 #include <term.h>
43
44 MODULE_ID("$Id: lib_color.c,v 1.24 1998/02/11 12:13:58 tom Exp $")
45
46 /*
47  * Only 8 ANSI colors are defined; the ISO 6429 control sequences work only
48  * for 8 values (0-7).
49  */
50 #define MAX_ANSI_COLOR 8
51
52 /*
53  * These should be screen structure members.  They need to be globals for
54  * hystorical reasons.  So we assign them in start_color() and also in
55  * set_term()'s screen-switching logic.
56  */
57 int COLOR_PAIRS;
58 int COLORS;
59
60 /*
61  * Given a RGB range of 0..1000, we'll normally set the individual values
62  * to about 2/3 of the maximum, leaving full-range for bold/bright colors.
63  */
64 #define RGB_ON  680
65 #define RGB_OFF 0
66
67 static const color_t cga_palette[] =
68 {
69     /*  R               G               B */
70         {RGB_OFF,       RGB_OFF,        RGB_OFF},       /* COLOR_BLACK */
71         {RGB_ON,        RGB_OFF,        RGB_OFF},       /* COLOR_RED */
72         {RGB_OFF,       RGB_ON,         RGB_OFF},       /* COLOR_GREEN */
73         {RGB_ON,        RGB_ON,         RGB_OFF},       /* COLOR_YELLOW */
74         {RGB_OFF,       RGB_OFF,        RGB_ON},        /* COLOR_BLUE */
75         {RGB_ON,        RGB_OFF,        RGB_ON},        /* COLOR_MAGENTA */
76         {RGB_OFF,       RGB_ON,         RGB_ON},        /* COLOR_CYAN */
77         {RGB_ON,        RGB_ON,         RGB_ON},        /* COLOR_WHITE */
78 };
79 static const color_t hls_palette[] =
80 {
81     /*  H       L       S */
82         {0,     0,      0},     /* COLOR_BLACK */
83         {120,   50,     100},   /* COLOR_RED */
84         {240,   50,     100},   /* COLOR_GREEN */
85         {180,   50,     100},   /* COLOR_YELLOW */
86         {330,   50,     100},   /* COLOR_BLUE */
87         {60,    50,     100},   /* COLOR_MAGENTA */
88         {300,   50,     100},   /* COLOR_CYAN */
89         {0,     50,     100},   /* COLOR_WHITE */
90 };
91
92 int start_color(void)
93 {
94         T((T_CALLED("start_color()")));
95
96 #ifdef orig_pair
97         if (orig_pair != NULL)
98         {
99                 TPUTS_TRACE("orig_pair");
100                 putp(orig_pair);
101         }
102 #endif /* orig_pair */
103 #ifdef orig_colors
104         if (orig_colors != NULL)
105         {
106                 TPUTS_TRACE("orig_colors");
107                 putp(orig_colors);
108         }
109 #endif /* orig_colors */
110 #if defined(orig_pair) && defined(orig_colors)
111         if (!orig_pair && !orig_colors)
112                 returnCode(ERR);
113 #endif /* defined(orig_pair) && defined(orig_colors) */
114         if (max_pairs != -1)
115                 COLOR_PAIRS = SP->_pair_count = max_pairs;
116         else
117                 returnCode(ERR);
118         SP->_color_pairs = typeCalloc(unsigned short, max_pairs);
119         SP->_color_pairs[0] = PAIR_OF(COLOR_WHITE, COLOR_BLACK);
120         if (max_colors != -1)
121                 COLORS = SP->_color_count = max_colors;
122         else
123                 returnCode(ERR);
124         SP->_coloron = 1;
125
126         SP->_color_table = malloc(sizeof(color_t) * COLORS);
127 #ifdef hue_lightness_saturation
128         if (hue_lightness_saturation)
129             memcpy(SP->_color_table, hls_palette, sizeof(color_t) * COLORS);
130         else
131 #endif /* hue_lightness_saturation */
132             memcpy(SP->_color_table, cga_palette, sizeof(color_t) * COLORS);
133
134         if (orig_colors)
135         {
136             TPUTS_TRACE("orig_colors");
137             putp(orig_colors);
138         }
139
140         T(("started color: COLORS = %d, COLOR_PAIRS = %d", COLORS, COLOR_PAIRS));
141
142         returnCode(OK);
143 }
144
145 #ifdef hue_lightness_saturation
146 /* This function was originally written by Daniel Weaver <danw@znyx.com> */
147 static void rgb2hls(short r, short g, short b, short *h, short *l, short *s)
148 /* convert RGB to HLS system */
149 {
150     short min, max, t;
151     
152     if ((min = g < r ? g : r) > b) min = b;
153     if ((max = g > r ? g : r) < b) max = b;
154
155     /* calculate lightness */
156     *l = (min + max) / 20;
157
158     if (min == max)             /* black, white and all shades of gray */
159     {
160         *h = 0;
161         *s = 0;
162         return;
163     }
164
165     /* calculate saturation */
166     if (*l < 50)
167         *s = ((max - min) * 100) / (max + min);
168     else *s = ((max - min) * 100) / (2000 - max - min);
169
170     /* calculate hue */
171     if (r == max)
172         t = 120 + ((g - b) * 60) / (max - min);
173     else
174         if (g == max)
175             t = 240 + ((b - r) * 60) / (max - min);
176         else
177             t = 360 + ((r - g) * 60) / (max - min);
178
179     *h = t % 360;
180 }
181 #endif /* hue_lightness_saturation */
182
183 /*
184  * Extension (1997/1/18) - Allow negative f/b values to set default color
185  * values.
186  */
187 int init_pair(short pair, short f, short b)
188 {
189         T((T_CALLED("init_pair(%d,%d,%d)"), pair, f, b));
190
191         if ((pair < 1) || (pair >= COLOR_PAIRS))
192                 returnCode(ERR);
193         if (SP->_default_color)
194         {
195                 if (f < 0)
196                         f = C_MASK;
197                 if (b < 0)
198                         b = C_MASK;
199                 if (f >= COLORS && f != C_MASK)
200                         returnCode(ERR);
201                 if (b >= COLORS && b != C_MASK)
202                         returnCode(ERR);
203         }
204         else
205         if ((f < 0) || (f >= COLORS)
206          || (b < 0) || (b >= COLORS))
207                 returnCode(ERR);
208
209         /*
210          * FIXME: when a pair's content is changed, replace its colors
211          * (if pair was initialized before a screen update is performed
212          * replacing original pair colors with the new ones)
213          */
214
215         SP->_color_pairs[pair] = PAIR_OF(f,b);
216
217         if (initialize_pair)
218         {
219             const color_t       *tp = hue_lightness_saturation ? hls_palette : cga_palette;
220
221             T(("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)",
222                pair,
223                tp[f].red, tp[f].green, tp[f].blue,
224                tp[b].red, tp[b].green, tp[b].blue));
225
226             if (initialize_pair)
227             {
228                 TPUTS_TRACE("initialize_pair");
229                 putp(tparm(initialize_pair,
230                             pair,
231                             tp[f].red, tp[f].green, tp[f].blue,
232                             tp[b].red, tp[b].green, tp[b].blue));
233             }
234         }
235
236         returnCode(OK);
237 }
238
239 int init_color(short color, short r, short g, short b)
240 {
241         T((T_CALLED("init_color(%d,%d,%d,%d)"), color, r, g, b));
242 #ifdef initialize_color
243         if (initialize_color == NULL)
244                 returnCode(ERR);
245 #endif /* initialize_color */
246
247         if (color < 0 || color >= COLORS)
248                 returnCode(ERR);
249         if (r < 0 || r > 1000 || g < 0 ||  g > 1000 || b < 0 || b > 1000)
250                 returnCode(ERR);
251
252 #ifdef hue_lightness_saturation
253         if (hue_lightness_saturation)
254             rgb2hls(r, g, b,
255                       &SP->_color_table[color].red,
256                       &SP->_color_table[color].green,
257                       &SP->_color_table[color].blue);
258         else
259 #endif /* hue_lightness_saturation */
260         {
261                 SP->_color_table[color].red = r;
262                 SP->_color_table[color].green = g;
263                 SP->_color_table[color].blue = b;
264         }
265
266 #ifdef initialize_color
267         if (initialize_color)
268         {
269                 TPUTS_TRACE("initialize_color");
270                 putp(tparm(initialize_color, color, r, g, b));
271         }
272 #endif /* initialize_color */
273         returnCode(OK);
274 }
275
276 bool can_change_color(void)
277 {
278         T((T_CALLED("can_change_color()")));
279         returnCode ((can_change != 0) ? TRUE : FALSE);
280 }
281
282 bool has_colors(void)
283 {
284         T((T_CALLED("has_colors()")));
285         returnCode (((orig_pair != NULL || orig_colors != NULL)
286                      && (max_colors != -1) && (max_pairs != -1)
287                      && (((set_foreground != NULL)
288                           && (set_background != NULL))
289                          || ((set_a_foreground != NULL)
290                              && (set_a_background != NULL))
291                          || set_color_pair)) ? TRUE : FALSE);
292 }
293
294 int color_content(short color, short *r, short *g, short *b)
295 {
296     T((T_CALLED("color_content(%d,%p,%p,%p)"), color, r, g, b));
297     if (color < 0 || color > COLORS)
298         returnCode(ERR);
299
300     if (r) *r = SP->_color_table[color].red;
301     if (g) *g = SP->_color_table[color].green;
302     if (b) *b = SP->_color_table[color].blue;
303     returnCode(OK);
304 }
305
306 int pair_content(short pair, short *f, short *b)
307 {
308         T((T_CALLED("pair_content(%d,%p,%p)"), pair, f, b));
309
310         if ((pair < 0) || (pair > COLOR_PAIRS))
311                 returnCode(ERR);
312         if (f) *f = ((SP->_color_pairs[pair] >> C_SHIFT) & C_MASK);
313         if (b) *b =  (SP->_color_pairs[pair] & C_MASK);
314
315         returnCode(OK);
316 }
317
318 /*
319  * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly
320  * to maintain compatibility with a pre-ANSI scheme.  The same scheme is
321  * also used in the FreeBSD syscons.
322  */
323 static int toggled_colors(int c)
324 {
325     if (c < 16) {
326         static const int table[] =
327                 { 0,  4,  2,  6,  1,  5,  3,  7,
328                   8, 12, 10, 14,  9, 13, 11, 15};
329         c = table[c];
330     }
331     return c;
332 }
333
334 void _nc_do_color(int pair, bool reverse, int  (*outc)(int))
335 {
336     short fg, bg;
337
338     if (reverse)
339         pair = -pair;
340
341     if (pair == 0)
342     {
343         if (orig_pair)
344         {
345             TPUTS_TRACE("orig_pair");
346             tputs(orig_pair, 1, outc);
347         }
348     }
349     else
350     {
351         if (set_color_pair)
352         {
353             TPUTS_TRACE("set_color_pair");
354             tputs(tparm(set_color_pair, pair), 1, outc);
355         }
356         else
357         {
358             pair_content(pair, &fg, &bg);
359             if (reverse) {
360                 short xx = fg;
361                 fg = bg;
362                 bg = xx;
363             }
364
365             T(("setting colors: pair = %d, fg = %d, bg = %d", pair, fg, bg));
366
367             if (fg == C_MASK || bg == C_MASK)
368             {
369                 if (orig_pair)
370                 {
371                     TPUTS_TRACE("orig_pair");
372                     tputs(orig_pair, 1, outc);
373                 }
374                 else
375                 {
376                     TPUTS_TRACE("orig_colors");
377                     tputs(orig_colors, 1, outc);
378                 }
379             }
380             if (fg != C_MASK)
381             {
382                 if (set_a_foreground && fg <= MAX_ANSI_COLOR)
383                 {
384                     TPUTS_TRACE("set_a_foreground");
385                     tputs(tparm(set_a_foreground, fg), 1, outc);
386                 }
387                 else
388                 {
389                     TPUTS_TRACE("set_foreground");
390                     tputs(tparm(set_foreground, toggled_colors(fg)), 1, outc);
391                 }
392             }
393             if (bg != C_MASK)
394             {
395                 if (set_a_background && bg <= MAX_ANSI_COLOR)
396                 {
397                     TPUTS_TRACE("set_a_background");
398                     tputs(tparm(set_a_background, bg), 1, outc);
399                 }
400                 else
401                 {
402                     TPUTS_TRACE("set_background");
403                     tputs(tparm(set_background, toggled_colors(bg)), 1, outc);
404                 }
405             }
406         }
407     }
408 }