1169691Skan/*
2169691Skan** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3169691Skan**
4169691Skan** This file is part of TACK.
5169691Skan**
6169691Skan** TACK is free software; you can redistribute it and/or modify
7169691Skan** it under the terms of the GNU General Public License as published by
8169691Skan** the Free Software Foundation; either version 2, or (at your option)
9169691Skan** any later version.
10169691Skan**
11169691Skan** TACK is distributed in the hope that it will be useful,
12169691Skan** but WITHOUT ANY WARRANTY; without even the implied warranty of
13169691Skan** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14169691Skan** GNU General Public License for more details.
15169691Skan**
16169691Skan** You should have received a copy of the GNU General Public License
17169691Skan** along with TACK; see the file COPYING.  If not, write to
18169691Skan** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19169691Skan** Boston, MA 02110-1301, USA
20169691Skan*/
21169691Skan
22169691Skan#include <tack.h>
23169691Skan
24169691SkanMODULE_ID("$Id: color.c,v 1.6 2005/09/17 19:49:16 tom Exp $")
25169691Skan
26169691Skan/*
27169691Skan * Color terminal tests.  Has only one entry point: test_color().
28169691Skan */
29169691Skan
30169691Skanstatic void color_check(struct test_list *, int *, int *);
31169691Skanstatic void color_setf(struct test_list *, int *, int *);
32169691Skanstatic void color_matrix(struct test_list *, int *, int *);
33169691Skanstatic void color_ncv(struct test_list *, int *, int *);
34169691Skanstatic void color_ccc(struct test_list *, int *, int *);
35169691Skanstatic void color_bce(struct test_list *, int *, int *);
36169691Skan
37169691Skanstruct test_list color_test_list[] = {
38169691Skan	{0, 0, 0, 0, "e) edit terminfo", 0, &edit_menu},
39169691Skan	{MENU_NEXT, 2, "colors) (pairs", 0, 0, color_check, 0},
40169691Skan	{MENU_NEXT, 12, "setf) (setb) (scp", 0, 0, color_setf, 0},
41169691Skan	{MENU_NEXT, 24, "op", 0, 0, color_matrix, 0},
42169691Skan	{MENU_NEXT, 16, "ncv", 0, 0, color_ncv, 0},
43169691Skan	{MENU_NEXT, 0, "bce", 0, 0, color_bce, 0},
44169691Skan	{MENU_NEXT | MENU_CLEAR, 0, "ccc) (initc) (initp", "hls op oc", 0, color_ccc, 0},
45169691Skan	{MENU_LAST, 0, 0, 0, 0, 0, 0}
46169691Skan};
47169691Skan
48169691Skan#ifndef COLOR_BLACK
49169691Skan#define COLOR_BLACK     0
50169691Skan#define COLOR_BLUE      1
51169691Skan#define COLOR_GREEN     2
52169691Skan#define COLOR_CYAN      3
53169691Skan#define COLOR_RED       4
54169691Skan#define COLOR_MAGENTA   5
55169691Skan#define COLOR_YELLOW    6
56169691Skan#define COLOR_WHITE     7
57169691Skan#endif
58169691Skan
59169691Skanstruct color_table {
60169691Skan	const char *name;
61169691Skan	int index;
62169691Skan	int r, g, b;
63169691Skan	int h, l, s;
64169691Skan};
65169691Skan
66169691Skanstatic struct color_table def_colors[8] = {
67169691Skan	{"black  ", COLOR_BLACK, 0, 0, 0, 0, 0, 0},
68169691Skan	{"blue   ", COLOR_BLUE, 0, 0, 1000, 330, 50, 100},
69169691Skan	{"green  ", COLOR_GREEN, 0, 1000, 0, 240, 50, 100},
70169691Skan	{"cyan   ", COLOR_CYAN, 0, 1000, 1000, 300, 50, 100},
71169691Skan	{"red    ", COLOR_RED, 1000, 0, 0, 120, 50, 100},
72169691Skan	{"magenta", COLOR_MAGENTA, 1000, 0, 1000, 60, 50, 100},
73169691Skan	{"yellow ", COLOR_YELLOW, 1000, 1000, 0, 180, 50, 100},
74169691Skan	{"white  ", COLOR_WHITE, 1000, 1000, 1000, 0, 100, 0}
75169691Skan};
76169691Skan
77169691Skan#define MAX_PAIR	256
78static int fg_color[MAX_PAIR] = {COLOR_BLACK, COLOR_BLUE, COLOR_GREEN,
79COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE};
80static int bg_color[MAX_PAIR] = {COLOR_BLACK, COLOR_BLACK, COLOR_BLACK,
81COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, COLOR_BLACK};
82static int pairs_used = 8;
83static int a_bright_color, bright_value;
84static int cookie_monster, color_step, colors_per_line;
85static int R, G, B;
86
87static void reset_colors(void)
88{
89	tc_putp(orig_colors);
90	tc_putp(tparm(orig_pair));
91}
92
93static int
94color_trans(int c)
95{				/* translate or load the color */
96	int i;
97
98	for (i = 0; i < pairs_used; i++) {
99		if (fg_color[i] == c) {
100			return i;
101		}
102	}
103	if (!can_change) {
104		return 0;
105	}
106	if (pairs_used > max_colors || pairs_used >= MAX_PAIR) {
107		pairs_used = 0;
108		ptextln("Ran out of colors");
109	}
110	fg_color[pairs_used] = c;
111	bg_color[pairs_used] = c;
112	if (hue_lightness_saturation) {
113		tc_putp(tparm(initialize_color, pairs_used,
114			def_colors[c].h, def_colors[c].l, def_colors[c].s));
115	} else {
116		tc_putp(tparm(initialize_color, pairs_used,
117			def_colors[c].r, def_colors[c].g, def_colors[c].b));
118	}
119	return pairs_used++;
120}
121
122static void
123new_color(
124	int fg,
125	int bg,
126	int hungry)
127{				/* change the color to fg and bg. */
128	int i;
129
130	if (hungry) {
131		eat_cookie();
132	}
133	if (set_a_foreground) {
134		/* set ANSI color (setaf) (setab) */
135		tc_putp(tparm(set_a_foreground, fg));
136		tc_putp(tparm(set_a_background, bg));
137	} else if (set_foreground) {
138		/* make sure black is zero */
139		(void) color_trans(COLOR_BLACK);
140		tc_putp(tparm(set_foreground, color_trans(fg)));
141		tc_putp(tparm(set_background, color_trans(bg)));
142	} else {	/* set color pair */
143		for (i = 0; i < pairs_used; i++) {
144			if (fg_color[i] == fg && bg_color[i] == bg) {
145				tc_putp(tparm(set_color_pair, i));
146				if (hungry) {
147					eat_cookie();
148				}
149				return;
150			}
151		}
152		if (!can_change) {
153			/* try to set just the foreground */
154			for (i = pairs_used - 1; i; i--) {
155				if (fg_color[i] == fg)
156					break;
157			}
158			tc_putp(tparm(set_color_pair, i));
159			if (hungry) {
160				eat_cookie();
161			}
162			return;
163		}
164		if (pairs_used > max_pairs || pairs_used >= MAX_PAIR) {
165			pairs_used = 0;
166			ptextln("Ran out of color pairs");
167		}
168		fg_color[pairs_used] = fg;
169		bg_color[pairs_used] = bg;
170		if (hue_lightness_saturation) {
171			tc_putp(tparm(initialize_pair, pairs_used,
172				def_colors[fg].h, def_colors[fg].l, def_colors[fg].s,
173				def_colors[bg].h, def_colors[bg].l, def_colors[bg].s));
174		} else {
175			tc_putp(tparm(initialize_pair, pairs_used,
176				def_colors[fg].r, def_colors[fg].g, def_colors[fg].b,
177				def_colors[bg].r, def_colors[bg].g, def_colors[bg].b));
178		}
179		tc_putp(tparm(set_color_pair, pairs_used));
180		pairs_used++;
181	}
182	if (hungry) {
183		eat_cookie();
184	}
185}
186
187
188static void
189set_color_step(void)
190{				/* set the color_step for the (ccc) display */
191	int i;
192
193	for (i = 2; i < 1000; i++) {
194		if ((i * i * i) >= max_colors) {
195			break;
196		}
197	}
198	color_step = 1000 / (i - 1);
199}
200
201
202static void
203rgb_2_hls(int r, int g, int b, int *h, int *l, int *s)
204{				/* convert RGB to HLS system */
205	int min, max, t;
206
207	if ((min = g < r ? g : r) > b) {
208		min = b;
209	}
210	if ((max = g > r ? g : r) < b) {
211		max = b;
212	}
213
214	/* calculate lightness */
215	*l = (min + max) / 20;
216
217	if (min == max) {	/* black, white and all shades of gray */
218		*h = 0;
219		*s = 0;
220		return;
221	}
222	/* calculate saturation */
223	if (*l < 50) {
224		*s = ((max - min) * 100) / (max + min);
225	} else {
226		*s = ((max - min) * 100) / (2000 - max - min);
227	}
228
229	/* calculate hue */
230	if (r == max) {
231		t = 120 + ((g - b) * 60) / (max - min);
232	} else if (g == max) {
233		t = 240 + ((b - r) * 60) / (max - min);
234	} else {
235		t = 360 + ((r - g) * 60) / (max - min);
236	}
237	*h = t % 360;
238}
239
240
241static void
242send_color(int p, int r, int g, int b)
243{				/* send the initialize_color (initc) command */
244	int h, l, s;
245
246	if (hue_lightness_saturation) {
247		rgb_2_hls(r, g, b, &h, &l, &s);
248		tc_putp(tparm(initialize_color, p, h, l, s));
249	} else {
250		tc_putp(tparm(initialize_color, p, r, g, b));
251	}
252}
253
254
255static void
256send_pair(int p, int fr, int fg, int fb, int br, int bg, int bb)
257{				/* send the initialize_pair (initp) command */
258	int fh, fl, fs, bh, bl, bs;
259
260	if (hue_lightness_saturation) {
261		rgb_2_hls(fr, fg, fb, &fh, &fl, &fs);
262		rgb_2_hls(br, bg, bb, &bh, &bl, &bs);
263		tc_putp(tparm(initialize_pair, p, fh, fl, fs, bh, bl, bs));
264	} else {
265		tc_putp(tparm(initialize_pair, p, fr, fg, fb, bb, bg, bb));
266	}
267}
268
269
270static int
271load_palette(int n)
272{				/* load the color palette */
273	int rgb;
274
275	for (;;) {
276		if (pairs_used >= n) {
277			return FALSE;
278		}
279		if (set_a_foreground || set_foreground) {
280			if (pairs_used >= max_colors) {
281				return FALSE;
282			}
283			send_color(pairs_used, R, G, B);
284			rgb = R + G + B;
285			if (rgb > bright_value) {
286				bright_value = rgb;
287				a_bright_color = pairs_used;
288			}
289		} else {
290			if (pairs_used >= max_pairs) {
291				return FALSE;
292			}
293			if (pairs_used == 0) {
294				send_pair(pairs_used, 1000, 1000, 1000, R, G, B);
295			} else {
296				send_pair(pairs_used, R, G, B, R, G, B);
297			}
298		}
299		pairs_used++;
300		if ((B += color_step) > 1000) {
301			B = 0;
302			if ((G += color_step) > 1000) {
303				G = 0;
304				if ((R += color_step) > 1000) {
305					return TRUE;
306				}
307			}
308		}
309	}
310}
311
312
313static int
314rainbow(int n)
315{				/* print the programmable color display */
316	int i, c, d, palette_full, initial_pair;
317	static const struct {
318		const char *name;
319		char ch;
320	}  splat[] = {
321		{"Bg normal", ' '},
322		{"Fg normal", ' '},
323		{0, 0}
324	};
325
326	if ((set_a_foreground || set_foreground)
327	  ? pairs_used >= max_colors
328	  : pairs_used >= max_pairs) {
329		ptext("New palette: ");
330		(void) wait_here();
331		initial_pair = pairs_used = 1;
332		bright_value = 0;
333	} else if (line_count + 3 >= lines) {
334		ptext("Go: ");
335		(void) wait_here();
336		put_clear();
337		initial_pair = pairs_used = 1;
338		bright_value = 0;
339		n++;
340	} else {
341		initial_pair = pairs_used;
342		n += initial_pair;
343	}
344	palette_full = load_palette(n);
345	for (d = 0; splat[d].name; d++) {
346		c = splat[d].ch;
347		if (d == 1) {
348			put_mode(enter_reverse_mode);
349		}
350		for (i = initial_pair; i < n; i++) {
351			if (i >= pairs_used) {
352				break;
353			}
354			if (set_a_foreground) {
355				if (i >= max_colors) {
356					break;
357				}
358				tc_putp(tparm(set_a_foreground, i));
359				tc_putp(tparm(set_a_background, i));
360			} else if (set_foreground) {
361				if (i >= max_colors) {
362					break;
363				}
364				tc_putp(tparm(set_foreground, i));
365				tc_putp(tparm(set_background, i));
366			} else {
367				if (i >= max_pairs) {
368					break;
369				}
370				tc_putp(tparm(set_color_pair, i));
371			}
372			putchp(c);
373		}
374		if (d == 1) {
375			put_mode(exit_attribute_mode);
376		}
377		if (set_a_foreground) {
378			tc_putp(tparm(set_a_foreground, a_bright_color));
379			tc_putp(tparm(set_a_background, 0));
380		} else if (set_foreground) {
381			tc_putp(tparm(set_foreground, a_bright_color));
382			tc_putp(tparm(set_background, 0));
383		} else {
384			tc_putp(tparm(set_color_pair, 0));
385		}
386		put_str("   ");
387		put_str(splat[d].name);
388		put_crlf();
389	}
390	return palette_full;
391}
392
393
394static void
395ncv_display(int m)
396{				/* print the no_color_video (ncv) test line */
397	putchp('0' + m);
398	putchp(' ');
399	eat_cookie();
400	set_attr(1 << m);
401	sprintf(temp, "%-11s", alt_modes[m].name);
402	put_str(temp);
403
404	new_color(COLOR_BLUE, COLOR_BLACK, TRUE);
405	put_str("blue");
406
407	new_color(COLOR_BLACK, COLOR_GREEN, TRUE);
408	put_str("green");
409
410	new_color(COLOR_WHITE, COLOR_BLACK, TRUE);
411	put_str(alt_modes[m].name);
412	eat_cookie();
413	set_attr(0);
414	reset_colors();
415	put_crlf();
416}
417
418
419static void
420dump_colors(void)
421{				/* display the colors in some esthetic
422				   pattern */
423	static int xmap[8] = {0, 3, 4, 7, 1, 2, 5, 6};
424	int i, j, k, xi, xj, width, p, cs;
425	int found_one;
426
427	cs = color_step <= 125 ? 125 : color_step;
428	width = (1000 / cs) + 1;
429	for (xi = 0; xi < 16; xi++) {
430		i = (xi & 8) ? xi ^ 15 : xi;
431		R = i * cs;
432		if (R <= 1000) {
433			found_one = FALSE;
434			for (xj = 0; xj < 32; xj++) {
435				j = ((xj & 8) ? xj ^ 15 : xj) & 7;
436				k = xmap[((xi >> 1) & 4) + (xj >> 3)];
437				G = j * cs;
438				B = k * cs;
439				if (G <= 1000 && B <= 1000) {
440					p = (k * width + j) * width + i;
441					if (set_a_background) {
442						if (p >= max_colors) {
443							continue;
444						}
445						send_color(p, R, G, B);
446						tc_putp(tparm(set_a_background, p));
447					} else if (set_background) {
448						if (p >= max_colors) {
449							continue;
450						}
451						send_color(p, R, G, B);
452						tc_putp(tparm(set_background, p));
453					} else {
454						if (p >= max_pairs) {
455							continue;
456						}
457						send_pair(p, R, G, B, R, G, B);
458						tc_putp(tparm(set_color_pair, p));
459					}
460					found_one = TRUE;
461					putchp(' ');
462					putchp(' ');
463				}
464			}
465			if (found_one) {
466				put_crlf();
467			}
468		}
469	}
470}
471
472/*
473**	color_check(test_list, status, ch)
474**
475**	test (colors) and (pairs)
476*/
477static void
478color_check(
479	struct test_list *t,
480	int *state,
481	int *ch)
482{
483	if (max_colors <= 0 && max_pairs <= 0) {
484		ptext("This is not a color terminal; (colors) and (pairs) are missing.  ");
485		*state |= MENU_STOP;
486	} else {
487		sprintf(temp, "This terminal can display %d colors and %d color pairs.  (colors) (pairs)",
488			max_colors, max_pairs);
489		ptextln(temp);
490	}
491	generic_done_message(t, state, ch);
492}
493
494/*
495**	color_setf(test_list, status, ch)
496**
497**	test (setf) (setb) and (scp)
498*/
499static void
500color_setf(
501	struct test_list *t,
502	int *state,
503	int *ch)
504{
505	int i, j;
506
507	if (max_colors <= 0 && max_pairs <= 0) {
508		ptext("This is not a color terminal; (colors) and (pairs) are missing.  ");
509		generic_done_message(t, state, ch);
510		*state |= MENU_STOP;
511		return;
512	}
513	if ((set_a_foreground == NULL || set_a_background == NULL)
514	 && (set_foreground == NULL   || set_background == NULL)
515	 && set_color_pair == NULL) {
516		ptextln("Both set foreground (setaf/setf) and set color pair (scp) are not present.");
517		if (!set_a_background || !set_background) {
518			ptextln("(setab/setb) set background not present");
519		}
520		ptext("These must be defined for color testing.  ");
521		generic_done_message(t, state, ch);
522		*state |= MENU_STOP;
523		return;
524	}
525	/* initialize the color palette */
526	pairs_used = max_colors >= 8 ? 8 : max_colors;
527	reset_colors();
528	new_color(COLOR_WHITE, COLOR_BLACK, FALSE);
529
530	ptextln("(setf) (setb) (scp) The following colors are predefined:");
531	ptextln("\n   Foreground     Background");
532	put_crlf();
533	j = max_colors > 8 ? 8 : max_colors;
534	/*
535	 * the black on white test is the same as the white on black test.
536	 */
537	for (i = 1; i < j; i++) {
538		putchp('0' + def_colors[i].index);
539		putchp(' ');
540		sprintf(temp, " %s ", def_colors[i].name);
541
542		new_color(def_colors[i].index, COLOR_BLACK, TRUE);
543		put_str(temp);
544
545		new_color(COLOR_BLACK, COLOR_BLACK, TRUE);
546		put_str("  ");
547
548		new_color(COLOR_BLACK, def_colors[i].index, TRUE);
549		put_str(temp);
550
551		new_color(COLOR_WHITE, COLOR_BLACK, FALSE);
552		put_crlf();
553	}
554	reset_colors();
555	put_crlf();
556	generic_done_message(t, state, ch);
557}
558
559/*
560**	color_matrix(test_list, status, ch)
561**
562**	test (pairs) (op)
563*/
564static void
565color_matrix(
566	struct test_list *t,
567	int *state,
568	int *ch)
569{
570	int i, j, matrix_size, matrix_area, brightness;
571
572	matrix_size = max_colors > 8 ? 8 : max_colors;
573
574	sprintf(temp, "(pairs) There are %d color pairs.", max_pairs);
575	ptextln(temp);
576
577	for ( ; matrix_size; matrix_size--) {
578		if (matrix_size * matrix_size <= max_pairs) {
579			break;
580		}
581	}
582	matrix_area = matrix_size * matrix_size;
583	for (brightness = 0; brightness < 2; brightness++) {
584		put_crlf();
585		sprintf(temp,
586			"%dx%d matrix of foreground/background colors, bright *o%s*",
587			matrix_size, matrix_size, brightness ? "n" : "ff");
588		put_str(temp);
589
590		put_str("\n          ");
591		for (i = 0; i < matrix_size; i++) {
592			(void) sprintf(temp, "%-8s", def_colors[i].name);
593			put_str(temp);
594		}
595		for (j = 0; j < matrix_area; j++) {
596			if (j % matrix_size == 0) {
597				reset_colors();
598				put_crlf();
599				if (brightness) {
600					tc_putp(exit_attribute_mode);
601				}
602				(void) sprintf(temp, "%-8s", def_colors[j / matrix_size].name);
603				put_str(temp);
604				if (brightness) {
605					put_mode(enter_bold_mode);
606				}
607			}
608			new_color(def_colors[j % matrix_size].index,
609				def_colors[j / matrix_size].index,
610				FALSE);
611			put_str("  Hello ");
612		}
613		reset_colors();
614		if (brightness) {
615			tc_putp(exit_attribute_mode);
616		}
617		put_crlf();
618	}
619	generic_done_message(t, state, ch);
620}
621
622/*
623**	color_ncv(test_list, status, ch)
624**
625**	test (ncv)
626*/
627static void
628color_ncv(
629	struct test_list *t,
630	int *state,
631	int *ch)
632{
633	int i;
634
635	if (no_color_video == -1) {
636		/* I have no idea what this means */
637		return;
638	}
639	sprintf(temp, "According to no_color_video (ncv) which is %d, the following attributes should work correctly with color.", no_color_video);
640	ptextln(temp);
641	put_crlf();
642	set_attr(0);
643	ncv_display(0);
644	for (i = 1; i <= 9; i++) {
645		if (((no_color_video >> (mode_map[i] - 1)) & 1) == 0) {
646			ncv_display(mode_map[i]);
647		}
648	}
649	if (no_color_video & 0x3ff) {
650		ptextln("\nThe following attributes should not work correctly with color. (ncv)\n");
651		for (i = 1; i <= 9; i++) {
652			if ((no_color_video >> (mode_map[i] - 1)) & 1) {
653				ncv_display(mode_map[i]);
654			}
655		}
656	}
657	reset_colors();
658	put_crlf();
659	generic_done_message(t, state, ch);
660}
661
662/*
663**	color_bce(test_list, status, ch)
664**
665**	test (bce) background color erase
666*/
667static void
668color_bce(
669	struct test_list *t,
670	int *state,
671	int *ch)
672{
673	new_color(COLOR_CYAN, COLOR_BLUE, FALSE);
674	put_clear();
675	put_newlines(2);
676	reset_colors();
677	ptextln("If the two lines above are blue then back_color_erase (bce) should be true.");
678	sprintf(temp, "(bce) is %s in the data base.", back_color_erase ? "true" : "false");
679	ptextln(temp);
680	generic_done_message(t, state, ch);
681}
682
683/*
684**	color_ccc(test_list, status, ch)
685**
686**	test (ccc) color palette test (oc) (op) (initc) (initp)
687*/
688static void
689color_ccc(
690	struct test_list *t,
691	int *state,
692	int *ch)
693{
694	int i, j;
695
696	if (!can_change) {
697		ptextln("Terminal can not change colors (ccc)");
698		generic_done_message(t, state, ch);
699		return;
700	}
701	reset_colors();
702	pairs_used = 0;
703	new_color(COLOR_WHITE, COLOR_BLACK, FALSE);
704	sprintf(temp, "Reloading colors (init%c) using %s method",
705		set_foreground ? 'c' : 'p',
706		hue_lightness_saturation ? "HLS" : "RGB");
707	ptextln(temp);
708	put_crlf();
709	j = max_colors > 7 ? 7 : max_colors;
710	/* redisplay the above test with reinitialized colors */
711	/* If these colors don't look right to you... */
712	for (i = 0; i < j; i++) {
713		sprintf(temp, " %s ", def_colors[i ^ 7].name);
714
715		new_color(i ^ 7, COLOR_BLACK, TRUE);
716		put_str(temp);
717
718		new_color(COLOR_BLACK, COLOR_BLACK, TRUE);
719		put_str("  ");
720
721		new_color(COLOR_BLACK, i ^ 7, TRUE);
722		put_str(temp);
723
724		new_color(COLOR_WHITE, COLOR_BLACK, FALSE);
725		put_crlf();
726	}
727	generic_done_message(t, state, ch);
728	if (*ch != 0 && *ch != 'n') {
729		reset_colors();
730		return;
731	}
732
733	pairs_used = 0;
734	cookie_monster = 0;
735	if (magic_cookie_glitch > 0) {
736		cookie_monster =
737			((set_a_foreground || set_foreground)
738				? magic_cookie_glitch : 0) +
739			((set_a_background || set_background)
740				? magic_cookie_glitch : 0) +
741			(set_color_pair ? magic_cookie_glitch : 0);
742	}
743	set_color_step();
744	colors_per_line = max_colors > max_pairs
745		? max_pairs : max_colors;
746	j = (columns - 14) / (cookie_monster + 1);
747	if (colors_per_line > j) {
748		colors_per_line = (j / i) * i;
749	}
750	sprintf(temp, "RGB color step %d, cookies %d", color_step,
751		cookie_monster);
752	ptextln(temp);
753
754	R = G = B = 0;
755	pairs_used = 0;
756	for (;;) {
757		if (rainbow(colors_per_line)) {
758			break;
759		}
760	}
761	generic_done_message(t, state, ch);
762	if (*ch != 0 && *ch != 'n') {
763		reset_colors();
764		return;
765	}
766	dump_colors();
767	reset_colors();
768	generic_done_message(t, state, ch);
769}
770