Home | History | Annotate | Line # | Download | only in libcurses
color.c revision 1.20
      1 /*	$NetBSD: color.c,v 1.20 2002/10/22 11:37:34 blymn 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.20 2002/10/22 11:37:34 blymn Exp $");
     42 #endif				/* not lint */
     43 
     44 #include "curses.h"
     45 #include "curses_private.h"
     46 
     47 /* Have we initialised colours? */
     48 int	__using_color = 0;
     49 
     50 /* Default colour number */
     51 attr_t	__default_color = 0;
     52 
     53 /* Default colour pair values - white on black. */
     54 struct __pair	__default_pair = {COLOR_WHITE, COLOR_BLACK, 0};
     55 
     56 /* Default colour values */
     57 /* Flags for colours and pairs */
     58 #define	__USED		0x01
     59 
     60 /* Attributes that clash with colours */
     61 attr_t	__nca;
     62 
     63 static void
     64 __change_pair(short);
     65 
     66 /*
     67  * has_colors --
     68  *	Check if terminal has colours.
     69  */
     70 bool
     71 has_colors(void)
     72 {
     73 	if (__tc_Co > 0 && __tc_pa > 0 && ((__tc_AF != NULL &&
     74 	    __tc_AB != NULL) || __tc_Ip != NULL || __tc_Ic != NULL ||
     75 	    (__tc_Sb != NULL && __tc_Sf != NULL)))
     76 		return(TRUE);
     77 	else
     78 		return(FALSE);
     79 }
     80 
     81 /*
     82  * can_change_colors --
     83  *	Check if terminal can change colours.
     84  */
     85 bool
     86 can_change_colors(void)
     87 {
     88 	if (__tc_cc)
     89 		return(TRUE);
     90 	else
     91 		return(FALSE);
     92 }
     93 
     94 /*
     95  * start_color --
     96  *	Initialise colour support.
     97  */
     98 int
     99 start_color(void)
    100 {
    101 	int			 i;
    102 	attr_t			 temp_nc;
    103 	struct __winlist	*wlp;
    104 	WINDOW			*win;
    105 	int			 y, x;
    106 
    107 	if (has_colors() == FALSE)
    108 		return(ERR);
    109 
    110 	/* Max colours and colour pairs */
    111 	if (__tc_Co == -1)
    112 		COLORS = 0;
    113 	else {
    114 		COLORS = __tc_Co > MAX_COLORS ? MAX_COLORS : __tc_Co;
    115 		if (__tc_pa == -1) {
    116 			COLOR_PAIRS = 0;
    117 			COLORS = 0;
    118 		} else {
    119 			COLOR_PAIRS = (__tc_pa > MAX_PAIRS ?
    120 			    MAX_PAIRS : __tc_pa) - 1;
    121 			 /* Use the last colour pair for curses default. */
    122 			__default_color = COLOR_PAIR(COLOR_PAIRS);
    123 		}
    124 	}
    125 	if (!COLORS)
    126 		return (ERR);
    127 
    128 	_cursesi_screen->COLORS = COLORS;
    129 	_cursesi_screen->COLOR_PAIRS = COLOR_PAIRS;
    130 
    131 	/* Reset terminal colour and colour pairs. */
    132 	if (__tc_oc != NULL)
    133 		tputs(__tc_oc, 0, __cputchar);
    134 	if (__tc_op != NULL) {
    135 		tputs(__tc_op, 0, __cputchar);
    136 		curscr->wattr &= _cursesi_screen->mask_op;
    137 	}
    138 
    139 	/* Type of colour manipulation - ANSI/TEK/HP/other */
    140 	if (__tc_AF != NULL && __tc_AB != NULL)
    141 		_cursesi_screen->color_type = COLOR_ANSI;
    142 	else if (__tc_Ip != NULL)
    143 		_cursesi_screen->color_type = COLOR_HP;
    144 	else if (__tc_Ic != NULL)
    145 		_cursesi_screen->color_type = COLOR_TEK;
    146 	else if (__tc_Sb != NULL && __tc_Sf != NULL)
    147 		_cursesi_screen->color_type = COLOR_OTHER;
    148 	else
    149 		return(ERR);		/* Unsupported colour method */
    150 
    151 #ifdef DEBUG
    152 	__CTRACE("start_color: COLORS = %d, COLOR_PAIRS = %d",
    153 	    COLORS, COLOR_PAIRS);
    154 	switch (_cursesi_screen->color_type) {
    155 	case COLOR_ANSI:
    156 		__CTRACE(" (ANSI style)\n");
    157 		break;
    158 	case COLOR_HP:
    159 		__CTRACE(" (HP style)\n");
    160 		break;
    161 	case COLOR_TEK:
    162 		__CTRACE(" (Tektronics style)\n");
    163 		break;
    164 	case COLOR_OTHER:
    165 		__CTRACE(" (Other style)\n");
    166 		break;
    167 	}
    168 #endif
    169 
    170 	/*
    171 	 * Attributes that cannot be used with color.
    172 	 * Store these in an attr_t for wattrset()/wattron().
    173 	 */
    174 	_cursesi_screen->nca = __NORMAL;
    175 	if (__tc_NC != -1) {
    176 		temp_nc = (attr_t) t_getnum(_cursesi_screen->cursesi_genbuf, "NC");
    177 		if (temp_nc & 0x0001)
    178 			_cursesi_screen->nca |= __STANDOUT;
    179 		if (temp_nc & 0x0002)
    180 			_cursesi_screen->nca |= __UNDERSCORE;
    181 		if (temp_nc & 0x0004)
    182 			_cursesi_screen->nca |= __REVERSE;
    183 		if (temp_nc & 0x0008)
    184 			_cursesi_screen->nca |= __BLINK;
    185 		if (temp_nc & 0x0010)
    186 			_cursesi_screen->nca |= __DIM;
    187 		if (temp_nc & 0x0020)
    188 			_cursesi_screen->nca |= __BOLD;
    189 		if (temp_nc & 0x0040)
    190 			_cursesi_screen->nca |= __BLANK;
    191 		if (temp_nc & 0x0080)
    192 			_cursesi_screen->nca |= __PROTECT;
    193 		if (temp_nc & 0x0100)
    194 			_cursesi_screen->nca |= __ALTCHARSET;
    195 	}
    196 #ifdef DEBUG
    197 	__CTRACE ("start_color: __nca = %d\n", _cursesi_screen->nca);
    198 #endif
    199 
    200 	/* Set up initial 8 colours */
    201 	if (COLORS >= COLOR_BLACK)
    202 		(void) init_color(COLOR_BLACK, 0, 0, 0);
    203 	if (COLORS >= COLOR_RED)
    204 		(void) init_color(COLOR_RED, 1000, 0, 0);
    205 	if (COLORS >= COLOR_GREEN)
    206 		(void) init_color(COLOR_GREEN, 0, 1000, 0);
    207 	if (COLORS >= COLOR_YELLOW)
    208 		(void) init_color(COLOR_YELLOW, 1000, 1000, 0);
    209 	if (COLORS >= COLOR_BLUE)
    210 		(void) init_color(COLOR_BLUE, 0, 0, 1000);
    211 	if (COLORS >= COLOR_MAGENTA)
    212 		(void) init_color(COLOR_MAGENTA, 1000, 0, 1000);
    213 	if (COLORS >= COLOR_CYAN)
    214 		(void) init_color(COLOR_CYAN, 0, 1000, 1000);
    215 	if (COLORS >= COLOR_WHITE)
    216 		(void) init_color(COLOR_WHITE, 1000, 1000, 1000);
    217 
    218 	/* Initialise other colours */
    219 	for (i = 8; i < COLORS; i++) {
    220 		_cursesi_screen->colours[i].red = 0;
    221 		_cursesi_screen->colours[i].green = 0;
    222 		_cursesi_screen->colours[i].blue = 0;
    223 		_cursesi_screen->colours[i].flags = 0;
    224 	}
    225 
    226 	/* Initialise pair 0 to default colours. */
    227 	_cursesi_screen->colour_pairs[0].fore = -1;
    228 	_cursesi_screen->colour_pairs[0].back = -1;
    229 	_cursesi_screen->colour_pairs[0].flags = 0;
    230 
    231 	/* Initialise user colour pairs to default (white on black) */
    232 	for (i = 1; i < COLOR_PAIRS; i++) {
    233 		_cursesi_screen->colour_pairs[i].fore = COLOR_WHITE;
    234 		_cursesi_screen->colour_pairs[i].back = COLOR_BLACK;
    235 		_cursesi_screen->colour_pairs[i].flags = 0;
    236 	}
    237 
    238 	/* Initialise default colour pair. */
    239 	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore =
    240 	    __default_pair.fore;
    241 	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back =
    242 	    __default_pair.back;
    243 	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags =
    244 	    __default_pair.flags;
    245 
    246 	__using_color = 1;
    247 
    248 	/* Set all positions on all windows to curses default colours. */
    249 	for (wlp = __winlistp; wlp != NULL; wlp = wlp->nextp) {
    250 		win = wlp->winp;
    251 		if (wlp->winp == curscr) {
    252 			/* Reset colour attribute on curscr */
    253 			for (y = 0; y < curscr->maxy; y++)
    254 				for (x = 0; x < curscr->maxx; x++) {
    255 					if ((curscr->lines[y]->line[x].battr & __COLOR) == __default_color)
    256 						curscr->lines[y]->line[x].battr &= ~__COLOR;
    257 				}
    258 		} else if (wlp->winp != __virtscr) {
    259 			/* Set background attribute on other windows */
    260 			if (!(win->battr & __COLOR))
    261 				win->battr |= __default_color;
    262 			for (y = 0; y < win->maxy; y++) {
    263 				for (x = 0; x < win->maxx; x++)
    264 					if (!(win->lines[y]->line[x].battr & __COLOR))
    265 						win->lines[y]->line[x].battr |= __default_color;
    266 			}
    267 			__touchwin(win);
    268 		}
    269 	}
    270 
    271 	return(OK);
    272 }
    273 
    274 /*
    275  * init_pair --
    276  *	Set pair foreground and background colors.
    277  */
    278 int
    279 init_pair(short pair, short fore, short back)
    280 {
    281 	int	changed;
    282 
    283 #ifdef DEBUG
    284 	__CTRACE("init_pair: %d, %d, %d\n", pair, fore, back);
    285 #endif
    286 
    287 	if (pair < 0 || pair > COLOR_PAIRS)
    288 		return (ERR);
    289 	if (fore < -1 || fore >= COLORS)
    290 		return (ERR);
    291 	if (back < -1 || back >= COLORS)
    292 		return (ERR);
    293 
    294 	if ((_cursesi_screen->colour_pairs[pair].flags & __USED) &&
    295 	    (fore != _cursesi_screen->colour_pairs[pair].fore ||
    296 	     back != _cursesi_screen->colour_pairs[pair].back))
    297 		changed = 1;
    298 	else
    299 		changed = 0;
    300 
    301 	_cursesi_screen->colour_pairs[pair].flags |= __USED;
    302 	_cursesi_screen->colour_pairs[pair].fore = fore;
    303 	_cursesi_screen->colour_pairs[pair].back = back;
    304 
    305 	/* XXX: need to initialise HP style (Ip) */
    306 
    307 	if (changed)
    308 		__change_pair(pair);
    309 	return (OK);
    310 }
    311 
    312 /*
    313  * pair_content --
    314  *	Get pair foreground and background colours.
    315  */
    316 int
    317 pair_content(short pair, short *forep, short *backp)
    318 {
    319 	if (pair < 0 || pair > _cursesi_screen->COLOR_PAIRS)
    320 		return(ERR);
    321 
    322 	*forep = _cursesi_screen->colour_pairs[pair].fore;
    323 	*backp = _cursesi_screen->colour_pairs[pair].back;
    324 	return(OK);
    325 }
    326 
    327 /*
    328  * init_color --
    329  *	Set colour red, green and blue values.
    330  */
    331 int
    332 init_color(short color, short red, short green, short blue)
    333 {
    334 #ifdef DEBUG
    335 	__CTRACE("init_color: %d, %d, %d, %d\n", color, red, green, blue);
    336 #endif
    337 	if (color < 0 || color >= _cursesi_screen->COLORS)
    338 		return(ERR);
    339 
    340 	_cursesi_screen->colours[color].red = red;
    341 	_cursesi_screen->colours[color].green = green;
    342 	_cursesi_screen->colours[color].blue = blue;
    343 	/* XXX Not yet implemented */
    344 	return(ERR);
    345 	/* XXX: need to initialise Tek style (Ic) and support HLS */
    346 }
    347 
    348 /*
    349  * color_content --
    350  *	Get colour red, green and blue values.
    351  */
    352 int
    353 color_content(short color, short *redp, short *greenp, short *bluep)
    354 {
    355 	if (color < 0 || color >= _cursesi_screen->COLORS)
    356 		return(ERR);
    357 
    358 	*redp = _cursesi_screen->colours[color].red;
    359 	*greenp = _cursesi_screen->colours[color].green;
    360 	*bluep = _cursesi_screen->colours[color].blue;
    361 	return(OK);
    362 }
    363 
    364 /*
    365  * use_default_colors --
    366  *	Use terminal default colours instead of curses default colour.
    367   */
    368 int
    369 use_default_colors()
    370 {
    371 #ifdef DEBUG
    372 	__CTRACE("use_default_colors\n");
    373 #endif
    374 
    375 	return(assume_default_colors(-1, -1));
    376 }
    377 
    378 /*
    379  * assume_default_colors --
    380  *	Set the default foreground and background colours.
    381  */
    382 int
    383 assume_default_colors(short fore, short back)
    384 {
    385 #ifdef DEBUG
    386 	__CTRACE("assume_default_colors: %d, %d, %d\n", fore, back);
    387 #endif
    388 	__default_pair.fore = fore;
    389 	__default_pair.back = back;
    390 	__default_pair.flags = __USED;
    391 
    392 	if (COLOR_PAIRS) {
    393 		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore;
    394 		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back;
    395 		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED;
    396 	}
    397 
    398 	/*
    399 	 * If we've already called start_color(), make sure all instances
    400 	 * of the curses default colour pair are dirty.
    401 	 */
    402 	if (__using_color)
    403 		__change_pair(PAIR_NUMBER(__default_color));
    404 
    405 	return(OK);
    406 }
    407 
    408 
    409 /*
    410  * __set_color --
    411  *	Set terminal foreground and background colours.
    412  */
    413 void
    414 __set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr)
    415 {
    416 	short	pair;
    417 
    418 	if ((curscr->wattr & __COLOR) == (attr & __COLOR))
    419 		return;
    420 
    421 	pair = PAIR_NUMBER((u_int32_t)attr);
    422 #ifdef DEBUG
    423 	__CTRACE("__set_color: %d, %d, %d\n", pair,
    424 		 _cursesi_screen->colour_pairs[pair].fore,
    425 		 _cursesi_screen->colour_pairs[pair].back);
    426 #endif
    427 	switch (_cursesi_screen->color_type) {
    428 	/* Set ANSI forground and background colours */
    429 	case COLOR_ANSI:
    430 		if (_cursesi_screen->colour_pairs[pair].fore == -1 ||
    431 		    _cursesi_screen->colour_pairs[pair].back == -1)
    432 			__unset_color(curscr);
    433 		if (_cursesi_screen->colour_pairs[pair].fore != -1)
    434 			tputs(__parse_cap(_cursesi_screen->tc_AF,
    435 			    _cursesi_screen->colour_pairs[pair].fore),
    436 			    0, __cputchar);
    437 		if (_cursesi_screen->colour_pairs[pair].back != -1)
    438 			tputs(__parse_cap(_cursesi_screen->tc_AB,
    439 			    _cursesi_screen->colour_pairs[pair].back),
    440 			    0, __cputchar);
    441 		break;
    442 	case COLOR_HP:
    443 		/* XXX: need to support HP style */
    444 		break;
    445 	case COLOR_TEK:
    446 		/* XXX: need to support Tek style */
    447 		break;
    448 	case COLOR_OTHER:
    449 		if (_cursesi_screen->colour_pairs[pair].fore == -1 ||
    450 		    _cursesi_screen->colour_pairs[pair].back == -1)
    451 			__unset_color(curscr);
    452 		if (_cursesi_screen->colour_pairs[pair].fore != -1)
    453 			tputs(__parse_cap(_cursesi_screen->tc_Sf,
    454 			    _cursesi_screen->colour_pairs[pair].fore),
    455 			    0, __cputchar);
    456 		if (_cursesi_screen->colour_pairs[pair].back != -1)
    457 			tputs(__parse_cap(_cursesi_screen->tc_Sb,
    458 			    _cursesi_screen->colour_pairs[pair].back),
    459 			    0, __cputchar);
    460 		break;
    461 	}
    462 	curscr->wattr &= ~__COLOR;
    463 	curscr->wattr |= attr & __COLOR;
    464 }
    465 
    466 /*
    467  * __unset_color --
    468  *	Clear terminal foreground and background colours.
    469  */
    470 void
    471 __unset_color(WINDOW *win)
    472 {
    473 #ifdef DEBUG
    474 	__CTRACE("__unset_color\n");
    475 #endif
    476 	switch (_cursesi_screen->color_type) {
    477 	/* Clear ANSI forground and background colours */
    478 	case COLOR_ANSI:
    479 		if (__tc_op != NULL) {
    480 			tputs(__tc_op, 0, __cputchar);
    481 			win->wattr &= __mask_op;
    482 		}
    483 		break;
    484 	case COLOR_HP:
    485 		/* XXX: need to support HP style */
    486 		break;
    487 	case COLOR_TEK:
    488 		/* XXX: need to support Tek style */
    489 		break;
    490 	case COLOR_OTHER:
    491 		if (__tc_op != NULL) {
    492 			tputs(__tc_op, 0, __cputchar);
    493 			win->wattr &= __mask_op;
    494 		}
    495 		break;
    496 	}
    497 }
    498 
    499 /*
    500  * __restore_colors --
    501  *	Redo color definitions after restarting 'curses' mode.
    502  */
    503 void
    504 __restore_colors(void)
    505 {
    506 	if (__tc_cc != NULL)
    507 		switch (_cursesi_screen->color_type) {
    508 		case COLOR_HP:
    509 			/* XXX: need to re-initialise HP style (Ip) */
    510 			break;
    511 		case COLOR_TEK:
    512 			/* XXX: need to re-initialise Tek style (Ic) */
    513 			break;
    514 		}
    515 }
    516 
    517 /*
    518  * __change_pair --
    519  *	Mark dirty all positions using pair.
    520  */
    521 void
    522 __change_pair(short pair)
    523 {
    524 	struct __winlist	*wlp;
    525 	WINDOW			*win;
    526 	int			 y, x;
    527 
    528 
    529 	for (wlp = __winlistp; wlp != NULL; wlp = wlp->nextp) {
    530 #ifdef DEBUG
    531 		__CTRACE("__change_pair: win = %0.2o\n", wlp->winp);
    532 #endif
    533 		win = wlp->winp;
    534 		if (win == curscr) {
    535 			/* Reset colour attribute on curscr */
    536 #ifdef DEBUG
    537 			__CTRACE("__change_pair: win == curscr\n");
    538 #endif
    539 			for (y = 0; y < curscr->maxy; y++)
    540 				for (x = 0; x < curscr->maxx; x++) {
    541 					if ((curscr->lines[y]->line[x].attr &
    542 					    __COLOR) == COLOR_PAIR(pair))
    543 						curscr->lines[y]->line[x].attr
    544 						    &= ~__COLOR;
    545 					if ((curscr->lines[y]->line[x].battr &
    546 					    __COLOR) == COLOR_PAIR(pair))
    547 						curscr->lines[y]->line[x].battr
    548 						    &= ~__COLOR;
    549 				}
    550 		} else if (win != __virtscr) {
    551 			/* Mark dirty those positions with colour pair "pair" */
    552 			for (y = 0; y < win->maxy; y++) {
    553 				for (x = 0; x < win->maxx; x++)
    554 					if ((win->lines[y]->line[x].attr &
    555 					    __COLOR) == COLOR_PAIR(pair) ||
    556 					    (win->lines[y]->line[x].battr &
    557 					    __COLOR) == COLOR_PAIR(pair)) {
    558 						if (!(win->lines[y]->flags &
    559 						    __ISDIRTY))
    560 							win->lines[y]->flags |=
    561 							    __ISDIRTY;
    562 						/*
    563 						 * firstchp/lastchp are shared
    564 						 * between parent window and
    565 						 * sub-window.
    566 						 */
    567 						if (*win->lines[y]->firstchp >
    568 						    x)
    569 							*win->lines[y]->firstchp
    570 							    = x;
    571 						if (*win->lines[y]->lastchp < x)
    572 							*win->lines[y]->lastchp
    573 							    = x;
    574 					}
    575 #ifdef DEBUG
    576 				if ((win->lines[y]->flags & __ISDIRTY))
    577 					__CTRACE("__change_pair: first = %d, last = %d\n", *win->lines[y]->firstchp, *win->lines[y]->lastchp);
    578 #endif
    579 			}
    580 		}
    581 	}
    582 }
    583