ncurses 4.2
[ncurses.git] / ncurses / base / 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.36 1999/10/03 00:20:37 Philippe.Blain Exp $")
45
46 /*
47  * These should be screen structure members.  They need to be globals for
48  * hystorical reasons.  So we assign them in start_color() and also in
49  * set_term()'s screen-switching logic.
50  */
51 int COLOR_PAIRS;
52 int COLORS;
53
54 /*
55  * Given a RGB range of 0..1000, we'll normally set the individual values
56  * to about 2/3 of the maximum, leaving full-range for bold/bright colors.
57  */
58 #define RGB_ON  680
59 #define RGB_OFF 0
60
61 static const color_t cga_palette[] =
62 {
63     /*  R               G               B */
64         {RGB_OFF,       RGB_OFF,        RGB_OFF},       /* COLOR_BLACK */
65         {RGB_ON,        RGB_OFF,        RGB_OFF},       /* COLOR_RED */
66         {RGB_OFF,       RGB_ON,         RGB_OFF},       /* COLOR_GREEN */
67         {RGB_ON,        RGB_ON,         RGB_OFF},       /* COLOR_YELLOW */
68         {RGB_OFF,       RGB_OFF,        RGB_ON},        /* COLOR_BLUE */
69         {RGB_ON,        RGB_OFF,        RGB_ON},        /* COLOR_MAGENTA */
70         {RGB_OFF,       RGB_ON,         RGB_ON},        /* COLOR_CYAN */
71         {RGB_ON,        RGB_ON,         RGB_ON},        /* COLOR_WHITE */
72 };
73 static const color_t hls_palette[] =
74 {
75     /*  H       L       S */
76         {0,     0,      0},     /* COLOR_BLACK */
77         {120,   50,     100},   /* COLOR_RED */
78         {240,   50,     100},   /* COLOR_GREEN */
79         {180,   50,     100},   /* COLOR_YELLOW */
80         {330,   50,     100},   /* COLOR_BLUE */
81         {60,    50,     100},   /* COLOR_MAGENTA */
82         {300,   50,     100},   /* COLOR_CYAN */
83         {0,     50,     100},   /* COLOR_WHITE */
84 };
85
86 /*
87  * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly
88  * to maintain compatibility with a pre-ANSI scheme.  The same scheme is
89  * also used in the FreeBSD syscons.
90  */
91 static int toggled_colors(int c)
92 {
93     if (c < 16) {
94         static const int table[] =
95                 { 0,  4,  2,  6,  1,  5,  3,  7,
96                   8, 12, 10, 14,  9, 13, 11, 15};
97         c = table[c];
98     }
99     return c;
100 }
101
102 static void set_background_color(int bg, int  (*outc)(int))
103 {
104         if (set_a_background)
105         {
106             TPUTS_TRACE("set_a_background");
107             tputs(tparm(set_a_background, bg), 1, outc);
108         }
109         else
110         {
111             TPUTS_TRACE("set_background");
112             tputs(tparm(set_background, toggled_colors(bg)), 1, outc);
113         }
114 }
115
116 static void set_foreground_color(int fg, int  (*outc)(int))
117 {
118         if (set_a_foreground)
119         {
120             TPUTS_TRACE("set_a_foreground");
121             tputs(tparm(set_a_foreground, fg), 1, outc);
122         }
123         else
124         {
125             TPUTS_TRACE("set_foreground");
126             tputs(tparm(set_foreground, toggled_colors(fg)), 1, outc);
127         }
128 }
129
130 static bool set_original_colors(void)
131 {
132         if (orig_pair != 0) {
133                 TPUTS_TRACE("orig_pair");
134                 putp(orig_pair);
135                 return TRUE;
136         }
137         else if (orig_colors != NULL)
138         {
139                 TPUTS_TRACE("orig_colors");
140                 putp(orig_colors);
141                 return TRUE;
142         }
143         return FALSE;
144 }
145
146 int start_color(void)
147 {
148         T((T_CALLED("start_color()")));
149
150         if (set_original_colors() != TRUE)
151         {
152                 set_foreground_color(COLOR_WHITE, _nc_outch);
153                 set_background_color(COLOR_BLACK, _nc_outch);
154         }
155
156         if (max_pairs != -1)
157                 COLOR_PAIRS = SP->_pair_count = max_pairs;
158         else
159                 returnCode(ERR);
160         if ((SP->_color_pairs = typeCalloc(unsigned short, max_pairs)) == 0)
161                 returnCode(ERR);
162         SP->_color_pairs[0] = PAIR_OF(COLOR_WHITE, COLOR_BLACK);
163         if (max_colors != -1)
164                 COLORS = SP->_color_count = max_colors;
165         else
166                 returnCode(ERR);
167         SP->_coloron = 1;
168
169         if ((SP->_color_table = typeMalloc(color_t, COLORS)) == 0)
170                 returnCode(ERR);
171         if (hue_lightness_saturation)
172             memcpy(SP->_color_table, hls_palette, sizeof(color_t) * COLORS);
173         else
174             memcpy(SP->_color_table, cga_palette, sizeof(color_t) * COLORS);
175
176         T(("started color: COLORS = %d, COLOR_PAIRS = %d", COLORS, COLOR_PAIRS));
177
178         returnCode(OK);
179 }
180
181 /* This function was originally written by Daniel Weaver <danw@znyx.com> */
182 static void rgb2hls(short r, short g, short b, short *h, short *l, short *s)
183 /* convert RGB to HLS system */
184 {
185     short min, max, t;
186
187     if ((min = g < r ? g : r) > b) min = b;
188     if ((max = g > r ? g : r) < b) max = b;
189
190     /* calculate lightness */
191     *l = (min + max) / 20;
192
193     if (min == max)             /* black, white and all shades of gray */
194     {
195         *h = 0;
196         *s = 0;
197         return;
198     }
199
200     /* calculate saturation */
201     if (*l < 50)
202         *s = ((max - min) * 100) / (max + min);
203     else *s = ((max - min) * 100) / (2000 - max - min);
204
205     /* calculate hue */
206     if (r == max)
207         t = 120 + ((g - b) * 60) / (max - min);
208     else
209         if (g == max)
210             t = 240 + ((b - r) * 60) / (max - min);
211         else
212             t = 360 + ((r - g) * 60) / (max - min);
213
214     *h = t % 360;
215 }
216
217 /*
218  * Extension (1997/1/18) - Allow negative f/b values to set default color
219  * values.
220  */
221 int init_pair(short pair, short f, short b)
222 {
223         unsigned result;
224
225         T((T_CALLED("init_pair(%d,%d,%d)"), pair, f, b));
226
227         if ((pair < 1) || (pair >= COLOR_PAIRS))
228                 returnCode(ERR);
229         if (SP->_default_color)
230         {
231                 if (f < 0)
232                         f = C_MASK;
233                 if (b < 0)
234                         b = C_MASK;
235                 if (f >= COLORS && f != C_MASK)
236                         returnCode(ERR);
237                 if (b >= COLORS && b != C_MASK)
238                         returnCode(ERR);
239         }
240         else
241         if ((f < 0) || (f >= COLORS)
242          || (b < 0) || (b >= COLORS))
243                 returnCode(ERR);
244
245         /*
246          * When a pair's content is changed, replace its colors (if pair was
247          * initialized before a screen update is performed replacing original
248          * pair colors with the new ones).
249          */
250         result = PAIR_OF(f,b);
251         if (SP->_color_pairs[pair] != 0
252          && SP->_color_pairs[pair] != result) {
253             int y, x;
254             attr_t z = COLOR_PAIR(pair);
255
256             for (y = 0; y <= curscr->_maxy; y++) {
257                 struct ldat *ptr = &(curscr->_line[y]);
258                 bool changed = FALSE;
259                 for (x = 0; x <= curscr->_maxx; x++) {
260                     if ((ptr->text[x] & A_COLOR) == z) {
261                         /* Set the old cell to zero to ensure it will be
262                            updated on the next doupdate() */
263                         ptr->text[x] = 0;
264                         CHANGED_CELL(ptr,x);
265                         changed = TRUE;
266                     }
267                 }
268                 if (changed)
269                         _nc_make_oldhash(y);
270             }
271         }
272         SP->_color_pairs[pair] = result;
273
274         if (initialize_pair)
275         {
276             const color_t *tp = hue_lightness_saturation ? hls_palette : cga_palette;
277
278             T(("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)",
279                pair,
280                tp[f].red, tp[f].green, tp[f].blue,
281                tp[b].red, tp[b].green, tp[b].blue));
282
283             if (initialize_pair)
284             {
285                 TPUTS_TRACE("initialize_pair");
286                 putp(tparm(initialize_pair,
287                             pair,
288                             tp[f].red, tp[f].green, tp[f].blue,
289                             tp[b].red, tp[b].green, tp[b].blue));
290             }
291         }
292
293         returnCode(OK);
294 }
295
296 int init_color(short color, short r, short g, short b)
297 {
298         T((T_CALLED("init_color(%d,%d,%d,%d)"), color, r, g, b));
299
300         if (initialize_color == NULL)
301                 returnCode(ERR);
302
303         if (color < 0 || color >= COLORS)
304                 returnCode(ERR);
305         if (r < 0 || r > 1000 || g < 0 ||  g > 1000 || b < 0 || b > 1000)
306                 returnCode(ERR);
307
308         if (hue_lightness_saturation)
309             rgb2hls(r, g, b,
310                       &SP->_color_table[color].red,
311                       &SP->_color_table[color].green,
312                       &SP->_color_table[color].blue);
313         else
314         {
315                 SP->_color_table[color].red = r;
316                 SP->_color_table[color].green = g;
317                 SP->_color_table[color].blue = b;
318         }
319
320         if (initialize_color)
321         {
322                 TPUTS_TRACE("initialize_color");
323                 putp(tparm(initialize_color, color, r, g, b));
324         }
325         returnCode(OK);
326 }
327
328 bool can_change_color(void)
329 {
330         T((T_CALLED("can_change_color()")));
331         returnCode ((can_change != 0) ? TRUE : FALSE);
332 }
333
334 bool has_colors(void)
335 {
336         T((T_CALLED("has_colors()")));
337         returnCode (((max_colors != -1) && (max_pairs != -1)
338                      && (((set_foreground != NULL)
339                           && (set_background != NULL))
340                          || ((set_a_foreground != NULL)
341                              && (set_a_background != NULL))
342                          || set_color_pair)) ? TRUE : FALSE);
343 }
344
345 int color_content(short color, short *r, short *g, short *b)
346 {
347     T((T_CALLED("color_content(%d,%p,%p,%p)"), color, r, g, b));
348     if (color < 0 || color >= COLORS)
349         returnCode(ERR);
350
351     if (r) *r = SP->_color_table[color].red;
352     if (g) *g = SP->_color_table[color].green;
353     if (b) *b = SP->_color_table[color].blue;
354     returnCode(OK);
355 }
356
357 int pair_content(short pair, short *f, short *b)
358 {
359         T((T_CALLED("pair_content(%d,%p,%p)"), pair, f, b));
360
361         if ((pair < 0) || (pair >= COLOR_PAIRS))
362                 returnCode(ERR);
363         if (f) *f = ((SP->_color_pairs[pair] >> C_SHIFT) & C_MASK);
364         if (b) *b =  (SP->_color_pairs[pair] & C_MASK);
365
366         returnCode(OK);
367 }
368
369 void _nc_do_color(int pair, bool reverse, int (*outc)(int))
370 {
371     short fg, bg;
372
373     if (pair == 0)
374     {
375         if (orig_pair)
376         {
377             TPUTS_TRACE("orig_pair");
378             tputs(orig_pair, 1, outc);
379         }
380         else if (set_color_pair)
381         {
382             TPUTS_TRACE("set_color_pair");
383             tputs(tparm(set_color_pair, pair), 1, outc);
384         }
385         else
386         {
387             set_foreground_color(COLOR_WHITE, outc);
388             set_background_color(COLOR_BLACK, outc);
389         }
390     }
391     else
392     {
393         if (set_color_pair)
394         {
395             TPUTS_TRACE("set_color_pair");
396             tputs(tparm(set_color_pair, pair), 1, outc);
397         }
398         else
399         {
400             pair_content(pair, &fg, &bg);
401             if (reverse) {
402                 short xx = fg;
403                 fg = bg;
404                 bg = xx;
405             }
406
407             T(("setting colors: pair = %d, fg = %d, bg = %d", pair, fg, bg));
408
409             if (fg == C_MASK || bg == C_MASK)
410             {
411                 if (set_original_colors() != TRUE)
412                 {
413                         if (fg == C_MASK)
414                                 set_foreground_color(COLOR_WHITE, outc);
415                         if (bg == C_MASK)
416                                 set_background_color(COLOR_BLACK, outc);
417                 }
418             }
419             if (fg != C_MASK)
420             {
421                 set_foreground_color(fg, outc);
422             }
423             if (bg != C_MASK)
424             {
425                 set_background_color(bg, outc);
426             }
427         }
428     }
429 }