1/*	$NetBSD: color.c,v 1.37 2011/01/06 11:29:40 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__RCSID("$NetBSD: color.c,v 1.37 2011/01/06 11:29:40 blymn Exp $");
35#endif				/* not lint */
36
37#include "curses.h"
38#include "curses_private.h"
39
40/* Have we initialised colours? */
41int	__using_color = 0;
42
43/* Default colour number */
44attr_t	__default_color = 0;
45
46/* Default colour pair values - white on black. */
47struct __pair	__default_pair = {COLOR_WHITE, COLOR_BLACK, 0};
48
49/* Default colour values */
50/* Flags for colours and pairs */
51#define	__USED		0x01
52
53static void
54__change_pair(short);
55
56/*
57 * has_colors --
58 *	Check if terminal has colours.
59 */
60bool
61has_colors(void)
62{
63	if (max_colors > 0 && max_pairs > 0 &&
64	    ((set_a_foreground != NULL && set_a_background != NULL) ||
65		initialize_pair != NULL || initialize_color != NULL ||
66		(set_background != NULL && set_foreground != NULL)))
67		return(TRUE);
68	else
69		return(FALSE);
70}
71
72/*
73 * can_change_color --
74 *	Check if terminal can change colours.
75 */
76bool
77can_change_color(void)
78{
79	if (can_change)
80		return(TRUE);
81	else
82		return(FALSE);
83}
84
85/*
86 * start_color --
87 *	Initialise colour support.
88 */
89int
90start_color(void)
91{
92	int			 i;
93	attr_t			 temp_nc;
94	struct __winlist	*wlp;
95	WINDOW			*win;
96	int			 y, x;
97
98	if (has_colors() == FALSE)
99		return(ERR);
100
101	/* Max colours and colour pairs */
102	if (max_colors == -1)
103		COLORS = 0;
104	else {
105		COLORS = max_colors > MAX_COLORS ? MAX_COLORS : max_colors;
106		if (max_pairs == -1) {
107			COLOR_PAIRS = 0;
108			COLORS = 0;
109		} else {
110			COLOR_PAIRS = (max_pairs > MAX_PAIRS - 1 ?
111			    MAX_PAIRS - 1 : max_pairs);
112			 /* Use the last colour pair for curses default. */
113			__default_color = COLOR_PAIR(MAX_PAIRS - 1);
114		}
115	}
116	if (!COLORS)
117		return (ERR);
118
119	_cursesi_screen->COLORS = COLORS;
120	_cursesi_screen->COLOR_PAIRS = COLOR_PAIRS;
121
122	/* Reset terminal colour and colour pairs. */
123	if (orig_colors != NULL)
124		tputs(orig_colors, 0, __cputchar);
125	if (orig_pair != NULL) {
126		tputs(orig_pair, 0, __cputchar);
127		curscr->wattr &= _cursesi_screen->mask_op;
128	}
129
130	/* Type of colour manipulation - ANSI/TEK/HP/other */
131	if (set_a_foreground != NULL && set_a_background != NULL)
132		_cursesi_screen->color_type = COLOR_ANSI;
133	else if (initialize_pair != NULL)
134		_cursesi_screen->color_type = COLOR_HP;
135	else if (initialize_color != NULL)
136		_cursesi_screen->color_type = COLOR_TEK;
137	else if (set_foreground != NULL && set_background != NULL)
138		_cursesi_screen->color_type = COLOR_OTHER;
139	else
140		return(ERR);		/* Unsupported colour method */
141
142#ifdef DEBUG
143	__CTRACE(__CTRACE_COLOR, "start_color: COLORS = %d, COLOR_PAIRS = %d",
144	    COLORS, COLOR_PAIRS);
145	switch (_cursesi_screen->color_type) {
146	case COLOR_ANSI:
147		__CTRACE(__CTRACE_COLOR, " (ANSI style)\n");
148		break;
149	case COLOR_HP:
150		__CTRACE(__CTRACE_COLOR, " (HP style)\n");
151		break;
152	case COLOR_TEK:
153		__CTRACE(__CTRACE_COLOR, " (Tektronics style)\n");
154		break;
155	case COLOR_OTHER:
156		__CTRACE(__CTRACE_COLOR, " (Other style)\n");
157		break;
158	}
159#endif
160
161	/*
162	 * Attributes that cannot be used with color.
163	 * Store these in an attr_t for wattrset()/wattron().
164	 */
165	_cursesi_screen->nca = __NORMAL;
166	if (no_color_video != -1) {
167		temp_nc = (attr_t) t_no_color_video(_cursesi_screen->term);
168		if (temp_nc & 0x0001)
169			_cursesi_screen->nca |= __STANDOUT;
170		if (temp_nc & 0x0002)
171			_cursesi_screen->nca |= __UNDERSCORE;
172		if (temp_nc & 0x0004)
173			_cursesi_screen->nca |= __REVERSE;
174		if (temp_nc & 0x0008)
175			_cursesi_screen->nca |= __BLINK;
176		if (temp_nc & 0x0010)
177			_cursesi_screen->nca |= __DIM;
178		if (temp_nc & 0x0020)
179			_cursesi_screen->nca |= __BOLD;
180		if (temp_nc & 0x0040)
181			_cursesi_screen->nca |= __BLANK;
182		if (temp_nc & 0x0080)
183			_cursesi_screen->nca |= __PROTECT;
184		if (temp_nc & 0x0100)
185			_cursesi_screen->nca |= __ALTCHARSET;
186	}
187#ifdef DEBUG
188	__CTRACE(__CTRACE_COLOR, "start_color: _cursesi_screen->nca = %08x\n",
189	    _cursesi_screen->nca);
190#endif
191
192	/* Set up initial 8 colours */
193	if (COLORS >= COLOR_BLACK)
194		(void) init_color(COLOR_BLACK, 0, 0, 0);
195	if (COLORS >= COLOR_RED)
196		(void) init_color(COLOR_RED, 1000, 0, 0);
197	if (COLORS >= COLOR_GREEN)
198		(void) init_color(COLOR_GREEN, 0, 1000, 0);
199	if (COLORS >= COLOR_YELLOW)
200		(void) init_color(COLOR_YELLOW, 1000, 1000, 0);
201	if (COLORS >= COLOR_BLUE)
202		(void) init_color(COLOR_BLUE, 0, 0, 1000);
203	if (COLORS >= COLOR_MAGENTA)
204		(void) init_color(COLOR_MAGENTA, 1000, 0, 1000);
205	if (COLORS >= COLOR_CYAN)
206		(void) init_color(COLOR_CYAN, 0, 1000, 1000);
207	if (COLORS >= COLOR_WHITE)
208		(void) init_color(COLOR_WHITE, 1000, 1000, 1000);
209
210	/* Initialise other colours */
211	for (i = 8; i < COLORS; i++) {
212		_cursesi_screen->colours[i].red = 0;
213		_cursesi_screen->colours[i].green = 0;
214		_cursesi_screen->colours[i].blue = 0;
215		_cursesi_screen->colours[i].flags = 0;
216	}
217
218	/* Initialise pair 0 to default colours. */
219	_cursesi_screen->colour_pairs[0].fore = -1;
220	_cursesi_screen->colour_pairs[0].back = -1;
221	_cursesi_screen->colour_pairs[0].flags = 0;
222
223	/* Initialise user colour pairs to default (white on black) */
224	for (i = 0; i < COLOR_PAIRS; i++) {
225		_cursesi_screen->colour_pairs[i].fore = COLOR_WHITE;
226		_cursesi_screen->colour_pairs[i].back = COLOR_BLACK;
227		_cursesi_screen->colour_pairs[i].flags = 0;
228	}
229
230	/* Initialise default colour pair. */
231	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore =
232	    __default_pair.fore;
233	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back =
234	    __default_pair.back;
235	_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags =
236	    __default_pair.flags;
237
238	__using_color = 1;
239
240	/* Set all positions on all windows to curses default colours. */
241	for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) {
242		win = wlp->winp;
243		if (wlp->winp != __virtscr && wlp->winp != curscr) {
244			/* Set color attribute on other windows */
245			win->battr |= __default_color;
246			for (y = 0; y < win->maxy; y++) {
247				for (x = 0; x < win->maxx; x++) {
248					win->alines[y]->line[x].attr &= ~__COLOR;
249					win->alines[y]->line[x].attr |= __default_color;
250				}
251			}
252			__touchwin(win);
253		}
254	}
255
256	return(OK);
257}
258
259/*
260 * init_pair --
261 *	Set pair foreground and background colors.
262 *	Our default colour ordering is ANSI - 1 = red, 4 = blue, 3 = yellow,
263 *	6 = cyan.  The older style (Sb/Sf) uses 1 = blue, 4 = red, 3 = cyan,
264 *	6 = yellow, so we swap them here and in pair_content().
265 */
266int
267init_pair(short pair, short fore, short back)
268{
269	int	changed;
270
271#ifdef DEBUG
272	__CTRACE(__CTRACE_COLOR, "init_pair: %d, %d, %d\n", pair, fore, back);
273#endif
274
275	if (pair < 0 || pair >= COLOR_PAIRS)
276		return (ERR);
277
278	if (pair == 0) /* Ignore request for pair 0, it is default. */
279		return OK;
280
281	if (fore >= COLORS)
282		return (ERR);
283	if (back >= COLORS)
284		return (ERR);
285
286	/* Swap red/blue and yellow/cyan */
287	if (_cursesi_screen->color_type == COLOR_OTHER) {
288		switch (fore) {
289		case COLOR_RED:
290			fore = COLOR_BLUE;
291			break;
292		case COLOR_BLUE:
293			fore = COLOR_RED;
294			break;
295		case COLOR_YELLOW:
296			fore = COLOR_CYAN;
297			break;
298		case COLOR_CYAN:
299			fore = COLOR_YELLOW;
300			break;
301		}
302		switch (back) {
303		case COLOR_RED:
304			back = COLOR_BLUE;
305			break;
306		case COLOR_BLUE:
307			back = COLOR_RED;
308			break;
309		case COLOR_YELLOW:
310			back = COLOR_CYAN;
311			break;
312		case COLOR_CYAN:
313			back = COLOR_YELLOW;
314			break;
315		}
316	}
317
318	if ((_cursesi_screen->colour_pairs[pair].flags & __USED) &&
319	    (fore != _cursesi_screen->colour_pairs[pair].fore ||
320	     back != _cursesi_screen->colour_pairs[pair].back))
321		changed = 1;
322	else
323		changed = 0;
324
325	_cursesi_screen->colour_pairs[pair].flags |= __USED;
326	_cursesi_screen->colour_pairs[pair].fore = fore;
327	_cursesi_screen->colour_pairs[pair].back = back;
328
329	/* XXX: need to initialise HP style (Ip) */
330
331	if (changed)
332		__change_pair(pair);
333	return (OK);
334}
335
336/*
337 * pair_content --
338 *	Get pair foreground and background colours.
339 */
340int
341pair_content(short pair, short *forep, short *backp)
342{
343	if (pair < 0 || pair > _cursesi_screen->COLOR_PAIRS)
344		return(ERR);
345
346	*forep = _cursesi_screen->colour_pairs[pair].fore;
347	*backp = _cursesi_screen->colour_pairs[pair].back;
348
349	/* Swap red/blue and yellow/cyan */
350	if (_cursesi_screen->color_type == COLOR_OTHER) {
351		switch (*forep) {
352		case COLOR_RED:
353			*forep = COLOR_BLUE;
354			break;
355		case COLOR_BLUE:
356			*forep = COLOR_RED;
357			break;
358		case COLOR_YELLOW:
359			*forep = COLOR_CYAN;
360			break;
361		case COLOR_CYAN:
362			*forep = COLOR_YELLOW;
363			break;
364		}
365		switch (*backp) {
366		case COLOR_RED:
367			*backp = COLOR_BLUE;
368			break;
369		case COLOR_BLUE:
370			*backp = COLOR_RED;
371			break;
372		case COLOR_YELLOW:
373			*backp = COLOR_CYAN;
374			break;
375		case COLOR_CYAN:
376			*backp = COLOR_YELLOW;
377			break;
378		}
379	}
380	return(OK);
381}
382
383/*
384 * init_color --
385 *	Set colour red, green and blue values.
386 */
387int
388init_color(short color, short red, short green, short blue)
389{
390#ifdef DEBUG
391	__CTRACE(__CTRACE_COLOR, "init_color: %d, %d, %d, %d\n",
392	    color, red, green, blue);
393#endif
394	if (color < 0 || color >= _cursesi_screen->COLORS)
395		return(ERR);
396
397	_cursesi_screen->colours[color].red = red;
398	_cursesi_screen->colours[color].green = green;
399	_cursesi_screen->colours[color].blue = blue;
400	/* XXX Not yet implemented */
401	return(ERR);
402	/* XXX: need to initialise Tek style (Ic) and support HLS */
403}
404
405/*
406 * color_content --
407 *	Get colour red, green and blue values.
408 */
409int
410color_content(short color, short *redp, short *greenp, short *bluep)
411{
412	if (color < 0 || color >= _cursesi_screen->COLORS)
413		return(ERR);
414
415	*redp = _cursesi_screen->colours[color].red;
416	*greenp = _cursesi_screen->colours[color].green;
417	*bluep = _cursesi_screen->colours[color].blue;
418	return(OK);
419}
420
421/*
422 * use_default_colors --
423 *	Use terminal default colours instead of curses default colour.
424  */
425int
426use_default_colors()
427{
428#ifdef DEBUG
429	__CTRACE(__CTRACE_COLOR, "use_default_colors\n");
430#endif
431
432	return(assume_default_colors(-1, -1));
433}
434
435/*
436 * assume_default_colors --
437 *	Set the default foreground and background colours.
438 */
439int
440assume_default_colors(short fore, short back)
441{
442#ifdef DEBUG
443	__CTRACE(__CTRACE_COLOR, "assume_default_colors: %d, %d\n",
444	    fore, back);
445	__CTRACE(__CTRACE_COLOR, "assume_default_colors: default_colour = %d, pair_number = %d\n", __default_color, PAIR_NUMBER(__default_color));
446#endif
447
448	/* Swap red/blue and yellow/cyan */
449	if (_cursesi_screen->color_type == COLOR_OTHER) {
450		switch (fore) {
451		case COLOR_RED:
452			fore = COLOR_BLUE;
453			break;
454		case COLOR_BLUE:
455			fore = COLOR_RED;
456			break;
457		case COLOR_YELLOW:
458			fore = COLOR_CYAN;
459			break;
460		case COLOR_CYAN:
461			fore = COLOR_YELLOW;
462			break;
463		}
464		switch (back) {
465		case COLOR_RED:
466			back = COLOR_BLUE;
467			break;
468		case COLOR_BLUE:
469			back = COLOR_RED;
470			break;
471		case COLOR_YELLOW:
472			back = COLOR_CYAN;
473			break;
474		case COLOR_CYAN:
475			back = COLOR_YELLOW;
476			break;
477		}
478	}
479	__default_pair.fore = fore;
480	__default_pair.back = back;
481	__default_pair.flags = __USED;
482
483	if (COLOR_PAIRS) {
484		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].fore = fore;
485		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].back = back;
486		_cursesi_screen->colour_pairs[PAIR_NUMBER(__default_color)].flags = __USED;
487	}
488
489	/*
490	 * If we've already called start_color(), make sure all instances
491	 * of the curses default colour pair are dirty.
492	 */
493	if (__using_color)
494		__change_pair(PAIR_NUMBER(__default_color));
495
496	return(OK);
497}
498
499/* no_color_video is a terminfo macro, but we need to retain binary compat */
500#ifdef __strong_alias
501#undef no_color_video
502__strong_alias(no_color_video, no_color_attributes)
503#endif
504/*
505 * no_color_attributes --
506 *	Return attributes that cannot be combined with color.
507 */
508attr_t
509no_color_attributes(void)
510{
511	return(_cursesi_screen->nca);
512}
513
514/*
515 * __set_color --
516 *	Set terminal foreground and background colours.
517 */
518void
519__set_color( /*ARGSUSED*/ WINDOW *win, attr_t attr)
520{
521	short	pair;
522
523	if ((curscr->wattr & __COLOR) == (attr & __COLOR))
524		return;
525
526	pair = PAIR_NUMBER((u_int32_t)attr);
527#ifdef DEBUG
528	__CTRACE(__CTRACE_COLOR, "__set_color: %d, %d, %d\n", pair,
529		 _cursesi_screen->colour_pairs[pair].fore,
530		 _cursesi_screen->colour_pairs[pair].back);
531#endif
532	switch (_cursesi_screen->color_type) {
533	/* Set ANSI forground and background colours */
534	case COLOR_ANSI:
535		if (_cursesi_screen->colour_pairs[pair].fore < 0 ||
536		    _cursesi_screen->colour_pairs[pair].back < 0)
537			__unset_color(curscr);
538		if (_cursesi_screen->colour_pairs[pair].fore >= 0)
539			tputs(tiparm(t_set_a_foreground(_cursesi_screen->term),
540			    (int)_cursesi_screen->colour_pairs[pair].fore),
541			    0, __cputchar);
542		if (_cursesi_screen->colour_pairs[pair].back >= 0)
543			tputs(tiparm(t_set_a_background(_cursesi_screen->term),
544			    (int)_cursesi_screen->colour_pairs[pair].back),
545			    0, __cputchar);
546		break;
547	case COLOR_HP:
548		/* XXX: need to support HP style */
549		break;
550	case COLOR_TEK:
551		/* XXX: need to support Tek style */
552		break;
553	case COLOR_OTHER:
554		if (_cursesi_screen->colour_pairs[pair].fore < 0 ||
555		    _cursesi_screen->colour_pairs[pair].back < 0)
556			__unset_color(curscr);
557		if (_cursesi_screen->colour_pairs[pair].fore >= 0)
558			tputs(tiparm(t_set_foreground(_cursesi_screen->term),
559			    (int)_cursesi_screen->colour_pairs[pair].fore),
560			    0, __cputchar);
561		if (_cursesi_screen->colour_pairs[pair].back >= 0)
562			tputs(tiparm(t_set_background(_cursesi_screen->term),
563			    (int)_cursesi_screen->colour_pairs[pair].back),
564			    0, __cputchar);
565		break;
566	}
567	curscr->wattr &= ~__COLOR;
568	curscr->wattr |= attr & __COLOR;
569}
570
571/*
572 * __unset_color --
573 *	Clear terminal foreground and background colours.
574 */
575void
576__unset_color(WINDOW *win)
577{
578#ifdef DEBUG
579	__CTRACE(__CTRACE_COLOR, "__unset_color\n");
580#endif
581	switch (_cursesi_screen->color_type) {
582	/* Clear ANSI forground and background colours */
583	case COLOR_ANSI:
584		if (orig_pair != NULL) {
585			tputs(orig_pair, 0, __cputchar);
586			win->wattr &= __mask_op;
587		}
588		break;
589	case COLOR_HP:
590		/* XXX: need to support HP style */
591		break;
592	case COLOR_TEK:
593		/* XXX: need to support Tek style */
594		break;
595	case COLOR_OTHER:
596		if (orig_pair != NULL) {
597			tputs(orig_pair, 0, __cputchar);
598			win->wattr &= __mask_op;
599		}
600		break;
601	}
602}
603
604/*
605 * __restore_colors --
606 *	Redo color definitions after restarting 'curses' mode.
607 */
608void
609__restore_colors(void)
610{
611	if (can_change != 0)
612		switch (_cursesi_screen->color_type) {
613		case COLOR_HP:
614			/* XXX: need to re-initialise HP style (Ip) */
615			break;
616		case COLOR_TEK:
617			/* XXX: need to re-initialise Tek style (Ic) */
618			break;
619		}
620}
621
622/*
623 * __change_pair --
624 *	Mark dirty all positions using pair.
625 */
626void
627__change_pair(short pair)
628{
629	struct __winlist	*wlp;
630	WINDOW			*win;
631	int			 y, x;
632	__LINE			*lp;
633	uint32_t		cl = COLOR_PAIR(pair);
634
635
636	for (wlp = _cursesi_screen->winlistp; wlp != NULL; wlp = wlp->nextp) {
637#ifdef DEBUG
638		__CTRACE(__CTRACE_COLOR, "__change_pair: win = %p\n",
639		    wlp->winp);
640#endif
641		win = wlp->winp;
642		if (win == __virtscr)
643			continue;
644		else if (win == curscr) {
645			/* Reset colour attribute on curscr */
646#ifdef DEBUG
647			__CTRACE(__CTRACE_COLOR,
648			    "__change_pair: win == curscr\n");
649#endif
650			for (y = 0; y < curscr->maxy; y++) {
651				lp = curscr->alines[y];
652				for (x = 0; x < curscr->maxx; x++) {
653					if ((lp->line[x].attr & __COLOR) == cl)
654						lp->line[x].attr &= ~__COLOR;
655				}
656			}
657		} else {
658			/* Mark dirty those positions with colour pair "pair" */
659			for (y = 0; y < win->maxy; y++) {
660				lp = win->alines[y];
661				for (x = 0; x < win->maxx; x++)
662					if ((lp->line[x].attr &
663					    __COLOR) == cl) {
664						if (!(lp->flags & __ISDIRTY))
665							lp->flags |= __ISDIRTY;
666						/*
667					 	* firstchp/lastchp are shared
668					 	* between parent window and
669					 	* sub-window.
670					 	*/
671						if (*lp->firstchp > x)
672						*lp->firstchp = x;
673						if (*lp->lastchp < x)
674							*lp->lastchp = x;
675					}
676#ifdef DEBUG
677				if ((win->alines[y]->flags & __ISDIRTY))
678					__CTRACE(__CTRACE_COLOR,
679					    "__change_pair: first = %d, "
680					    "last = %d\n",
681					    *win->alines[y]->firstchp,
682					    *win->alines[y]->lastchp);
683#endif
684			}
685		}
686	}
687}
688