1/****************************************************************************
2 * Copyright (c) 1998-2004,2005 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
30NAME
31   ncurses.c --- ncurses library exerciser
32
33SYNOPSIS
34   ncurses
35
36DESCRIPTION
37   An interactive test module for the ncurses library.
38
39AUTHOR
40   Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
41           Thomas E. Dickey (beginning revision 1.27 in 1996).
42
43$Id: ncurses.c,v 1.253 2005/10/01 16:00:56 tom Exp $
44
45***************************************************************************/
46
47#include <test.priv.h>
48
49#if HAVE_GETTIMEOFDAY
50#if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
51#include <sys/time.h>
52#endif
53#if HAVE_SYS_SELECT_H
54#include <sys/select.h>
55#endif
56#endif
57
58#if USE_LIBPANEL
59#include <panel.h>
60#endif
61
62#if USE_LIBMENU
63#include <menu.h>
64#endif
65
66#if USE_LIBFORM
67#include <form.h>
68#endif
69
70#ifdef NCURSES_VERSION
71
72#ifdef TRACE
73static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
74extern unsigned _nc_tracing;
75#endif
76
77#else
78
79#define mmask_t chtype		/* not specified in XSI */
80
81#ifdef CURSES_ACS_ARRAY
82#define ACS_S3          (CURSES_ACS_ARRAY['p'])		/* scan line 3 */
83#define ACS_S7          (CURSES_ACS_ARRAY['r'])		/* scan line 7 */
84#define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])		/* less/equal */
85#define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])		/* greater/equal */
86#define ACS_PI          (CURSES_ACS_ARRAY['{'])		/* Pi */
87#define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])		/* not equal */
88#define ACS_STERLING    (CURSES_ACS_ARRAY['}'])		/* UK pound sign */
89#else
90#define ACS_S3          (A_ALTCHARSET + 'p')	/* scan line 3 */
91#define ACS_S7          (A_ALTCHARSET + 'r')	/* scan line 7 */
92#define ACS_LEQUAL      (A_ALTCHARSET + 'y')	/* less/equal */
93#define ACS_GEQUAL      (A_ALTCHARSET + 'z')	/* greater/equal */
94#define ACS_PI          (A_ALTCHARSET + '{')	/* Pi */
95#define ACS_NEQUAL      (A_ALTCHARSET + '|')	/* not equal */
96#define ACS_STERLING    (A_ALTCHARSET + '}')	/* UK pound sign */
97#endif
98
99#ifdef CURSES_WACS_ARRAY
100#define WACS_S3         (&(CURSES_WACS_ARRAY['p']))	/* scan line 3 */
101#define WACS_S7         (&(CURSES_WACS_ARRAY['r']))	/* scan line 7 */
102#define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))	/* less/equal */
103#define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))	/* greater/equal */
104#define WACS_PI         (&(CURSES_WACS_ARRAY['{']))	/* Pi */
105#define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))	/* not equal */
106#define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))	/* UK pound sign */
107#endif
108
109#endif
110
111#define P(string)	printw("%s\n", string)
112
113#define BLANK		' '	/* this is the background character */
114
115#undef max_colors
116static int max_colors;		/* the actual number of colors we'll use */
117static int min_colors;		/* the minimum color code */
118
119#undef max_pairs
120static int max_pairs;		/* ...and the number of color pairs */
121
122typedef struct {
123    short red;
124    short green;
125    short blue;
126} RGB_DATA;
127
128static RGB_DATA *all_colors;
129
130static void main_menu(bool);
131
132/* The behavior of mvhline, mvvline for negative/zero length is unspecified,
133 * though we can rely on negative x/y values to stop the macro.
134 */
135static void
136do_h_line(int y, int x, chtype c, int to)
137{
138    if ((to) > (x))
139	mvhline(y, x, c, (to) - (x));
140}
141
142static void
143do_v_line(int y, int x, chtype c, int to)
144{
145    if ((to) > (y))
146	mvvline(y, x, c, (to) - (y));
147}
148
149static void
150Repaint(void)
151{
152    touchwin(stdscr);
153    touchwin(curscr);
154    wrefresh(curscr);
155}
156
157/* Common function to allow ^T to toggle trace-mode in the middle of a test
158 * so that trace-files can be made smaller.
159 */
160static int
161wGetchar(WINDOW *win)
162{
163    int c;
164#ifdef TRACE
165    while ((c = wgetch(win)) == CTRL('T')) {
166	if (_nc_tracing) {
167	    save_trace = _nc_tracing;
168	    _tracef("TOGGLE-TRACING OFF");
169	    _nc_tracing = 0;
170	} else {
171	    _nc_tracing = save_trace;
172	}
173	trace(_nc_tracing);
174	if (_nc_tracing)
175	    _tracef("TOGGLE-TRACING ON");
176    }
177#else
178    c = wgetch(win);
179#endif
180    return c;
181}
182#define Getchar() wGetchar(stdscr)
183
184/* replaces wgetnstr(), since we want to be able to edit values */
185static void
186wGetstring(WINDOW *win, char *buffer, int limit)
187{
188    int y0, x0, x, ch;
189    bool done = FALSE;
190
191    echo();
192    getyx(win, y0, x0);
193    wattrset(win, A_REVERSE);
194
195    x = strlen(buffer);
196    while (!done) {
197	if (x > (int) strlen(buffer))
198	    x = (int) strlen(buffer);
199	wmove(win, y0, x0);
200	wprintw(win, "%-*s", limit, buffer);
201	wmove(win, y0, x0 + x);
202	switch (ch = wGetchar(win)) {
203	case '\n':
204	case KEY_ENTER:
205	    done = TRUE;
206	    break;
207	case CTRL('U'):
208	    *buffer = '\0';
209	    break;
210	case '\b':
211	case KEY_BACKSPACE:
212	case KEY_DC:
213	    if (x > 0) {
214		int j;
215		for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
216		    ;
217		}
218	    } else {
219		beep();
220	    }
221	    break;
222	case KEY_LEFT:
223	    if (x > 0) {
224		--x;
225	    } else {
226		flash();
227	    }
228	    break;
229	case KEY_RIGHT:
230	    ++x;
231	    break;
232	default:
233	    if (!isprint(ch) || ch >= KEY_MIN) {
234		beep();
235	    } else if ((int) strlen(buffer) < limit) {
236		int j;
237		for (j = strlen(buffer) + 1; j > x; --j) {
238		    buffer[j] = buffer[j - 1];
239		}
240		buffer[x++] = ch;
241	    } else {
242		flash();
243	    }
244	}
245    }
246
247    wattroff(win, A_REVERSE);
248    wmove(win, y0, x0);
249    noecho();
250}
251
252#if USE_WIDEC_SUPPORT
253static int
254wGet_wchar(WINDOW *win, wint_t *result)
255{
256    int c;
257#ifdef TRACE
258    while ((c = wget_wch(win, result)) == CTRL('T')) {
259	if (_nc_tracing) {
260	    save_trace = _nc_tracing;
261	    _tracef("TOGGLE-TRACING OFF");
262	    _nc_tracing = 0;
263	} else {
264	    _nc_tracing = save_trace;
265	}
266	trace(_nc_tracing);
267	if (_nc_tracing)
268	    _tracef("TOGGLE-TRACING ON");
269    }
270#else
271    c = wget_wch(win, result);
272#endif
273    return c;
274}
275#define Get_wchar(result) wGet_wchar(stdscr, result)
276
277/* replaces wgetn_wstr(), since we want to be able to edit values */
278static void
279wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
280{
281    int y0, x0, x;
282    wint_t ch;
283    bool done = FALSE;
284    bool fkey = FALSE;
285
286    echo();
287    getyx(win, y0, x0);
288    wattrset(win, A_REVERSE);
289
290    x = wcslen(buffer);
291    while (!done) {
292	if (x > (int) wcslen(buffer))
293	    x = (int) wcslen(buffer);
294
295	/* clear the "window' */
296	wmove(win, y0, x0);
297	wprintw(win, "%*s", limit, " ");
298
299	/* write the existing buffer contents */
300	wmove(win, y0, x0);
301	waddnwstr(win, buffer, limit);
302
303	/* positions the cursor past character 'x' */
304	wmove(win, y0, x0);
305	waddnwstr(win, buffer, x);
306
307	switch (wGet_wchar(win, &ch)) {
308	case KEY_CODE_YES:
309	    fkey = TRUE;
310	    switch (ch) {
311	    case KEY_ENTER:
312		ch = '\n';
313		fkey = FALSE;
314		break;
315	    case KEY_BACKSPACE:
316	    case KEY_DC:
317		ch = '\b';
318		fkey = FALSE;
319		break;
320	    case KEY_LEFT:
321	    case KEY_RIGHT:
322		break;
323	    default:
324		ch = (wint_t) -1;
325		break;
326	    }
327	    break;
328	case OK:
329	    fkey = FALSE;
330	    break;
331	default:
332	    ch = (wint_t) -1;
333	    fkey = TRUE;
334	    break;
335	}
336
337	switch (ch) {
338	case '\n':
339	    done = TRUE;
340	    break;
341	case CTRL('U'):
342	    *buffer = '\0';
343	    break;
344	case '\b':
345	    if (x > 0) {
346		int j;
347		for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
348		    ;
349		}
350	    } else {
351		beep();
352	    }
353	    break;
354	case KEY_LEFT:
355	    if (x > 0) {
356		--x;
357	    } else {
358		beep();
359	    }
360	    break;
361	case KEY_RIGHT:
362	    ++x;
363	    break;
364	default:
365	    if (fkey) {
366		beep();
367	    } else if ((int) wcslen(buffer) < limit) {
368		int j;
369		for (j = wcslen(buffer) + 1; j > x; --j) {
370		    buffer[j] = buffer[j - 1];
371		}
372		buffer[x++] = ch;
373	    } else {
374		beep();
375	    }
376	}
377    }
378
379    wattroff(win, A_REVERSE);
380    wmove(win, y0, x0);
381    noecho();
382}
383
384#endif
385
386static void
387Pause(void)
388{
389    move(LINES - 1, 0);
390    addstr("Press any key to continue... ");
391    (void) Getchar();
392}
393
394static void
395Cannot(const char *what)
396{
397    printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
398    Pause();
399}
400
401static void
402ShellOut(bool message)
403{
404    if (message)
405	addstr("Shelling out...");
406    def_prog_mode();
407    endwin();
408    system("sh");
409    if (message)
410	addstr("returned from shellout.\n");
411    refresh();
412}
413
414#ifdef NCURSES_MOUSE_VERSION
415/*
416 * This function is the same as _tracemouse(), but we cannot count on that
417 * being available in the non-debug library.
418 */
419static const char *
420mouse_decode(MEVENT const *ep)
421{
422    static char buf[80 + (5 * 10) + (32 * 15)];
423
424    (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
425		   ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
426
427#define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
428
429    SHOW(BUTTON1_RELEASED, "release-1");
430    SHOW(BUTTON1_PRESSED, "press-1");
431    SHOW(BUTTON1_CLICKED, "click-1");
432    SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
433    SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
434#if NCURSES_MOUSE_VERSION == 1
435    SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
436#endif
437
438    SHOW(BUTTON2_RELEASED, "release-2");
439    SHOW(BUTTON2_PRESSED, "press-2");
440    SHOW(BUTTON2_CLICKED, "click-2");
441    SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
442    SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
443#if NCURSES_MOUSE_VERSION == 1
444    SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
445#endif
446
447    SHOW(BUTTON3_RELEASED, "release-3");
448    SHOW(BUTTON3_PRESSED, "press-3");
449    SHOW(BUTTON3_CLICKED, "click-3");
450    SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
451    SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
452#if NCURSES_MOUSE_VERSION == 1
453    SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
454#endif
455
456    SHOW(BUTTON4_RELEASED, "release-4");
457    SHOW(BUTTON4_PRESSED, "press-4");
458    SHOW(BUTTON4_CLICKED, "click-4");
459    SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
460    SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
461#if NCURSES_MOUSE_VERSION == 1
462    SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
463#endif
464
465#if NCURSES_MOUSE_VERSION == 2
466    SHOW(BUTTON5_RELEASED, "release-5");
467    SHOW(BUTTON5_PRESSED, "press-5");
468    SHOW(BUTTON5_CLICKED, "click-5");
469    SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
470    SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
471#endif
472
473    SHOW(BUTTON_CTRL, "ctrl");
474    SHOW(BUTTON_SHIFT, "shift");
475    SHOW(BUTTON_ALT, "alt");
476    SHOW(ALL_MOUSE_EVENTS, "all-events");
477    SHOW(REPORT_MOUSE_POSITION, "position");
478
479#undef SHOW
480
481    if (buf[strlen(buf) - 1] == ' ')
482	buf[strlen(buf) - 2] = '\0';
483    (void) strcat(buf, "}");
484    return (buf);
485}
486#endif /* NCURSES_MOUSE_VERSION */
487
488/****************************************************************************
489 *
490 * Character input test
491 *
492 ****************************************************************************/
493
494static void
495setup_getch(WINDOW *win, bool flags[])
496{
497    keypad(win, flags['k']);	/* should be redundant, but for testing */
498    meta(win, flags['m']);	/* force this to a known state */
499    if (flags['e'])
500	echo();
501    else
502	noecho();
503}
504
505static void
506wgetch_help(WINDOW *win, bool flags[])
507{
508    static const char *help[] =
509    {
510	"e -- toggle echo mode"
511	,"g -- triggers a getstr test"
512	,"k -- toggle keypad/literal mode"
513	,"m -- toggle meta (7-bit/8-bit) mode"
514	,"q -- quit (x also exits)"
515	,"s -- shell out\n"
516	,"w -- create a new window"
517#ifdef SIGTSTP
518	,"z -- suspend this process"
519#endif
520    };
521    int y, x;
522    unsigned chk = ((SIZEOF(help) + 1) / 2);
523    unsigned n;
524
525    getyx(win, y, x);
526    move(0, 0);
527    printw("Type any key to see its %s value.  Also:\n",
528	   flags['k'] ? "keypad" : "literal");
529    for (n = 0; n < SIZEOF(help); ++n) {
530	int row = 1 + (n % chk);
531	int col = (n >= chk) ? COLS / 2 : 0;
532	int flg = ((strstr(help[n], "toggle") != 0)
533		   && (flags[UChar(*help[n])] != FALSE));
534	if (flg)
535	    standout();
536	mvprintw(row, col, "%s", help[n]);
537	if (col == 0)
538	    clrtoeol();
539	if (flg)
540	    standend();
541    }
542    wrefresh(stdscr);
543    wmove(win, y, x);
544}
545
546static void
547wgetch_wrap(WINDOW *win, int first_y)
548{
549    int last_y = getmaxy(win) - 1;
550    int y = getcury(win) + 1;
551
552    if (y >= last_y)
553	y = first_y;
554    wmove(win, y, 0);
555    wclrtoeol(win);
556}
557
558#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
559typedef struct {
560    WINDOW *text;
561    WINDOW *frame;
562} WINSTACK;
563
564static WINSTACK *winstack = 0;
565static unsigned len_winstack = 0;
566
567static void
568remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
569{
570    unsigned need = (level + 1) * 2;
571
572    if (winstack == 0) {
573	len_winstack = 20;
574	winstack = (WINSTACK *) malloc(len_winstack * sizeof(WINSTACK));
575    } else if (need >= len_winstack) {
576	len_winstack = need;
577	winstack = (WINSTACK *) realloc(winstack, len_winstack * sizeof(WINSTACK));
578    }
579    winstack[level].text = txt_win;
580    winstack[level].frame = box_win;
581}
582
583/*
584 * For wgetch_test(), we create pairs of windows - one for a box, one for text.
585 * Resize both and paint the box in the parent.
586 */
587static void
588resize_boxes(unsigned level, WINDOW *win)
589{
590    unsigned n;
591    int base = 5;
592    int high = LINES - base;
593    int wide = COLS;
594
595    touchwin(stdscr);
596    wnoutrefresh(stdscr);
597
598    /* FIXME: this chunk should be done in resizeterm() */
599    slk_touch();
600    slk_clear();
601    slk_noutrefresh();
602
603    for (n = 0; n < level; ++n) {
604	wresize(winstack[n].frame, high, wide);
605	wresize(winstack[n].text, high - 2, wide - 2);
606	high -= 2;
607	wide -= 2;
608	werase(winstack[n].text);
609	box(winstack[n].frame, 0, 0);
610	wnoutrefresh(winstack[n].frame);
611	wprintw(winstack[n].text,
612		"size %dx%d\n",
613		getmaxy(winstack[n].text),
614		getmaxx(winstack[n].text));
615	wnoutrefresh(winstack[n].text);
616	if (winstack[n].text == win)
617	    break;
618    }
619    doupdate();
620}
621#else
622#define remember_boxes(level,text,frame)	/* nothing */
623#endif
624
625static void
626wgetch_test(unsigned level, WINDOW *win, int delay)
627{
628    char buf[BUFSIZ];
629    int first_y, first_x;
630    int c;
631    int incount = 0;
632    bool flags[256];
633    bool blocking = (delay < 0);
634
635    memset(flags, FALSE, sizeof(flags));
636    flags[UChar('k')] = (win == stdscr);
637
638    setup_getch(win, flags);
639    wtimeout(win, delay);
640    getyx(win, first_y, first_x);
641
642    wgetch_help(win, flags);
643    wsetscrreg(win, first_y, getmaxy(win) - 1);
644    scrollok(win, TRUE);
645
646    for (;;) {
647	while ((c = wGetchar(win)) == ERR) {
648	    incount++;
649	    if (blocking) {
650		(void) wprintw(win, "%05d: input error", incount);
651		break;
652	    } else {
653		(void) wprintw(win, "%05d: input timed out", incount);
654	    }
655	    wgetch_wrap(win, first_y);
656	}
657	if (c == ERR && blocking) {
658	    wprintw(win, "ERR");
659	    wgetch_wrap(win, first_y);
660	} else if (c == 'x' || c == 'q') {
661	    break;
662	} else if (c == 'e') {
663	    flags[UChar('e')] = !flags[UChar('e')];
664	    setup_getch(win, flags);
665	    wgetch_help(win, flags);
666	} else if (c == 'g') {
667	    waddstr(win, "getstr test: ");
668	    echo();
669	    wgetnstr(win, buf, sizeof(buf) - 1);
670	    noecho();
671	    wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
672	    wclrtoeol(win);
673	    wgetch_wrap(win, first_y);
674	} else if (c == 'k') {
675	    flags[UChar('k')] = !flags[UChar('k')];
676	    setup_getch(win, flags);
677	    wgetch_help(win, flags);
678	} else if (c == 'm') {
679	    flags[UChar('m')] = !flags[UChar('m')];
680	    setup_getch(win, flags);
681	    wgetch_help(win, flags);
682	} else if (c == 's') {
683	    ShellOut(TRUE);
684	} else if (c == 'w') {
685	    int high = getmaxy(win) - 1 - first_y + 1;
686	    int wide = getmaxx(win) - first_x;
687	    int old_y, old_x;
688	    int new_y = first_y + getbegy(win);
689	    int new_x = first_x + getbegx(win);
690
691	    getyx(win, old_y, old_x);
692	    if (high > 2 && wide > 2) {
693		WINDOW *wb = newwin(high, wide, new_y, new_x);
694		WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
695
696		box(wb, 0, 0);
697		wrefresh(wb);
698		wmove(wi, 0, 0);
699		remember_boxes(level, wi, wb);
700		wgetch_test(level + 1, wi, delay);
701		delwin(wi);
702		delwin(wb);
703
704		wgetch_help(win, flags);
705		wmove(win, old_y, old_x);
706		touchwin(win);
707		wrefresh(win);
708		doupdate();
709	    }
710#ifdef SIGTSTP
711	} else if (c == 'z') {
712	    kill(getpid(), SIGTSTP);
713#endif
714	} else {
715	    wprintw(win, "Key pressed: %04o ", c);
716#ifdef NCURSES_MOUSE_VERSION
717	    if (c == KEY_MOUSE) {
718		int y, x;
719		MEVENT event;
720
721		getmouse(&event);
722		wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
723		getyx(win, y, x);
724		move(event.y, event.x);
725		addch('*');
726		wmove(win, y, x);
727	    } else
728#endif /* NCURSES_MOUSE_VERSION */
729	    if (c >= KEY_MIN) {
730#if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
731		if (c == KEY_RESIZE) {
732		    resize_boxes(level, win);
733		}
734#endif
735		(void) waddstr(win, keyname(c));
736	    } else if (c > 0x80) {
737		unsigned c2 = (c & 0x7f);
738		if (isprint(c2))
739		    (void) wprintw(win, "M-%c", UChar(c2));
740		else
741		    (void) wprintw(win, "M-%s", unctrl(c2));
742		waddstr(win, " (high-half character)");
743	    } else {
744		if (isprint(c))
745		    (void) wprintw(win, "%c (ASCII printable character)", c);
746		else
747		    (void) wprintw(win, "%s (ASCII control character)",
748				   unctrl(UChar(c)));
749	    }
750	    wgetch_wrap(win, first_y);
751	}
752    }
753
754    wtimeout(win, -1);
755}
756
757static int
758begin_getch_test(void)
759{
760    char buf[BUFSIZ];
761    int delay;
762
763    refresh();
764
765#ifdef NCURSES_MOUSE_VERSION
766    mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
767#endif
768
769    (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
770    echo();
771    getnstr(buf, sizeof(buf) - 1);
772    noecho();
773    nonl();
774
775    if (isdigit(UChar(buf[0]))) {
776	delay = atoi(buf) * 100;
777    } else {
778	delay = -1;
779    }
780    raw();
781    move(5, 0);
782    return delay;
783}
784
785static void
786finish_getch_test(void)
787{
788#ifdef NCURSES_MOUSE_VERSION
789    mousemask(0, (mmask_t *) 0);
790#endif
791    erase();
792    noraw();
793    nl();
794    endwin();
795}
796
797static void
798getch_test(void)
799{
800    int delay = begin_getch_test();
801    wgetch_test(0, stdscr, delay);
802    finish_getch_test();
803}
804
805#if USE_WIDEC_SUPPORT
806/*
807 * For wgetch_test(), we create pairs of windows - one for a box, one for text.
808 * Resize both and paint the box in the parent.
809 */
810#ifdef KEY_RESIZE
811static void
812resize_wide_boxes(unsigned level, WINDOW *win)
813{
814    unsigned n;
815    int base = 5;
816    int high = LINES - base;
817    int wide = COLS;
818
819    touchwin(stdscr);
820    wnoutrefresh(stdscr);
821
822    /* FIXME: this chunk should be done in resizeterm() */
823    slk_touch();
824    slk_clear();
825    slk_noutrefresh();
826
827    for (n = 0; n < level; ++n) {
828	wresize(winstack[n].frame, high, wide);
829	wresize(winstack[n].text, high - 2, wide - 2);
830	high -= 2;
831	wide -= 2;
832	werase(winstack[n].text);
833	box_set(winstack[n].frame, 0, 0);
834	wnoutrefresh(winstack[n].frame);
835	wprintw(winstack[n].text,
836		"size %dx%d\n",
837		getmaxy(winstack[n].text),
838		getmaxx(winstack[n].text));
839	wnoutrefresh(winstack[n].text);
840	if (winstack[n].text == win)
841	    break;
842    }
843    doupdate();
844}
845#endif /* KEY_RESIZE */
846
847static char *
848wcstos(const wchar_t *src)
849{
850    int need;
851    mbstate_t state;
852    char *result = 0;
853    const wchar_t *tmp = src;
854
855    memset(&state, 0, sizeof(state));
856    if ((need = wcsrtombs(0, &tmp, 0, &state)) > 0) {
857	unsigned have = need;
858	result = (char *) calloc(have + 1, 1);
859	tmp = src;
860	if (wcsrtombs(result, &tmp, have, &state) != have) {
861	    free(result);
862	    result = 0;
863	}
864    }
865    return result;
866}
867
868static void
869wget_wch_test(unsigned level, WINDOW *win, int delay)
870{
871    wchar_t wchar_buf[BUFSIZ];
872    wint_t wint_buf[BUFSIZ];
873    int first_y, first_x;
874    wint_t c;
875    int incount = 0;
876    bool flags[256];
877    bool blocking = (delay < 0);
878    int y, x, code;
879    char *temp;
880
881    memset(flags, FALSE, sizeof(flags));
882    flags[UChar('k')] = (win == stdscr);
883
884    setup_getch(win, flags);
885    wtimeout(win, delay);
886    getyx(win, first_y, first_x);
887
888    wgetch_help(win, flags);
889    wsetscrreg(win, first_y, getmaxy(win) - 1);
890    scrollok(win, TRUE);
891
892    for (;;) {
893	while ((code = wGet_wchar(win, &c)) == ERR) {
894	    incount++;
895	    if (blocking) {
896		(void) wprintw(win, "%05d: input error", incount);
897		break;
898	    } else {
899		(void) wprintw(win, "%05d: input timed out", incount);
900	    }
901	    wgetch_wrap(win, first_y);
902	}
903	if (code == ERR && blocking) {
904	    wprintw(win, "ERR");
905	    wgetch_wrap(win, first_y);
906	} else if (c == 'x' || c == 'q') {
907	    break;
908	} else if (c == 'e') {
909	    flags[UChar('e')] = !flags[UChar('e')];
910	    setup_getch(win, flags);
911	    wgetch_help(win, flags);
912	} else if (c == 'g') {
913	    waddstr(win, "getstr test: ");
914	    echo();
915	    code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
916	    noecho();
917	    if (code == ERR) {
918		wprintw(win, "wgetn_wstr returns an error.");
919	    } else {
920		int n;
921		for (n = 0; (wchar_buf[n] = wint_buf[n]) != 0; ++n) ;
922		if ((temp = wcstos(wchar_buf)) != 0) {
923		    wprintw(win, "I saw %d characters:\n\t`%s'.",
924			    wcslen(wchar_buf), temp);
925		    free(temp);
926		} else {
927		    wprintw(win, "I saw %d characters (cannot convert).",
928			    wcslen(wchar_buf));
929		}
930	    }
931	    wclrtoeol(win);
932	    wgetch_wrap(win, first_y);
933	} else if (c == 'k') {
934	    flags[UChar('k')] = !flags[UChar('k')];
935	    setup_getch(win, flags);
936	    wgetch_help(win, flags);
937	} else if (c == 'm') {
938	    flags[UChar('m')] = !flags[UChar('m')];
939	    setup_getch(win, flags);
940	    wgetch_help(win, flags);
941	} else if (c == 's') {
942	    ShellOut(TRUE);
943	} else if (c == 'w') {
944	    int high = getmaxy(win) - 1 - first_y + 1;
945	    int wide = getmaxx(win) - first_x;
946	    int old_y, old_x;
947	    int new_y = first_y + getbegy(win);
948	    int new_x = first_x + getbegx(win);
949
950	    getyx(win, old_y, old_x);
951	    if (high > 2 && wide > 2) {
952		WINDOW *wb = newwin(high, wide, new_y, new_x);
953		WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
954
955		box_set(wb, 0, 0);
956		wrefresh(wb);
957		wmove(wi, 0, 0);
958		remember_boxes(level, wi, wb);
959		wget_wch_test(level + 1, wi, delay);
960		delwin(wi);
961		delwin(wb);
962
963		wgetch_help(win, flags);
964		wmove(win, old_y, old_x);
965		touchwin(win);
966		wrefresh(win);
967	    }
968#ifdef SIGTSTP
969	} else if (c == 'z') {
970	    kill(getpid(), SIGTSTP);
971#endif
972	} else {
973	    wprintw(win, "Key pressed: %04o ", c);
974#ifdef NCURSES_MOUSE_VERSION
975	    if (c == KEY_MOUSE) {
976		MEVENT event;
977
978		getmouse(&event);
979		wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
980		getyx(win, y, x);
981		move(event.y, event.x);
982		addch('*');
983		wmove(win, y, x);
984	    } else
985#endif /* NCURSES_MOUSE_VERSION */
986	    if (code == KEY_CODE_YES) {
987#ifdef KEY_RESIZE
988		if (c == KEY_RESIZE) {
989		    resize_wide_boxes(level, win);
990		}
991#endif
992		(void) waddstr(win, key_name((wchar_t) c));
993	    } else {
994		if (c < 256 && iscntrl(c)) {
995		    (void) wprintw(win, "%s (control character)", unctrl(c));
996		} else {
997		    wchar_t c2 = c;
998		    waddnwstr(win, &c2, 1);
999		    (void) wprintw(win, " = %#x (printable character)", c);
1000		}
1001	    }
1002	    wgetch_wrap(win, first_y);
1003	}
1004    }
1005
1006    wtimeout(win, -1);
1007}
1008
1009static void
1010get_wch_test(void)
1011{
1012    int delay = begin_getch_test();
1013    wget_wch_test(0, stdscr, delay);
1014    finish_getch_test();
1015}
1016#endif
1017
1018/****************************************************************************
1019 *
1020 * Character attributes test
1021 *
1022 ****************************************************************************/
1023
1024#define MAX_ATTRSTRING 31
1025#define LEN_ATTRSTRING 26
1026
1027static char attr_test_string[MAX_ATTRSTRING + 1];
1028
1029static void
1030attr_legend(WINDOW *helpwin)
1031{
1032    int row = 1;
1033    int col = 1;
1034
1035    mvwprintw(helpwin, row++, col,
1036	      "q or ESC to exit.");
1037    mvwprintw(helpwin, row++, col,
1038	      "^L repaints.");
1039    ++row;
1040    mvwprintw(helpwin, row++, col,
1041	      "Modify the test strings:");
1042    mvwprintw(helpwin, row++, col,
1043	      "  A digit sets gaps on each side of displayed attributes");
1044    mvwprintw(helpwin, row++, col,
1045	      "  </> shifts the text left/right. ");
1046    ++row;
1047    mvwprintw(helpwin, row++, col,
1048	      "Toggles:");
1049    if (has_colors()) {
1050	mvwprintw(helpwin, row++, col,
1051		  "  f/F/b/F toggle foreground/background background color");
1052	mvwprintw(helpwin, row++, col,
1053		  "  t/T     toggle text/background color attribute");
1054    }
1055    mvwprintw(helpwin, row++, col,
1056	      "  a/A     toggle ACS (alternate character set) mapping");
1057    mvwprintw(helpwin, row++, col,
1058	      "  v/V     toggle video attribute to combine with each line");
1059}
1060
1061static void
1062show_color_attr(int fg, int bg, int tx)
1063{
1064    if (has_colors()) {
1065	printw("  Colors (fg %d, bg %d", fg, bg);
1066	if (tx >= 0)
1067	    printw(", text %d", tx);
1068	printw("),");
1069    }
1070}
1071
1072static bool
1073cycle_color_attr(int ch, int *fg, int *bg, int *tx)
1074{
1075    bool error = FALSE;
1076
1077    if (has_colors()) {
1078	switch (ch) {
1079	case 'f':
1080	    *fg = (*fg + 1);
1081	    break;
1082	case 'F':
1083	    *fg = (*fg - 1);
1084	    break;
1085	case 'b':
1086	    *bg = (*bg + 1);
1087	    break;
1088	case 'B':
1089	    *bg = (*bg - 1);
1090	    break;
1091	case 't':
1092	    *tx = (*tx + 1);
1093	    break;
1094	case 'T':
1095	    *tx = (*tx - 1);
1096	    break;
1097	default:
1098	    beep();
1099	    error = TRUE;
1100	    break;
1101	}
1102	if (*fg >= COLORS)
1103	    *fg = min_colors;
1104	if (*fg < min_colors)
1105	    *fg = COLORS - 1;
1106	if (*bg >= COLORS)
1107	    *bg = min_colors;
1108	if (*bg < min_colors)
1109	    *bg = COLORS - 1;
1110	if (*tx >= COLORS)
1111	    *tx = -1;
1112	if (*tx < -1)
1113	    *tx = COLORS - 1;
1114    } else {
1115	beep();
1116	error = TRUE;
1117    }
1118    return error;
1119}
1120
1121static void
1122adjust_attr_string(int adjust)
1123{
1124    int first = ((int) UChar(attr_test_string[0])) + adjust;
1125    int last = first + LEN_ATTRSTRING;
1126
1127    if (first >= ' ' && last <= '~') {	/* 32..126 */
1128	int j, k;
1129	for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1130	    attr_test_string[j] = k;
1131	    if (((k + 1 - first) % 5) == 0) {
1132		++j;
1133		if (j < MAX_ATTRSTRING)
1134		    attr_test_string[j] = ' ';
1135	    }
1136	}
1137	while (j < MAX_ATTRSTRING)
1138	    attr_test_string[j++] = ' ';
1139	attr_test_string[j] = '\0';
1140    } else {
1141	beep();
1142    }
1143}
1144
1145static void
1146init_attr_string(void)
1147{
1148    attr_test_string[0] = 'a';
1149    adjust_attr_string(0);
1150}
1151
1152static int
1153show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1154{
1155    int ncv = tigetnum("ncv");
1156    chtype test = attr & (chtype) (~A_ALTCHARSET);
1157
1158    if (arrow)
1159	mvprintw(row, 5, "-->");
1160    mvprintw(row, 8, "%s mode:", name);
1161    mvprintw(row, 24, "|");
1162    if (skip)
1163	printw("%*s", skip, " ");
1164    /*
1165     * Just for testing, write text using the alternate character set one
1166     * character at a time (to pass its rendition directly), and use the
1167     * string operation for the other attributes.
1168     */
1169    if (attr & A_ALTCHARSET) {
1170	const char *s;
1171	chtype ch;
1172
1173	for (s = attr_test_string; *s != '\0'; ++s) {
1174	    ch = UChar(*s);
1175	    addch(ch | attr);
1176	}
1177    } else {
1178	attrset(attr);
1179	addstr(attr_test_string);
1180	attroff(attr);
1181    }
1182    if (skip)
1183	printw("%*s", skip, " ");
1184    printw("|");
1185    if (test != A_NORMAL) {
1186	if (!(termattrs() & test)) {
1187	    printw(" (N/A)");
1188	} else {
1189	    if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1190		static const chtype table[] =
1191		{
1192		    A_STANDOUT,
1193		    A_UNDERLINE,
1194		    A_REVERSE,
1195		    A_BLINK,
1196		    A_DIM,
1197		    A_BOLD,
1198		    A_INVIS,
1199		    A_PROTECT,
1200		    A_ALTCHARSET
1201		};
1202		unsigned n;
1203		bool found = FALSE;
1204		for (n = 0; n < SIZEOF(table); n++) {
1205		    if ((table[n] & attr) != 0
1206			&& ((1 << n) & ncv) != 0) {
1207			found = TRUE;
1208			break;
1209		    }
1210		}
1211		if (found)
1212		    printw(" (NCV)");
1213	    }
1214	    if ((termattrs() & test) != test)
1215		printw(" (Part)");
1216	}
1217    }
1218    return row + 2;
1219}
1220/* *INDENT-OFF* */
1221static const struct {
1222    attr_t			attr;
1223    NCURSES_CONST char *	name;
1224} attrs_to_test[] = {
1225    { A_STANDOUT,	"STANDOUT" },
1226    { A_REVERSE,	"REVERSE" },
1227    { A_BOLD,		"BOLD" },
1228    { A_UNDERLINE,	"UNDERLINE" },
1229    { A_DIM,		"DIM" },
1230    { A_BLINK,		"BLINK" },
1231    { A_PROTECT,	"PROTECT" },
1232    { A_INVIS,		"INVISIBLE" },
1233    { A_NORMAL,		"NORMAL" },
1234};
1235/* *INDENT-ON* */
1236
1237static bool
1238attr_getc(int *skip, int *fg, int *bg, int *tx, int *ac, unsigned *kc)
1239{
1240    bool result = TRUE;
1241    bool error = FALSE;
1242    WINDOW *helpwin;
1243
1244    do {
1245	int ch = Getchar();
1246
1247	error = FALSE;
1248	if (ch < 256 && isdigit(ch)) {
1249	    *skip = (ch - '0');
1250	} else {
1251	    switch (ch) {
1252	    case CTRL('L'):
1253		Repaint();
1254		break;
1255	    case '?':
1256		if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1257		    box(helpwin, 0, 0);
1258		    attr_legend(helpwin);
1259		    wGetchar(helpwin);
1260		    delwin(helpwin);
1261		}
1262		break;
1263	    case 'a':
1264		*ac = 0;
1265		break;
1266	    case 'A':
1267		*ac = A_ALTCHARSET;
1268		break;
1269	    case 'v':
1270		if (*kc == 0)
1271		    *kc = SIZEOF(attrs_to_test) - 1;
1272		else
1273		    *kc -= 1;
1274		break;
1275	    case 'V':
1276		*kc += 1;
1277		if (*kc >= SIZEOF(attrs_to_test))
1278		    *kc = 0;
1279		break;
1280	    case '<':
1281		adjust_attr_string(-1);
1282		break;
1283	    case '>':
1284		adjust_attr_string(1);
1285		break;
1286	    case 'q':
1287	    case ESCAPE:
1288		result = FALSE;
1289		break;
1290	    default:
1291		error = cycle_color_attr(ch, fg, bg, tx);
1292		break;
1293	    }
1294	}
1295    } while (error);
1296    return result;
1297}
1298
1299static void
1300attr_test(void)
1301/* test text attributes */
1302{
1303    int n;
1304    int skip = tigetnum("xmc");
1305    int fg = COLOR_BLACK;	/* color pair 0 is special */
1306    int bg = COLOR_BLACK;
1307    int tx = -1;
1308    int ac = 0;
1309    unsigned j, k;
1310
1311    if (skip < 0)
1312	skip = 0;
1313
1314    n = skip;			/* make it easy */
1315    k = SIZEOF(attrs_to_test) - 1;
1316    init_attr_string();
1317
1318    do {
1319	int row = 2;
1320	chtype normal = A_NORMAL | BLANK;
1321	chtype extras = ac;
1322
1323	if (has_colors()) {
1324	    int pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1325	    if (pair != 0) {
1326		pair = 1;
1327		if (init_pair(pair, fg, bg) == ERR) {
1328		    beep();
1329		} else {
1330		    normal |= COLOR_PAIR(pair);
1331		}
1332	    }
1333	    if (tx >= 0) {
1334		pair = 2;
1335		if (init_pair(pair, tx, bg) == ERR) {
1336		    beep();
1337		} else {
1338		    extras |= COLOR_PAIR(pair);
1339		}
1340	    }
1341	}
1342	bkgd(normal);
1343	bkgdset(normal);
1344	erase();
1345
1346	box(stdscr, 0, 0);
1347	mvaddstr(0, 20, "Character attribute test display");
1348
1349	for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1350	    row = show_attr(row, n, j == k,
1351			    extras |
1352			    attrs_to_test[j].attr |
1353			    attrs_to_test[k].attr,
1354			    attrs_to_test[j].name);
1355	}
1356
1357	mvprintw(row, 8,
1358		 "This terminal does %shave the magic-cookie glitch",
1359		 tigetnum("xmc") > -1 ? "" : "not ");
1360	mvprintw(row + 1, 8, "Enter '?' for help.");
1361	show_color_attr(fg, bg, tx);
1362	printw("  ACS (%d)", ac != 0);
1363
1364	refresh();
1365    } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1366
1367    bkgdset(A_NORMAL | BLANK);
1368    erase();
1369    endwin();
1370}
1371
1372#if USE_WIDEC_SUPPORT
1373static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1374
1375static void
1376wide_adjust_attr_string(int adjust)
1377{
1378    int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1379    int last = first + LEN_ATTRSTRING;
1380
1381    if (first >= ' ' && last <= '~') {	/* 32..126 */
1382	int j, k;
1383	for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1384	    wide_attr_test_string[j] = k;
1385	    if (((k + 1 - first) % 5) == 0) {
1386		++j;
1387		if (j < MAX_ATTRSTRING)
1388		    wide_attr_test_string[j] = ' ';
1389	    }
1390	}
1391	while (j < MAX_ATTRSTRING)
1392	    wide_attr_test_string[j++] = ' ';
1393	wide_attr_test_string[j] = '\0';
1394    } else {
1395	beep();
1396    }
1397}
1398
1399static void
1400wide_init_attr_string(void)
1401{
1402    wide_attr_test_string[0] = 'a';
1403    wide_adjust_attr_string(0);
1404}
1405
1406static void
1407set_wide_background(short pair)
1408{
1409    cchar_t normal;
1410    wchar_t blank[2];
1411
1412    blank[0] = ' ';
1413    blank[1] = 0;
1414    setcchar(&normal, blank, A_NORMAL, pair, 0);
1415    bkgrnd(&normal);
1416    bkgrndset(&normal);
1417}
1418
1419static attr_t
1420get_wide_background(void)
1421{
1422    attr_t result = A_NORMAL;
1423    attr_t attr;
1424    cchar_t ch;
1425    short pair;
1426    wchar_t wch;
1427
1428    if (getbkgrnd(&ch) != ERR) {
1429	if (getcchar(&ch, &wch, &attr, &pair, 0) != ERR) {
1430	    result = attr;
1431	}
1432    }
1433    return result;
1434}
1435
1436static int
1437wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1438{
1439    int ncv = tigetnum("ncv");
1440    chtype test = attr & ~WA_ALTCHARSET;
1441
1442    if (arrow)
1443	mvprintw(row, 5, "-->");
1444    mvprintw(row, 8, "%s mode:", name);
1445    mvprintw(row, 24, "|");
1446    if (skip)
1447	printw("%*s", skip, " ");
1448
1449    /*
1450     * Just for testing, write text using the alternate character set one
1451     * character at a time (to pass its rendition directly), and use the
1452     * string operation for the other attributes.
1453     */
1454    if (attr & WA_ALTCHARSET) {
1455	const wchar_t *s;
1456	cchar_t ch;
1457
1458	for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1459	    wchar_t fill[2];
1460	    fill[0] = *s;
1461	    fill[1] = L'\0';
1462	    setcchar(&ch, fill, attr, pair, 0);
1463	    add_wch(&ch);
1464	}
1465    } else {
1466	attr_t old_attr;
1467	short old_pair;
1468
1469	attr_get(&old_attr, &old_pair, 0);
1470	attr_set(attr, pair, 0);
1471	addwstr(wide_attr_test_string);
1472	attr_set(old_attr, old_pair, 0);
1473    }
1474    if (skip)
1475	printw("%*s", skip, " ");
1476    printw("|");
1477    if (test != A_NORMAL) {
1478	if (!(term_attrs() & test)) {
1479	    printw(" (N/A)");
1480	} else {
1481	    if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1482		static const attr_t table[] =
1483		{
1484		    WA_STANDOUT,
1485		    WA_UNDERLINE,
1486		    WA_REVERSE,
1487		    WA_BLINK,
1488		    WA_DIM,
1489		    WA_BOLD,
1490		    WA_INVIS,
1491		    WA_PROTECT,
1492		    WA_ALTCHARSET
1493		};
1494		unsigned n;
1495		bool found = FALSE;
1496		for (n = 0; n < SIZEOF(table); n++) {
1497		    if ((table[n] & attr) != 0
1498			&& ((1 << n) & ncv) != 0) {
1499			found = TRUE;
1500			break;
1501		    }
1502		}
1503		if (found)
1504		    printw(" (NCV)");
1505	    }
1506	    if ((term_attrs() & test) != test)
1507		printw(" (Part)");
1508	}
1509    }
1510    return row + 2;
1511}
1512
1513static bool
1514wide_attr_getc(int *skip, int *fg, int *bg, int *tx, int *ac, unsigned *kc)
1515{
1516    bool result = TRUE;
1517    bool error = FALSE;
1518    WINDOW *helpwin;
1519
1520    do {
1521	int ch = Getchar();
1522
1523	error = FALSE;
1524	if (ch < 256 && isdigit(ch)) {
1525	    *skip = (ch - '0');
1526	} else {
1527	    switch (ch) {
1528	    case CTRL('L'):
1529		Repaint();
1530		break;
1531	    case '?':
1532		if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1533		    box_set(helpwin, 0, 0);
1534		    attr_legend(helpwin);
1535		    wGetchar(helpwin);
1536		    delwin(helpwin);
1537		}
1538		break;
1539	    case 'a':
1540		*ac = 0;
1541		break;
1542	    case 'A':
1543		*ac = A_ALTCHARSET;
1544		break;
1545	    case 'v':
1546		if (*kc == 0)
1547		    *kc = SIZEOF(attrs_to_test) - 1;
1548		else
1549		    *kc -= 1;
1550		break;
1551	    case 'V':
1552		*kc += 1;
1553		if (*kc >= SIZEOF(attrs_to_test))
1554		    *kc = 0;
1555		break;
1556	    case '<':
1557		wide_adjust_attr_string(-1);
1558		break;
1559	    case '>':
1560		wide_adjust_attr_string(1);
1561		break;
1562	    case 'q':
1563	    case ESCAPE:
1564		result = FALSE;
1565		break;
1566	    default:
1567		error = cycle_color_attr(ch, fg, bg, tx);
1568		break;
1569	    }
1570	}
1571    } while (error);
1572    return result;
1573}
1574
1575static void
1576wide_attr_test(void)
1577/* test text attributes using wide-character calls */
1578{
1579    int n;
1580    int skip = tigetnum("xmc");
1581    int fg = COLOR_BLACK;	/* color pair 0 is special */
1582    int bg = COLOR_BLACK;
1583    int tx = -1;
1584    int ac = 0;
1585    unsigned j, k;
1586
1587    if (skip < 0)
1588	skip = 0;
1589
1590    n = skip;			/* make it easy */
1591    k = SIZEOF(attrs_to_test) - 1;
1592    wide_init_attr_string();
1593
1594    do {
1595	int row = 2;
1596	short pair = 0;
1597	short extras = 0;
1598
1599	if (has_colors()) {
1600	    pair = (fg != COLOR_BLACK || bg != COLOR_BLACK);
1601	    if (pair != 0) {
1602		pair = 1;
1603		if (init_pair(pair, fg, bg) == ERR) {
1604		    beep();
1605		}
1606	    }
1607	    extras = pair;
1608	    if (tx >= 0) {
1609		extras = 2;
1610		if (init_pair(extras, tx, bg) == ERR) {
1611		    beep();
1612		}
1613	    }
1614	}
1615	set_wide_background(pair);
1616	erase();
1617
1618	box_set(stdscr, 0, 0);
1619	mvaddstr(0, 20, "Character attribute test display");
1620
1621	for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1622	    row = wide_show_attr(row, n, j == k,
1623				 ac |
1624				 attrs_to_test[j].attr |
1625				 attrs_to_test[k].attr,
1626				 extras,
1627				 attrs_to_test[j].name);
1628	}
1629
1630	mvprintw(row, 8,
1631		 "This terminal does %shave the magic-cookie glitch",
1632		 tigetnum("xmc") > -1 ? "" : "not ");
1633	mvprintw(row + 1, 8, "Enter '?' for help.");
1634	show_color_attr(fg, bg, tx);
1635	printw("  ACS (%d)", ac != 0);
1636
1637	refresh();
1638    } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1639
1640    set_wide_background(0);
1641    erase();
1642    endwin();
1643}
1644#endif
1645
1646/****************************************************************************
1647 *
1648 * Color support tests
1649 *
1650 ****************************************************************************/
1651
1652static NCURSES_CONST char *the_color_names[] =
1653{
1654    "black",
1655    "red",
1656    "green",
1657    "yellow",
1658    "blue",
1659    "magenta",
1660    "cyan",
1661    "white",
1662    "BLACK",
1663    "RED",
1664    "GREEN",
1665    "YELLOW",
1666    "BLUE",
1667    "MAGENTA",
1668    "CYAN",
1669    "WHITE"
1670};
1671
1672static void
1673show_color_name(int y, int x, int color, bool wide)
1674{
1675    if (move(y, x) != ERR) {
1676	char temp[80];
1677	int width = 8;
1678
1679	if (wide) {
1680	    sprintf(temp, "%02d", color);
1681	    width = 4;
1682	} else if (color >= 8) {
1683	    sprintf(temp, "[%02d]", color);
1684	} else {
1685	    strcpy(temp, the_color_names[color]);
1686	}
1687	printw("%-*.*s", width, width, temp);
1688    }
1689}
1690
1691static void
1692color_legend(WINDOW *helpwin)
1693{
1694    int row = 1;
1695    int col = 1;
1696
1697    mvwprintw(helpwin, row++, col,
1698	      "q or ESC to exit.");
1699    ++row;
1700    mvwprintw(helpwin, row++, col,
1701	      "Use up/down arrow to scroll through the display if it is");
1702    mvwprintw(helpwin, row++, col,
1703	      "longer than one screen. Control/N and Control/P can be used");
1704    mvwprintw(helpwin, row++, col,
1705	      "in place up up/down arrow.  Use pageup/pagedown to scroll a");
1706    mvwprintw(helpwin, row++, col,
1707	      "full screen; control/B and control/F can be used here.");
1708    ++row;
1709    mvwprintw(helpwin, row++, col,
1710	      "Toggles:");
1711    mvwprintw(helpwin, row++, col,
1712	      "  b/B     toggle bold off/on");
1713    mvwprintw(helpwin, row++, col,
1714	      "  n/N     toggle text/number on/off");
1715    mvwprintw(helpwin, row++, col,
1716	      "  w/W     toggle width between 8/16 colors");
1717}
1718
1719#define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1720
1721/* generate a color test pattern */
1722static void
1723color_test(void)
1724{
1725    int c;
1726    int i;
1727    int top = 0, width;
1728    int base_row = 0;
1729    int grid_top = top + 3;
1730    int page_size = (LINES - grid_top);
1731    int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1732    int row_limit;
1733    int per_row;
1734    char numbered[80];
1735    const char *hello;
1736    bool done = FALSE;
1737    bool opt_bold = FALSE;
1738    bool opt_wide = FALSE;
1739    bool opt_nums = FALSE;
1740    WINDOW *helpwin;
1741
1742    if (pairs_max > COLOR_PAIRS)
1743	pairs_max = COLOR_PAIRS;
1744
1745    while (!done) {
1746	int shown = 0;
1747
1748	/* this assumes an 80-column line */
1749	if (opt_wide) {
1750	    width = 4;
1751	    hello = "Test";
1752	    per_row = (COLORS > 8) ? 16 : 8;
1753	} else {
1754	    width = 8;
1755	    hello = "Hello";
1756	    per_row = 8;
1757	}
1758
1759	row_limit = (pairs_max + per_row - 1) / per_row;
1760
1761	move(0, 0);
1762	(void) printw("There are %d color pairs and %d colors\n",
1763		      pairs_max, COLORS);
1764
1765	clrtobot();
1766	(void) mvprintw(top + 1, 0,
1767			"%dx%d matrix of foreground/background colors, bold *%s*\n",
1768			row_limit,
1769			per_row,
1770			opt_bold ? "on" : "off");
1771
1772	/* show color names/numbers across the top */
1773	for (i = 0; i < per_row; i++)
1774	    show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1775
1776	/* show a grid of colors, with color names/ numbers on the left */
1777	for (i = (base_row * per_row); i < pairs_max; i++) {
1778	    int row = grid_top + (i / per_row) - base_row;
1779	    int col = (i % per_row + 1) * width;
1780	    int pair = i;
1781
1782	    if (row >= 0 && move(row, col) != ERR) {
1783		init_pair(pair, i % COLORS, i / COLORS);
1784		attron((attr_t) COLOR_PAIR(pair));
1785		if (opt_bold)
1786		    attron((attr_t) A_BOLD);
1787
1788		if (opt_nums) {
1789		    sprintf(numbered, "{%02X}", i);
1790		    hello = numbered;
1791		}
1792		printw("%-*.*s", width, width, hello);
1793		attrset(A_NORMAL);
1794
1795		if ((i % per_row) == 0 && (i % COLORS) == 0) {
1796		    show_color_name(row, 0, i / COLORS, opt_wide);
1797		}
1798		++shown;
1799	    } else if (shown) {
1800		break;
1801	    }
1802	}
1803
1804	switch (c = wGetchar(stdscr)) {
1805	case 'b':
1806	    opt_bold = FALSE;
1807	    break;
1808	case 'B':
1809	    opt_bold = TRUE;
1810	    break;
1811	case 'n':
1812	    opt_nums = FALSE;
1813	    break;
1814	case 'N':
1815	    opt_nums = TRUE;
1816	    break;
1817	case ESCAPE:
1818	case 'q':
1819	    done = TRUE;
1820	    continue;
1821	case 'w':
1822	    set_color_test(opt_wide, FALSE);
1823	    break;
1824	case 'W':
1825	    set_color_test(opt_wide, TRUE);
1826	    break;
1827	case CTRL('p'):
1828	case KEY_UP:
1829	    if (base_row <= 0) {
1830		beep();
1831	    } else {
1832		base_row -= 1;
1833	    }
1834	    break;
1835	case CTRL('n'):
1836	case KEY_DOWN:
1837	    if (base_row + page_size >= row_limit) {
1838		beep();
1839	    } else {
1840		base_row += 1;
1841	    }
1842	    break;
1843	case CTRL('b'):
1844	case KEY_PREVIOUS:
1845	case KEY_PPAGE:
1846	    if (base_row <= 0) {
1847		beep();
1848	    } else {
1849		base_row -= (page_size - 1);
1850		if (base_row < 0)
1851		    base_row = 0;
1852	    }
1853	    break;
1854	case CTRL('f'):
1855	case KEY_NEXT:
1856	case KEY_NPAGE:
1857	    if (base_row + page_size >= row_limit) {
1858		beep();
1859	    } else {
1860		base_row += page_size - 1;
1861		if (base_row + page_size >= row_limit) {
1862		    base_row = row_limit - page_size - 1;
1863		}
1864	    }
1865	    break;
1866	case '?':
1867	    if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1868		box(helpwin, 0, 0);
1869		color_legend(helpwin);
1870		wGetchar(helpwin);
1871		delwin(helpwin);
1872	    }
1873	    break;
1874	default:
1875	    beep();
1876	    continue;
1877	}
1878    }
1879
1880    erase();
1881    endwin();
1882}
1883
1884#if USE_WIDEC_SUPPORT
1885/* generate a color test pattern */
1886static void
1887wide_color_test(void)
1888{
1889    int c;
1890    int i;
1891    int top = 0, width;
1892    int base_row = 0;
1893    int grid_top = top + 3;
1894    int page_size = (LINES - grid_top);
1895    int pairs_max = COLOR_PAIRS;
1896    int row_limit;
1897    int per_row;
1898    char numbered[80];
1899    const char *hello;
1900    bool done = FALSE;
1901    bool opt_bold = FALSE;
1902    bool opt_wide = FALSE;
1903    bool opt_nums = FALSE;
1904    WINDOW *helpwin;
1905
1906    while (!done) {
1907	int shown = 0;
1908
1909	/* this assumes an 80-column line */
1910	if (opt_wide) {
1911	    width = 4;
1912	    hello = "Test";
1913	    per_row = (COLORS > 8) ? 16 : 8;
1914	} else {
1915	    width = 8;
1916	    hello = "Hello";
1917	    per_row = 8;
1918	}
1919
1920	row_limit = (pairs_max + per_row - 1) / per_row;
1921
1922	move(0, 0);
1923	(void) printw("There are %d color pairs and %d colors\n",
1924		      pairs_max, COLORS);
1925
1926	clrtobot();
1927	(void) mvprintw(top + 1, 0,
1928			"%dx%d matrix of foreground/background colors, bold *%s*\n",
1929			row_limit,
1930			per_row,
1931			opt_bold ? "on" : "off");
1932
1933	/* show color names/numbers across the top */
1934	for (i = 0; i < per_row; i++)
1935	    show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1936
1937	/* show a grid of colors, with color names/ numbers on the left */
1938	for (i = (base_row * per_row); i < pairs_max; i++) {
1939	    int row = grid_top + (i / per_row) - base_row;
1940	    int col = (i % per_row + 1) * width;
1941	    int pair = i;
1942
1943	    if (row >= 0 && move(row, col) != ERR) {
1944		init_pair(pair, i % COLORS, i / COLORS);
1945		color_set(pair, NULL);
1946		if (opt_bold)
1947		    attr_on((attr_t) A_BOLD, NULL);
1948
1949		if (opt_nums) {
1950		    sprintf(numbered, "{%02X}", i);
1951		    hello = numbered;
1952		}
1953		printw("%-*.*s", width, width, hello);
1954		attr_set(A_NORMAL, 0, NULL);
1955
1956		if ((i % per_row) == 0 && (i % COLORS) == 0) {
1957		    show_color_name(row, 0, i / COLORS, opt_wide);
1958		}
1959		++shown;
1960	    } else if (shown) {
1961		break;
1962	    }
1963	}
1964
1965	switch (c = wGetchar(stdscr)) {
1966	case 'b':
1967	    opt_bold = FALSE;
1968	    break;
1969	case 'B':
1970	    opt_bold = TRUE;
1971	    break;
1972	case 'n':
1973	    opt_nums = FALSE;
1974	    break;
1975	case 'N':
1976	    opt_nums = TRUE;
1977	    break;
1978	case ESCAPE:
1979	case 'q':
1980	    done = TRUE;
1981	    continue;
1982	case 'w':
1983	    set_color_test(opt_wide, FALSE);
1984	    break;
1985	case 'W':
1986	    set_color_test(opt_wide, TRUE);
1987	    break;
1988	case CTRL('p'):
1989	case KEY_UP:
1990	    if (base_row <= 0) {
1991		beep();
1992	    } else {
1993		base_row -= 1;
1994	    }
1995	    break;
1996	case CTRL('n'):
1997	case KEY_DOWN:
1998	    if (base_row + page_size >= row_limit) {
1999		beep();
2000	    } else {
2001		base_row += 1;
2002	    }
2003	    break;
2004	case CTRL('b'):
2005	case KEY_PREVIOUS:
2006	case KEY_PPAGE:
2007	    if (base_row <= 0) {
2008		beep();
2009	    } else {
2010		base_row -= (page_size - 1);
2011		if (base_row < 0)
2012		    base_row = 0;
2013	    }
2014	    break;
2015	case CTRL('f'):
2016	case KEY_NEXT:
2017	case KEY_NPAGE:
2018	    if (base_row + page_size >= row_limit) {
2019		beep();
2020	    } else {
2021		base_row += page_size - 1;
2022		if (base_row + page_size >= row_limit) {
2023		    base_row = row_limit - page_size - 1;
2024		}
2025	    }
2026	    break;
2027	case '?':
2028	    if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2029		box(helpwin, 0, 0);
2030		color_legend(helpwin);
2031		wGetchar(helpwin);
2032		delwin(helpwin);
2033	    }
2034	    break;
2035	default:
2036	    beep();
2037	    continue;
2038	}
2039    }
2040
2041    erase();
2042    endwin();
2043}
2044#endif /* USE_WIDEC_SUPPORT */
2045
2046static void
2047change_color(int current, int field, int value, int usebase)
2048{
2049    short red, green, blue;
2050
2051    if (usebase)
2052	color_content(current, &red, &green, &blue);
2053    else
2054	red = green = blue = 0;
2055
2056    switch (field) {
2057    case 0:
2058	red += value;
2059	break;
2060    case 1:
2061	green += value;
2062	break;
2063    case 2:
2064	blue += value;
2065	break;
2066    }
2067
2068    if (init_color(current, red, green, blue) == ERR)
2069	beep();
2070}
2071
2072static void
2073init_all_colors(void)
2074{
2075    int c;
2076    for (c = 0; c < COLORS; ++c)
2077	init_color(c,
2078		   all_colors[c].red,
2079		   all_colors[c].green,
2080		   all_colors[c].blue);
2081}
2082
2083#define scaled_rgb(n) ((255 * (n)) / 1000)
2084
2085static void
2086color_edit(void)
2087/* display the color test pattern, without trying to edit colors */
2088{
2089    int i, this_c = 0, value = 0, current = 0, field = 0;
2090    int last_c;
2091    int top_color = 0;
2092    int page_size = (LINES - 6);
2093
2094    init_all_colors();
2095    refresh();
2096
2097    for (i = 0; i < max_colors; i++)
2098	init_pair(i, COLOR_WHITE, i);
2099
2100    mvprintw(LINES - 2, 0, "Number: %d", value);
2101
2102    do {
2103	short red, green, blue;
2104
2105	attron(A_BOLD);
2106	mvaddstr(0, 20, "Color RGB Value Editing");
2107	attroff(A_BOLD);
2108
2109	for (i = top_color;
2110	     (i - top_color < page_size)
2111	     && (i < max_colors); i++) {
2112	    char numeric[80];
2113	    sprintf(numeric, "[%d]", i);
2114	    mvprintw(2 + i - top_color, 0, "%c %-8s:",
2115		     (i == current ? '>' : ' '),
2116		     (i < (int) SIZEOF(the_color_names)
2117		      ? the_color_names[i] : numeric));
2118	    attrset(COLOR_PAIR(i));
2119	    addstr("        ");
2120	    attrset(A_NORMAL);
2121
2122	    color_content(i, &red, &green, &blue);
2123	    addstr("   R = ");
2124	    if (current == i && field == 0)
2125		attron(A_STANDOUT);
2126	    printw("%04d", red);
2127	    if (current == i && field == 0)
2128		attrset(A_NORMAL);
2129	    addstr(", G = ");
2130	    if (current == i && field == 1)
2131		attron(A_STANDOUT);
2132	    printw("%04d", green);
2133	    if (current == i && field == 1)
2134		attrset(A_NORMAL);
2135	    addstr(", B = ");
2136	    if (current == i && field == 2)
2137		attron(A_STANDOUT);
2138	    printw("%04d", blue);
2139	    if (current == i && field == 2)
2140		attrset(A_NORMAL);
2141	    attrset(A_NORMAL);
2142	    printw(" ( %3d %3d %3d )",
2143		   scaled_rgb(red),
2144		   scaled_rgb(green),
2145		   scaled_rgb(blue));
2146	}
2147
2148	mvaddstr(LINES - 3, 0,
2149		 "Use up/down to select a color, left/right to change fields.");
2150	mvaddstr(LINES - 2, 0,
2151		 "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2152
2153	move(2 + current - top_color, 0);
2154
2155	last_c = this_c;
2156	this_c = Getchar();
2157	if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2158	    value = 0;
2159
2160	switch (this_c) {
2161	case CTRL('b'):
2162	case KEY_PPAGE:
2163	    if (current > 0)
2164		current -= (page_size - 1);
2165	    else
2166		beep();
2167	    break;
2168
2169	case CTRL('f'):
2170	case KEY_NPAGE:
2171	    if (current < (max_colors - 1))
2172		current += (page_size - 1);
2173	    else
2174		beep();
2175	    break;
2176
2177	case CTRL('p'):
2178	case KEY_UP:
2179	    current = (current == 0 ? (max_colors - 1) : current - 1);
2180	    break;
2181
2182	case CTRL('n'):
2183	case KEY_DOWN:
2184	    current = (current == (max_colors - 1) ? 0 : current + 1);
2185	    break;
2186
2187	case KEY_RIGHT:
2188	    field = (field == 2 ? 0 : field + 1);
2189	    break;
2190
2191	case KEY_LEFT:
2192	    field = (field == 0 ? 2 : field - 1);
2193	    break;
2194
2195	case '0':
2196	case '1':
2197	case '2':
2198	case '3':
2199	case '4':
2200	case '5':
2201	case '6':
2202	case '7':
2203	case '8':
2204	case '9':
2205	    value = value * 10 + (this_c - '0');
2206	    break;
2207
2208	case '+':
2209	    change_color(current, field, value, 1);
2210	    break;
2211
2212	case '-':
2213	    change_color(current, field, -value, 1);
2214	    break;
2215
2216	case '=':
2217	    change_color(current, field, value, 0);
2218	    break;
2219
2220	case '?':
2221	    erase();
2222	    P("                      RGB Value Editing Help");
2223	    P("");
2224	    P("You are in the RGB value editor.  Use the arrow keys to select one of");
2225	    P("the fields in one of the RGB triples of the current colors; the one");
2226	    P("currently selected will be reverse-video highlighted.");
2227	    P("");
2228	    P("To change a field, enter the digits of the new value; they are echoed");
2229	    P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2230	    P("To increment or decrement a value, use the same procedure, but finish");
2231	    P("with a `+' or `-'.");
2232	    P("");
2233	    P("Press 'm' to invoke the top-level menu with the current color settings.");
2234	    P("To quit, do `x' or 'q'");
2235
2236	    Pause();
2237	    erase();
2238	    break;
2239
2240	case 'm':
2241	    endwin();
2242	    main_menu(FALSE);
2243	    refresh();
2244	    break;
2245
2246	case 'x':
2247	case 'q':
2248	    break;
2249
2250	default:
2251	    beep();
2252	    break;
2253	}
2254
2255	if (current < 0)
2256	    current = 0;
2257	if (current >= max_colors)
2258	    current = max_colors - 1;
2259	if (current < top_color)
2260	    top_color = current;
2261	if (current - top_color >= page_size)
2262	    top_color = current - (page_size - 1);
2263
2264	mvprintw(LINES - 1, 0, "Number: %d", value);
2265	clrtoeol();
2266    } while
2267	(this_c != 'x' && this_c != 'q');
2268
2269    erase();
2270
2271    /*
2272     * ncurses does not reset each color individually when calling endwin().
2273     */
2274    init_all_colors();
2275
2276    endwin();
2277}
2278
2279/****************************************************************************
2280 *
2281 * Soft-key label test
2282 *
2283 ****************************************************************************/
2284
2285#define SLK_HELP 17
2286#define SLK_WORK (SLK_HELP + 3)
2287
2288static void
2289slk_help(void)
2290{
2291    static const char *table[] =
2292    {
2293	"Available commands are:"
2294	,""
2295	,"^L         -- repaint this message and activate soft keys"
2296	,"a/d        -- activate/disable soft keys"
2297	,"c          -- set centered format for labels"
2298	,"l          -- set left-justified format for labels"
2299	,"r          -- set right-justified format for labels"
2300	,"[12345678] -- set label; labels are numbered 1 through 8"
2301	,"e          -- erase stdscr (should not erase labels)"
2302	,"s          -- test scrolling of shortened screen"
2303#if HAVE_SLK_COLOR
2304	,"F/B        -- cycle through foreground/background colors"
2305#endif
2306	,"x, q       -- return to main menu"
2307	,""
2308	,"Note: if activating the soft keys causes your terminal to scroll up"
2309	,"one line, your terminal auto-scrolls when anything is written to the"
2310	,"last screen position.  The ncurses code does not yet handle this"
2311	,"gracefully."
2312    };
2313    unsigned j;
2314
2315    move(2, 0);
2316    for (j = 0; j < SIZEOF(table); ++j) {
2317	P(table[j]);
2318    }
2319    refresh();
2320}
2321
2322static void
2323slk_test(void)
2324/* exercise the soft keys */
2325{
2326    int c, fmt = 1;
2327    char buf[9];
2328    char *s;
2329#if HAVE_SLK_COLOR
2330    short fg = COLOR_BLACK;
2331    short bg = COLOR_WHITE;
2332    bool new_color = FALSE;
2333#endif
2334
2335    c = CTRL('l');
2336#if HAVE_SLK_COLOR
2337    if (has_colors()) {
2338	new_color = TRUE;
2339    }
2340#endif
2341
2342    do {
2343#if HAVE_SLK_COLOR
2344	if (new_color) {
2345	    init_pair(1, bg, fg);
2346	    slk_color(1);
2347	    new_color = FALSE;
2348	    mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2349	    refresh();
2350	}
2351#endif
2352	move(0, 0);
2353	switch (c) {
2354	case CTRL('l'):
2355	    erase();
2356	    attron(A_BOLD);
2357	    mvaddstr(0, 20, "Soft Key Exerciser");
2358	    attroff(A_BOLD);
2359
2360	    slk_help();
2361	    /* fall through */
2362
2363	case 'a':
2364	    slk_restore();
2365	    break;
2366
2367	case 'e':
2368	    wclear(stdscr);
2369	    break;
2370
2371	case 's':
2372	    mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2373	    while ((c = Getchar()) != 'Q' && (c != ERR))
2374		addch((chtype) c);
2375	    break;
2376
2377	case 'd':
2378	    slk_clear();
2379	    break;
2380
2381	case 'l':
2382	    fmt = 0;
2383	    break;
2384
2385	case 'c':
2386	    fmt = 1;
2387	    break;
2388
2389	case 'r':
2390	    fmt = 2;
2391	    break;
2392
2393	case '1':
2394	case '2':
2395	case '3':
2396	case '4':
2397	case '5':
2398	case '6':
2399	case '7':
2400	case '8':
2401	    (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2402	    strcpy(buf, "");
2403	    if ((s = slk_label(c - '0')) != 0) {
2404		strncpy(buf, s, 8);
2405	    }
2406	    wGetstring(stdscr, buf, 8);
2407	    slk_set((c - '0'), buf, fmt);
2408	    slk_refresh();
2409	    move(SLK_WORK, 0);
2410	    clrtobot();
2411	    break;
2412
2413	case 'x':
2414	case 'q':
2415	    goto done;
2416
2417#if HAVE_SLK_COLOR
2418	case 'F':
2419	    if (has_colors()) {
2420		fg = (fg + 1) % COLORS;
2421		new_color = TRUE;
2422	    }
2423	    break;
2424	case 'B':
2425	    if (has_colors()) {
2426		bg = (bg + 1) % COLORS;
2427		new_color = TRUE;
2428	    }
2429	    break;
2430#endif
2431
2432	default:
2433	    beep();
2434	}
2435    } while
2436	((c = Getchar()) != EOF);
2437
2438  done:
2439    erase();
2440    endwin();
2441}
2442
2443#if USE_WIDEC_SUPPORT
2444#define SLKLEN 8
2445static void
2446wide_slk_test(void)
2447/* exercise the soft keys */
2448{
2449    int c, fmt = 1;
2450    wchar_t buf[SLKLEN + 1];
2451    char *s;
2452    short fg = COLOR_BLACK;
2453    short bg = COLOR_WHITE;
2454    bool new_color = FALSE;
2455
2456    c = CTRL('l');
2457    if (has_colors()) {
2458	new_color = TRUE;
2459    }
2460    do {
2461	if (new_color) {
2462	    init_pair(1, bg, fg);
2463	    slk_color(1);
2464	    new_color = FALSE;
2465	    mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2466	    refresh();
2467	}
2468	move(0, 0);
2469	switch (c) {
2470	case CTRL('l'):
2471	    erase();
2472	    attr_on(WA_BOLD, NULL);
2473	    mvaddstr(0, 20, "Soft Key Exerciser");
2474	    attr_off(WA_BOLD, NULL);
2475
2476	    slk_help();
2477	    /* fall through */
2478
2479	case 'a':
2480	    slk_restore();
2481	    break;
2482
2483	case 'e':
2484	    wclear(stdscr);
2485	    break;
2486
2487	case 's':
2488	    mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2489	    while ((c = Getchar()) != 'Q' && (c != ERR))
2490		addch((chtype) c);
2491	    break;
2492
2493	case 'd':
2494	    slk_clear();
2495	    break;
2496
2497	case 'l':
2498	    fmt = 0;
2499	    break;
2500
2501	case 'c':
2502	    fmt = 1;
2503	    break;
2504
2505	case 'r':
2506	    fmt = 2;
2507	    break;
2508
2509	case '1':
2510	case '2':
2511	case '3':
2512	case '4':
2513	case '5':
2514	case '6':
2515	case '7':
2516	case '8':
2517	    (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2518	    *buf = 0;
2519	    if ((s = slk_label(c - '0')) != 0) {
2520		char *temp = strdup(s);
2521		size_t used = strlen(temp);
2522		size_t want = SLKLEN;
2523		size_t test;
2524		mbstate_t state;
2525
2526		buf[0] = L'\0';
2527		while (want > 0 && used != 0) {
2528		    const char *base = s;
2529		    memset(&state, 0, sizeof(state));
2530		    test = mbsrtowcs(0, &base, 0, &state);
2531		    if (test == (size_t) -1) {
2532			temp[--used] = 0;
2533		    } else if (test > want) {
2534			temp[--used] = 0;
2535		    } else {
2536			memset(&state, 0, sizeof(state));
2537			mbsrtowcs(buf, &base, want, &state);
2538			break;
2539		    }
2540		}
2541		free(temp);
2542	    }
2543	    wGet_wstring(stdscr, buf, SLKLEN);
2544	    slk_wset((c - '0'), buf, fmt);
2545	    slk_refresh();
2546	    move(SLK_WORK, 0);
2547	    clrtobot();
2548	    break;
2549
2550	case 'x':
2551	case 'q':
2552	    goto done;
2553
2554	case 'F':
2555	    if (has_colors()) {
2556		fg = (fg + 1) % COLORS;
2557		new_color = TRUE;
2558	    }
2559	    break;
2560	case 'B':
2561	    if (has_colors()) {
2562		bg = (bg + 1) % COLORS;
2563		new_color = TRUE;
2564	    }
2565	    break;
2566
2567	default:
2568	    beep();
2569	}
2570    } while
2571	((c = Getchar()) != EOF);
2572
2573  done:
2574    erase();
2575    endwin();
2576}
2577#endif
2578
2579/****************************************************************************
2580 *
2581 * Alternate character-set stuff
2582 *
2583 ****************************************************************************/
2584
2585/* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2586 * terminal to perform functions.  The remaining codes can be graphic.
2587 */
2588static void
2589show_upper_chars(unsigned first)
2590{
2591    bool C1 = (first == 128);
2592    unsigned code;
2593    unsigned last = first + 31;
2594    int reply;
2595
2596    erase();
2597    attron(A_BOLD);
2598    mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2599	     C1 ? "C1" : "GR", first, last);
2600    attroff(A_BOLD);
2601    refresh();
2602
2603    for (code = first; code <= last; code++) {
2604	int row = 4 + ((code - first) % 16);
2605	int col = ((code - first) / 16) * COLS / 2;
2606	char tmp[80];
2607	sprintf(tmp, "%3u (0x%x)", code, code);
2608	mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2609	if (C1)
2610	    nodelay(stdscr, TRUE);
2611	echochar(code);
2612	if (C1) {
2613	    /* (yes, this _is_ crude) */
2614	    while ((reply = Getchar()) != ERR) {
2615		addch(UChar(reply));
2616		napms(10);
2617	    }
2618	    nodelay(stdscr, FALSE);
2619	}
2620    }
2621}
2622
2623static void
2624show_box_chars(void)
2625{
2626    erase();
2627    attron(A_BOLD);
2628    mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2629    attroff(A_BOLD);
2630    refresh();
2631    box(stdscr, 0, 0);
2632    /* *INDENT-OFF* */
2633    mvhline(LINES / 2, 0,        ACS_HLINE, COLS);
2634    mvvline(0,         COLS / 2, ACS_VLINE, LINES);
2635    mvaddch(0,         COLS / 2, ACS_TTEE);
2636    mvaddch(LINES / 2, COLS / 2, ACS_PLUS);
2637    mvaddch(LINES - 1, COLS / 2, ACS_BTEE);
2638    mvaddch(LINES / 2, 0,        ACS_LTEE);
2639    mvaddch(LINES / 2, COLS - 1, ACS_RTEE);
2640    /* *INDENT-ON* */
2641
2642}
2643
2644static int
2645show_1_acs(int n, const char *name, chtype code)
2646{
2647    const int height = 16;
2648    int row = 4 + (n % height);
2649    int col = (n / height) * COLS / 2;
2650    mvprintw(row, col, "%*s : ", COLS / 4, name);
2651    addch(code);
2652    return n + 1;
2653}
2654
2655static void
2656show_acs_chars(void)
2657/* display the ACS character set */
2658{
2659    int n;
2660
2661#define BOTH(name) #name, name
2662
2663    erase();
2664    attron(A_BOLD);
2665    mvaddstr(0, 20, "Display of the ACS Character Set");
2666    attroff(A_BOLD);
2667    refresh();
2668
2669    n = show_1_acs(0, BOTH(ACS_ULCORNER));
2670    n = show_1_acs(n, BOTH(ACS_URCORNER));
2671    n = show_1_acs(n, BOTH(ACS_LLCORNER));
2672    n = show_1_acs(n, BOTH(ACS_LRCORNER));
2673
2674    n = show_1_acs(n, BOTH(ACS_LTEE));
2675    n = show_1_acs(n, BOTH(ACS_RTEE));
2676    n = show_1_acs(n, BOTH(ACS_TTEE));
2677    n = show_1_acs(n, BOTH(ACS_BTEE));
2678
2679    n = show_1_acs(n, BOTH(ACS_HLINE));
2680    n = show_1_acs(n, BOTH(ACS_VLINE));
2681
2682    n = show_1_acs(n, BOTH(ACS_LARROW));
2683    n = show_1_acs(n, BOTH(ACS_RARROW));
2684    n = show_1_acs(n, BOTH(ACS_UARROW));
2685    n = show_1_acs(n, BOTH(ACS_DARROW));
2686
2687    n = show_1_acs(n, BOTH(ACS_BLOCK));
2688    n = show_1_acs(n, BOTH(ACS_BOARD));
2689    n = show_1_acs(n, BOTH(ACS_LANTERN));
2690    n = show_1_acs(n, BOTH(ACS_BULLET));
2691    n = show_1_acs(n, BOTH(ACS_CKBOARD));
2692    n = show_1_acs(n, BOTH(ACS_DEGREE));
2693    n = show_1_acs(n, BOTH(ACS_DIAMOND));
2694    n = show_1_acs(n, BOTH(ACS_PLMINUS));
2695    n = show_1_acs(n, BOTH(ACS_PLUS));
2696
2697    n = show_1_acs(n, BOTH(ACS_GEQUAL));
2698    n = show_1_acs(n, BOTH(ACS_NEQUAL));
2699    n = show_1_acs(n, BOTH(ACS_LEQUAL));
2700
2701    n = show_1_acs(n, BOTH(ACS_STERLING));
2702    n = show_1_acs(n, BOTH(ACS_PI));
2703    n = show_1_acs(n, BOTH(ACS_S1));
2704    n = show_1_acs(n, BOTH(ACS_S3));
2705    n = show_1_acs(n, BOTH(ACS_S7));
2706    n = show_1_acs(n, BOTH(ACS_S9));
2707}
2708
2709static void
2710acs_display(void)
2711{
2712    int c = 'a';
2713
2714    do {
2715	switch (c) {
2716	case CTRL('L'):
2717	    Repaint();
2718	    break;
2719	case 'a':
2720	    show_acs_chars();
2721	    break;
2722	case 'b':
2723	    show_box_chars();
2724	    break;
2725	case '0':
2726	case '1':
2727	case '2':
2728	case '3':
2729	    show_upper_chars((unsigned) ((c - '0') * 32 + 128));
2730	    break;
2731	}
2732	mvprintw(LINES - 3, 0,
2733		 "Note: ANSI terminals may not display C1 characters.");
2734	mvprintw(LINES - 2, 0,
2735		 "Select: a=ACS, b=box, 0=C1, 1,2,3=GR characters, q=quit");
2736	refresh();
2737    } while ((c = Getchar()) != 'x' && c != 'q');
2738
2739    Pause();
2740    erase();
2741    endwin();
2742}
2743
2744#if USE_WIDEC_SUPPORT
2745static void
2746show_upper_widechars(int first, int repeat, int space)
2747{
2748    cchar_t temp;
2749    wchar_t code;
2750    int last = first + 31;
2751
2752    erase();
2753    attron(A_BOLD);
2754    mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
2755    attroff(A_BOLD);
2756
2757    for (code = first; code <= last; code++) {
2758	int row = 4 + ((code - first) % 16);
2759	int col = ((code - first) / 16) * COLS / 2;
2760	wchar_t codes[10];
2761	attr_t attrs = A_NORMAL;
2762	char tmp[80];
2763	int count = repeat;
2764
2765	memset(&codes, 0, sizeof(codes));
2766	codes[0] = code;
2767	sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
2768	mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2769	setcchar(&temp, codes, attrs, 0, 0);
2770	do {
2771	    /*
2772	     * Give non-spacing characters something to combine with.  If we
2773	     * don't, they'll bunch up in a heap on the space after the ":".
2774	     * Mark them with reverse-video to make them simpler to find on
2775	     * the display.
2776	     */
2777	    if (wcwidth(code) == 0)
2778		addch(space | A_REVERSE);
2779	    /*
2780	     * This could use add_wch(), but is done for comparison with the
2781	     * normal 'f' test (and to make a test-case for echo_wchar()).
2782	     * The screen will flicker because the erase() at the top of the
2783	     * function is met by the builtin refresh() in echo_wchar().
2784	     */
2785	    echo_wchar(&temp);
2786	} while (--count > 0);
2787    }
2788}
2789
2790static int
2791show_1_wacs(int n, const char *name, const cchar_t *code)
2792{
2793    const int height = 16;
2794    int row = 4 + (n % height);
2795    int col = (n / height) * COLS / 2;
2796    mvprintw(row, col, "%*s : ", COLS / 4, name);
2797    add_wchnstr(code, 1);
2798    return n + 1;
2799}
2800
2801static void
2802show_wacs_chars(void)
2803/* display the wide-ACS character set */
2804{
2805    int n;
2806
2807/*#define BOTH2(name) #name, &(name) */
2808#define BOTH2(name) #name, name
2809
2810    erase();
2811    attron(A_BOLD);
2812    mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2813    attroff(A_BOLD);
2814    refresh();
2815
2816    n = show_1_wacs(0, BOTH2(WACS_ULCORNER));
2817    n = show_1_wacs(n, BOTH2(WACS_URCORNER));
2818    n = show_1_wacs(n, BOTH2(WACS_LLCORNER));
2819    n = show_1_wacs(n, BOTH2(WACS_LRCORNER));
2820
2821    n = show_1_wacs(n, BOTH2(WACS_LTEE));
2822    n = show_1_wacs(n, BOTH2(WACS_RTEE));
2823    n = show_1_wacs(n, BOTH2(WACS_TTEE));
2824    n = show_1_wacs(n, BOTH2(WACS_BTEE));
2825
2826    n = show_1_wacs(n, BOTH2(WACS_HLINE));
2827    n = show_1_wacs(n, BOTH2(WACS_VLINE));
2828
2829    n = show_1_wacs(n, BOTH2(WACS_LARROW));
2830    n = show_1_wacs(n, BOTH2(WACS_RARROW));
2831    n = show_1_wacs(n, BOTH2(WACS_UARROW));
2832    n = show_1_wacs(n, BOTH2(WACS_DARROW));
2833
2834    n = show_1_wacs(n, BOTH2(WACS_BLOCK));
2835    n = show_1_wacs(n, BOTH2(WACS_BOARD));
2836    n = show_1_wacs(n, BOTH2(WACS_LANTERN));
2837    n = show_1_wacs(n, BOTH2(WACS_BULLET));
2838    n = show_1_wacs(n, BOTH2(WACS_CKBOARD));
2839    n = show_1_wacs(n, BOTH2(WACS_DEGREE));
2840    n = show_1_wacs(n, BOTH2(WACS_DIAMOND));
2841    n = show_1_wacs(n, BOTH2(WACS_PLMINUS));
2842    n = show_1_wacs(n, BOTH2(WACS_PLUS));
2843
2844#ifdef CURSES_WACS_ARRAY
2845    n = show_1_wacs(n, BOTH2(WACS_GEQUAL));
2846    n = show_1_wacs(n, BOTH2(WACS_NEQUAL));
2847    n = show_1_wacs(n, BOTH2(WACS_LEQUAL));
2848
2849    n = show_1_wacs(n, BOTH2(WACS_STERLING));
2850    n = show_1_wacs(n, BOTH2(WACS_PI));
2851    n = show_1_wacs(n, BOTH2(WACS_S1));
2852    n = show_1_wacs(n, BOTH2(WACS_S3));
2853    n = show_1_wacs(n, BOTH2(WACS_S7));
2854    n = show_1_wacs(n, BOTH2(WACS_S9));
2855#endif
2856}
2857
2858static void
2859show_wbox_chars(void)
2860{
2861    erase();
2862    attron(A_BOLD);
2863    mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
2864    attroff(A_BOLD);
2865    refresh();
2866    box_set(stdscr, 0, 0);
2867    /* *INDENT-OFF* */
2868    mvhline_set(LINES / 2, 0,        WACS_HLINE, COLS);
2869    mvvline_set(0,         COLS / 2, WACS_VLINE, LINES);
2870    mvadd_wch(0,           COLS / 2, WACS_TTEE);
2871    mvadd_wch(LINES / 2,   COLS / 2, WACS_PLUS);
2872    mvadd_wch(LINES - 1,   COLS / 2, WACS_BTEE);
2873    mvadd_wch(LINES / 2,   0,        WACS_LTEE);
2874    mvadd_wch(LINES / 2,   COLS - 1, WACS_RTEE);
2875    /* *INDENT-ON* */
2876
2877}
2878
2879static int
2880show_2_wacs(int n, const char *name, const char *code)
2881{
2882    const int height = 16;
2883    int row = 4 + (n % height);
2884    int col = (n / height) * COLS / 2;
2885    char temp[80];
2886
2887    mvprintw(row, col, "%*s : ", COLS / 4, name);
2888    addstr(strcpy(temp, code));
2889    return n + 1;
2890}
2891
2892static void
2893show_utf8_chars(void)
2894/* display the wide-ACS character set */
2895{
2896    int n;
2897
2898    erase();
2899    attron(A_BOLD);
2900    mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
2901    attroff(A_BOLD);
2902    refresh();
2903    /* *INDENT-OFF* */
2904    n = show_2_wacs(0, "WACS_ULCORNER",	"\342\224\214");
2905    n = show_2_wacs(n, "WACS_URCORNER",	"\342\224\220");
2906    n = show_2_wacs(n, "WACS_LLCORNER",	"\342\224\224");
2907    n = show_2_wacs(n, "WACS_LRCORNER",	"\342\224\230");
2908
2909    n = show_2_wacs(n, "WACS_LTEE",	"\342\224\234");
2910    n = show_2_wacs(n, "WACS_RTEE",	"\342\224\244");
2911    n = show_2_wacs(n, "WACS_TTEE",	"\342\224\254");
2912    n = show_2_wacs(n, "WACS_BTEE",	"\342\224\264");
2913
2914    n = show_2_wacs(n, "WACS_HLINE",	"\342\224\200");
2915    n = show_2_wacs(n, "WACS_VLINE",	"\342\224\202");
2916
2917    n = show_2_wacs(n, "WACS_LARROW",	"\342\206\220");
2918    n = show_2_wacs(n, "WACS_RARROW",	"\342\206\222");
2919    n = show_2_wacs(n, "WACS_UARROW",	"\342\206\221");
2920    n = show_2_wacs(n, "WACS_DARROW",	"\342\206\223");
2921
2922    n = show_2_wacs(n, "WACS_BLOCK",	"\342\226\256");
2923    n = show_2_wacs(n, "WACS_BOARD",	"\342\226\222");
2924    n = show_2_wacs(n, "WACS_LANTERN",	"\342\230\203");
2925    n = show_2_wacs(n, "WACS_BULLET",	"\302\267");
2926    n = show_2_wacs(n, "WACS_CKBOARD",	"\342\226\222");
2927    n = show_2_wacs(n, "WACS_DEGREE",	"\302\260");
2928    n = show_2_wacs(n, "WACS_DIAMOND",	"\342\227\206");
2929    n = show_2_wacs(n, "WACS_PLMINUS",	"\302\261");
2930    n = show_2_wacs(n, "WACS_PLUS",	"\342\224\274");
2931    n = show_2_wacs(n, "WACS_GEQUAL",	"\342\211\245");
2932    n = show_2_wacs(n, "WACS_NEQUAL",	"\342\211\240");
2933    n = show_2_wacs(n, "WACS_LEQUAL",	"\342\211\244");
2934
2935    n = show_2_wacs(n, "WACS_STERLING",	"\302\243");
2936    n = show_2_wacs(n, "WACS_PI",	"\317\200");
2937    n = show_2_wacs(n, "WACS_S1",	"\342\216\272");
2938    n = show_2_wacs(n, "WACS_S3",	"\342\216\273");
2939    n = show_2_wacs(n, "WACS_S7",	"\342\216\274");
2940    n = show_2_wacs(n, "WACS_S9",	"\342\216\275");
2941    /* *INDENT-ON* */
2942
2943}
2944
2945static void
2946wide_acs_display(void)
2947{
2948    int c = 'a';
2949    int digit = 0;
2950    int repeat = 0;
2951    int space = ' ';
2952
2953    do {
2954	switch (c) {
2955	case CTRL('L'):
2956	    Repaint();
2957	    break;
2958	case 'a':
2959	    show_wacs_chars();
2960	    break;
2961	case 'b':
2962	    show_wbox_chars();
2963	    break;
2964	case 'u':
2965	    show_utf8_chars();
2966	    break;
2967	default:
2968	    if (c < 256 && isdigit(c))
2969		digit = (c - '0');
2970	    else if (c == '+')
2971		++digit;
2972	    else if (c == '-' && digit > 0)
2973		--digit;
2974	    else if (c == '>')
2975		++repeat;
2976	    else if (c == '<' && repeat > 0)
2977		--repeat;
2978	    else if (c == '_')
2979		space = (space == ' ') ? '_' : ' ';
2980	    else {
2981		beep();
2982		break;
2983	    }
2984	    show_upper_widechars(digit * 32 + 128, repeat, space);
2985	    break;
2986	}
2987	mvprintw(LINES - 2, 0,
2988		 "Select: a WACS, b box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, q=quit");
2989	refresh();
2990    } while ((c = Getchar()) != 'x' && c != 'q');
2991
2992    Pause();
2993    erase();
2994    endwin();
2995}
2996
2997#endif
2998
2999/*
3000 * Graphic-rendition test (adapted from vttest)
3001 */
3002static void
3003test_sgr_attributes(void)
3004{
3005    int pass;
3006
3007    for (pass = 0; pass < 2; pass++) {
3008	chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3009
3010	/* Use non-default colors if possible to exercise bce a little */
3011	if (has_colors()) {
3012	    init_pair(1, COLOR_WHITE, COLOR_BLUE);
3013	    normal |= COLOR_PAIR(1);
3014	}
3015	bkgdset(normal);
3016	erase();
3017	mvprintw(1, 20, "Graphic rendition test pattern:");
3018
3019	mvprintw(4, 1, "vanilla");
3020
3021#define set_sgr(mask) bkgdset((normal^(mask)));
3022	set_sgr(A_BOLD);
3023	mvprintw(4, 40, "bold");
3024
3025	set_sgr(A_UNDERLINE);
3026	mvprintw(6, 6, "underline");
3027
3028	set_sgr(A_BOLD | A_UNDERLINE);
3029	mvprintw(6, 45, "bold underline");
3030
3031	set_sgr(A_BLINK);
3032	mvprintw(8, 1, "blink");
3033
3034	set_sgr(A_BLINK | A_BOLD);
3035	mvprintw(8, 40, "bold blink");
3036
3037	set_sgr(A_UNDERLINE | A_BLINK);
3038	mvprintw(10, 6, "underline blink");
3039
3040	set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3041	mvprintw(10, 45, "bold underline blink");
3042
3043	set_sgr(A_REVERSE);
3044	mvprintw(12, 1, "negative");
3045
3046	set_sgr(A_BOLD | A_REVERSE);
3047	mvprintw(12, 40, "bold negative");
3048
3049	set_sgr(A_UNDERLINE | A_REVERSE);
3050	mvprintw(14, 6, "underline negative");
3051
3052	set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3053	mvprintw(14, 45, "bold underline negative");
3054
3055	set_sgr(A_BLINK | A_REVERSE);
3056	mvprintw(16, 1, "blink negative");
3057
3058	set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3059	mvprintw(16, 40, "bold blink negative");
3060
3061	set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3062	mvprintw(18, 6, "underline blink negative");
3063
3064	set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3065	mvprintw(18, 45, "bold underline blink negative");
3066
3067	bkgdset(normal);
3068	mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3069		 "Light");
3070	clrtoeol();
3071	Pause();
3072    }
3073
3074    bkgdset(A_NORMAL | BLANK);
3075    erase();
3076    endwin();
3077}
3078
3079/****************************************************************************
3080 *
3081 * Windows and scrolling tester.
3082 *
3083 ****************************************************************************/
3084
3085#define BOTLINES	4	/* number of line stolen from screen bottom */
3086
3087typedef struct {
3088    int y, x;
3089} pair;
3090
3091#define FRAME struct frame
3092FRAME
3093{
3094    FRAME *next, *last;
3095    bool do_scroll;
3096    bool do_keypad;
3097    WINDOW *wind;
3098};
3099
3100#ifdef NCURSES_VERSION
3101#define keypad_active(win) (win)->_use_keypad
3102#define scroll_active(win) (win)->_scroll
3103#else
3104#define keypad_active(win) FALSE
3105#define scroll_active(win) FALSE
3106#endif
3107
3108/* We need to know if these flags are actually set, so don't look in FRAME.
3109 * These names are known to work with SVr4 curses as well as ncurses.  The
3110 * _use_keypad name does not work with Solaris 8.
3111 */
3112static bool
3113HaveKeypad(FRAME * curp)
3114{
3115    WINDOW *win = (curp ? curp->wind : stdscr);
3116    return keypad_active(win);
3117}
3118
3119static bool
3120HaveScroll(FRAME * curp)
3121{
3122    WINDOW *win = (curp ? curp->wind : stdscr);
3123    return scroll_active(win);
3124}
3125
3126static void
3127newwin_legend(FRAME * curp)
3128{
3129    static const struct {
3130	const char *msg;
3131	int code;
3132    } legend[] = {
3133	{
3134	    "^C = create window", 0
3135	},
3136	{
3137	    "^N = next window", 0
3138	},
3139	{
3140	    "^P = previous window", 0
3141	},
3142	{
3143	    "^F = scroll forward", 0
3144	},
3145	{
3146	    "^B = scroll backward", 0
3147	},
3148	{
3149	    "^K = keypad(%s)", 1
3150	},
3151	{
3152	    "^S = scrollok(%s)", 2
3153	},
3154	{
3155	    "^W = save window to file", 0
3156	},
3157	{
3158	    "^R = restore window", 0
3159	},
3160#if HAVE_WRESIZE
3161	{
3162	    "^X = resize", 0
3163	},
3164#endif
3165	{
3166	    "^Q%s = exit", 3
3167	}
3168    };
3169    size_t n;
3170    int x;
3171    bool do_keypad = HaveKeypad(curp);
3172    bool do_scroll = HaveScroll(curp);
3173    char buf[BUFSIZ];
3174
3175    move(LINES - 4, 0);
3176    for (n = 0; n < SIZEOF(legend); n++) {
3177	switch (legend[n].code) {
3178	default:
3179	    strcpy(buf, legend[n].msg);
3180	    break;
3181	case 1:
3182	    sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3183	    break;
3184	case 2:
3185	    sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3186	    break;
3187	case 3:
3188	    sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3189	    break;
3190	}
3191	x = getcurx(stdscr);
3192	addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3193	addstr(buf);
3194    }
3195    clrtoeol();
3196}
3197
3198static void
3199transient(FRAME * curp, NCURSES_CONST char *msg)
3200{
3201    newwin_legend(curp);
3202    if (msg) {
3203	mvaddstr(LINES - 1, 0, msg);
3204	refresh();
3205	napms(1000);
3206    }
3207
3208    move(LINES - 1, 0);
3209    printw("%s characters are echoed, window should %sscroll.",
3210	   HaveKeypad(curp) ? "Non-arrow" : "All other",
3211	   HaveScroll(curp) ? "" : "not ");
3212    clrtoeol();
3213}
3214
3215static void
3216newwin_report(FRAME * curp)
3217/* report on the cursor's current position, then restore it */
3218{
3219    WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3220    int y, x;
3221
3222    if (win != stdscr)
3223	transient(curp, (char *) 0);
3224    getyx(win, y, x);
3225    move(LINES - 1, COLS - 17);
3226    printw("Y = %2d X = %2d", y, x);
3227    if (win != stdscr)
3228	refresh();
3229    else
3230	wmove(win, y, x);
3231}
3232
3233static pair *
3234selectcell(int uli, int ulj, int lri, int lrj)
3235/* arrows keys move cursor, return location at current on non-arrow key */
3236{
3237    static pair res;		/* result cell */
3238    int si = lri - uli + 1;	/* depth of the select area */
3239    int sj = lrj - ulj + 1;	/* width of the select area */
3240    int i = 0, j = 0;		/* offsets into the select area */
3241
3242    res.y = uli;
3243    res.x = ulj;
3244    for (;;) {
3245	move(uli + i, ulj + j);
3246	newwin_report((FRAME *) 0);
3247
3248	switch (Getchar()) {
3249	case KEY_UP:
3250	    i += si - 1;
3251	    break;
3252	case KEY_DOWN:
3253	    i++;
3254	    break;
3255	case KEY_LEFT:
3256	    j += sj - 1;
3257	    break;
3258	case KEY_RIGHT:
3259	    j++;
3260	    break;
3261	case QUIT:
3262	case ESCAPE:
3263	    return ((pair *) 0);
3264#ifdef NCURSES_MOUSE_VERSION
3265	case KEY_MOUSE:
3266	    {
3267		MEVENT event;
3268
3269		getmouse(&event);
3270		if (event.y > uli && event.x > ulj) {
3271		    i = event.y - uli;
3272		    j = event.x - ulj;
3273		} else {
3274		    beep();
3275		    break;
3276		}
3277	    }
3278	    /* FALLTHRU */
3279#endif
3280	default:
3281	    res.y = uli + i;
3282	    res.x = ulj + j;
3283	    return (&res);
3284	}
3285	i %= si;
3286	j %= sj;
3287    }
3288}
3289
3290static void
3291outerbox(pair ul, pair lr, bool onoff)
3292/* draw or erase a box *outside* the given pair of corners */
3293{
3294    mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3295    mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3296    mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3297    mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3298    move(ul.y - 1, ul.x);
3299    hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3300    move(ul.y, ul.x - 1);
3301    vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3302    move(lr.y + 1, ul.x);
3303    hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3304    move(ul.y, lr.x + 1);
3305    vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3306}
3307
3308static WINDOW *
3309getwindow(void)
3310/* Ask user for a window definition */
3311{
3312    WINDOW *rwindow;
3313    pair ul, lr, *tmp;
3314
3315    move(0, 0);
3316    clrtoeol();
3317    addstr("Use arrows to move cursor, anything else to mark corner 1");
3318    refresh();
3319    if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3320	return ((WINDOW *) 0);
3321    memcpy(&ul, tmp, sizeof(pair));
3322    mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3323    move(0, 0);
3324    clrtoeol();
3325    addstr("Use arrows to move cursor, anything else to mark corner 2");
3326    refresh();
3327    if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3328	(pair *) 0)
3329	return ((WINDOW *) 0);
3330    memcpy(&lr, tmp, sizeof(pair));
3331
3332    rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3333
3334    outerbox(ul, lr, TRUE);
3335    refresh();
3336
3337    wrefresh(rwindow);
3338
3339    move(0, 0);
3340    clrtoeol();
3341    return (rwindow);
3342}
3343
3344static void
3345newwin_move(FRAME * curp, int dy, int dx)
3346{
3347    WINDOW *win = (curp != 0) ? curp->wind : stdscr;
3348    int cur_y, cur_x;
3349    int max_y, max_x;
3350
3351    getyx(win, cur_y, cur_x);
3352    getmaxyx(win, max_y, max_x);
3353    if ((cur_x += dx) < 0)
3354	cur_x = 0;
3355    else if (cur_x >= max_x)
3356	cur_x = max_x - 1;
3357    if ((cur_y += dy) < 0)
3358	cur_y = 0;
3359    else if (cur_y >= max_y)
3360	cur_y = max_y - 1;
3361    wmove(win, cur_y, cur_x);
3362}
3363
3364static FRAME *
3365delete_framed(FRAME * fp, bool showit)
3366{
3367    FRAME *np;
3368
3369    fp->last->next = fp->next;
3370    fp->next->last = fp->last;
3371
3372    if (showit) {
3373	werase(fp->wind);
3374	wrefresh(fp->wind);
3375    }
3376    delwin(fp->wind);
3377
3378    np = (fp == fp->next) ? 0 : fp->next;
3379    free(fp);
3380    return np;
3381}
3382
3383static void
3384acs_and_scroll(void)
3385/* Demonstrate windows */
3386{
3387    int c, i;
3388    FILE *fp;
3389    FRAME *current = (FRAME *) 0, *neww;
3390    WINDOW *usescr = stdscr;
3391
3392#define DUMPFILE	"screendump"
3393
3394#ifdef NCURSES_MOUSE_VERSION
3395    mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3396#endif
3397    c = CTRL('C');
3398    raw();
3399    do {
3400	transient((FRAME *) 0, (char *) 0);
3401	switch (c) {
3402	case CTRL('C'):
3403	    neww = (FRAME *) calloc(1, sizeof(FRAME));
3404	    if ((neww->wind = getwindow()) == (WINDOW *) 0)
3405		goto breakout;
3406
3407	    if (current == 0) {	/* First element,  */
3408		neww->next = neww;	/*   so point it at itself */
3409		neww->last = neww;
3410	    } else {
3411		neww->next = current->next;
3412		neww->last = current;
3413		neww->last->next = neww;
3414		neww->next->last = neww;
3415	    }
3416	    current = neww;
3417	    /* SVr4 curses sets the keypad on all newly-created windows to
3418	     * false.  Someone reported that PDCurses makes new windows inherit
3419	     * this flag.  Remove the following 'keypad()' call to test this
3420	     */
3421	    keypad(current->wind, TRUE);
3422	    current->do_keypad = HaveKeypad(current);
3423	    current->do_scroll = HaveScroll(current);
3424	    break;
3425
3426	case CTRL('N'):	/* go to next window */
3427	    if (current)
3428		current = current->next;
3429	    break;
3430
3431	case CTRL('P'):	/* go to previous window */
3432	    if (current)
3433		current = current->last;
3434	    break;
3435
3436	case CTRL('F'):	/* scroll current window forward */
3437	    if (current)
3438		wscrl(current->wind, 1);
3439	    break;
3440
3441	case CTRL('B'):	/* scroll current window backwards */
3442	    if (current)
3443		wscrl(current->wind, -1);
3444	    break;
3445
3446	case CTRL('K'):	/* toggle keypad mode for current */
3447	    if (current) {
3448		current->do_keypad = !current->do_keypad;
3449		keypad(current->wind, current->do_keypad);
3450	    }
3451	    break;
3452
3453	case CTRL('S'):
3454	    if (current) {
3455		current->do_scroll = !current->do_scroll;
3456		scrollok(current->wind, current->do_scroll);
3457	    }
3458	    break;
3459
3460	case CTRL('W'):	/* save and delete window */
3461	    if (current == current->next) {
3462		transient(current, "Will not save/delete ONLY window");
3463		break;
3464	    } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3465		transient(current, "Can't open screen dump file");
3466	    } else {
3467		(void) putwin(current->wind, fp);
3468		(void) fclose(fp);
3469
3470		current = delete_framed(current, TRUE);
3471	    }
3472	    break;
3473
3474	case CTRL('R'):	/* restore window */
3475	    if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3476		transient(current, "Can't open screen dump file");
3477	    } else {
3478		neww = (FRAME *) calloc(1, sizeof(FRAME));
3479
3480		neww->next = current->next;
3481		neww->last = current;
3482		neww->last->next = neww;
3483		neww->next->last = neww;
3484
3485		neww->wind = getwin(fp);
3486		(void) fclose(fp);
3487
3488		wrefresh(neww->wind);
3489	    }
3490	    break;
3491
3492#if HAVE_WRESIZE
3493	case CTRL('X'):	/* resize window */
3494	    if (current) {
3495		pair *tmp, ul, lr;
3496		int mx, my;
3497
3498		move(0, 0);
3499		clrtoeol();
3500		addstr("Use arrows to move cursor, anything else to mark new corner");
3501		refresh();
3502
3503		getbegyx(current->wind, ul.y, ul.x);
3504
3505		tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3506		if (tmp == (pair *) 0) {
3507		    beep();
3508		    break;
3509		}
3510
3511		getmaxyx(current->wind, lr.y, lr.x);
3512		lr.y += (ul.y - 1);
3513		lr.x += (ul.x - 1);
3514		outerbox(ul, lr, FALSE);
3515		wnoutrefresh(stdscr);
3516
3517		/* strictly cosmetic hack for the test */
3518		getmaxyx(current->wind, my, mx);
3519		if (my > tmp->y - ul.y) {
3520		    getyx(current->wind, lr.y, lr.x);
3521		    wmove(current->wind, tmp->y - ul.y + 1, 0);
3522		    wclrtobot(current->wind);
3523		    wmove(current->wind, lr.y, lr.x);
3524		}
3525		if (mx > tmp->x - ul.x)
3526		    for (i = 0; i < my; i++) {
3527			wmove(current->wind, i, tmp->x - ul.x + 1);
3528			wclrtoeol(current->wind);
3529		    }
3530		wnoutrefresh(current->wind);
3531
3532		memcpy(&lr, tmp, sizeof(pair));
3533		(void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
3534
3535		getbegyx(current->wind, ul.y, ul.x);
3536		getmaxyx(current->wind, lr.y, lr.x);
3537		lr.y += (ul.y - 1);
3538		lr.x += (ul.x - 1);
3539		outerbox(ul, lr, TRUE);
3540		wnoutrefresh(stdscr);
3541
3542		wnoutrefresh(current->wind);
3543		move(0, 0);
3544		clrtoeol();
3545		doupdate();
3546	    }
3547	    break;
3548#endif /* HAVE_WRESIZE */
3549
3550	case KEY_F(10):	/* undocumented --- use this to test area clears */
3551	    selectcell(0, 0, LINES - 1, COLS - 1);
3552	    clrtobot();
3553	    refresh();
3554	    break;
3555
3556	case KEY_UP:
3557	    newwin_move(current, -1, 0);
3558	    break;
3559	case KEY_DOWN:
3560	    newwin_move(current, 1, 0);
3561	    break;
3562	case KEY_LEFT:
3563	    newwin_move(current, 0, -1);
3564	    break;
3565	case KEY_RIGHT:
3566	    newwin_move(current, 0, 1);
3567	    break;
3568
3569	case KEY_BACKSPACE:
3570	    /* FALLTHROUGH */
3571	case KEY_DC:
3572	    {
3573		int y, x;
3574		getyx(current->wind, y, x);
3575		if (--x < 0) {
3576		    if (--y < 0)
3577			break;
3578		    x = getmaxx(current->wind) - 1;
3579		}
3580		mvwdelch(current->wind, y, x);
3581	    }
3582	    break;
3583
3584	case '\r':
3585	    c = '\n';
3586	    /* FALLTHROUGH */
3587
3588	default:
3589	    if (current)
3590		waddch(current->wind, (chtype) c);
3591	    else
3592		beep();
3593	    break;
3594	}
3595	newwin_report(current);
3596	usescr = (current ? current->wind : stdscr);
3597	wrefresh(usescr);
3598    } while
3599	((c = wGetchar(usescr)) != QUIT
3600	 && !((c == ESCAPE) && (keypad_active(usescr)))
3601	 && (c != ERR));
3602
3603  breakout:
3604    while (current != 0)
3605	current = delete_framed(current, FALSE);
3606
3607    scrollok(stdscr, TRUE);	/* reset to driver's default */
3608#ifdef NCURSES_MOUSE_VERSION
3609    mousemask(0, (mmask_t *) 0);
3610#endif
3611    noraw();
3612    erase();
3613    endwin();
3614}
3615
3616/****************************************************************************
3617 *
3618 * Panels tester
3619 *
3620 ****************************************************************************/
3621
3622#if USE_LIBPANEL
3623static int nap_msec = 1;
3624
3625static NCURSES_CONST char *mod[] =
3626{
3627    "test ",
3628    "TEST ",
3629    "(**) ",
3630    "*()* ",
3631    "<--> ",
3632    "LAST "
3633};
3634
3635/*+-------------------------------------------------------------------------
3636	wait_a_while(msec)
3637--------------------------------------------------------------------------*/
3638static void
3639wait_a_while(int msec GCC_UNUSED)
3640{
3641#if HAVE_NAPMS
3642    if (nap_msec == 1)
3643	wGetchar(stdscr);
3644    else
3645	napms(nap_msec);
3646#else
3647    if (nap_msec == 1)
3648	wGetchar(stdscr);
3649    else if (msec > 1000)
3650	sleep((unsigned) msec / 1000);
3651    else
3652	sleep(1);
3653#endif
3654}				/* end of wait_a_while */
3655
3656/*+-------------------------------------------------------------------------
3657	saywhat(text)
3658--------------------------------------------------------------------------*/
3659static void
3660saywhat(NCURSES_CONST char *text)
3661{
3662    wmove(stdscr, LINES - 1, 0);
3663    wclrtoeol(stdscr);
3664    waddstr(stdscr, text);
3665}				/* end of saywhat */
3666
3667/*+-------------------------------------------------------------------------
3668	mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
3669--------------------------------------------------------------------------*/
3670static PANEL *
3671mkpanel(unsigned color, int rows, int cols, int tly, int tlx)
3672{
3673    WINDOW *win;
3674    PANEL *pan = 0;
3675
3676    if ((win = newwin(rows, cols, tly, tlx)) != 0) {
3677	if ((pan = new_panel(win)) == 0) {
3678	    delwin(win);
3679	} else if (has_colors()) {
3680	    int fg = (color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK;
3681	    int bg = color;
3682	    init_pair(color, fg, bg);
3683	    wbkgdset(win, COLOR_PAIR(color) | ' ');
3684	} else {
3685	    wbkgdset(win, A_BOLD | ' ');
3686	}
3687    }
3688    return pan;
3689}				/* end of mkpanel */
3690
3691/*+-------------------------------------------------------------------------
3692	rmpanel(pan)
3693--------------------------------------------------------------------------*/
3694static void
3695rmpanel(PANEL * pan)
3696{
3697    WINDOW *win = panel_window(pan);
3698    del_panel(pan);
3699    delwin(win);
3700}				/* end of rmpanel */
3701
3702/*+-------------------------------------------------------------------------
3703	pflush()
3704--------------------------------------------------------------------------*/
3705static void
3706pflush(void)
3707{
3708    update_panels();
3709    doupdate();
3710}				/* end of pflush */
3711
3712/*+-------------------------------------------------------------------------
3713	fill_panel(win)
3714--------------------------------------------------------------------------*/
3715static void
3716fill_panel(PANEL * pan)
3717{
3718    WINDOW *win = panel_window(pan);
3719    int num = ((const char *) panel_userptr(pan))[1];
3720    int y, x;
3721
3722    wmove(win, 1, 1);
3723    wprintw(win, "-pan%c-", num);
3724    wclrtoeol(win);
3725    box(win, 0, 0);
3726    for (y = 2; y < getmaxy(win) - 1; y++) {
3727	for (x = 1; x < getmaxx(win) - 1; x++) {
3728	    wmove(win, y, x);
3729	    waddch(win, UChar(num));
3730	}
3731    }
3732}				/* end of fill_panel */
3733
3734static void
3735demo_panels(void)
3736{
3737    int itmp;
3738    register int y, x;
3739
3740    refresh();
3741
3742    for (y = 0; y < LINES - 1; y++) {
3743	for (x = 0; x < COLS; x++)
3744	    wprintw(stdscr, "%d", (y + x) % 10);
3745    }
3746    for (y = 0; y < 5; y++) {
3747	PANEL *p1;
3748	PANEL *p2;
3749	PANEL *p3;
3750	PANEL *p4;
3751	PANEL *p5;
3752
3753	p1 = mkpanel(COLOR_RED,
3754		     LINES / 2 - 2,
3755		     COLS / 8 + 1,
3756		     0,
3757		     0);
3758	set_panel_userptr(p1, (NCURSES_CONST void *) "p1");
3759
3760	p2 = mkpanel(COLOR_GREEN,
3761		     LINES / 2 + 1,
3762		     COLS / 7,
3763		     LINES / 4,
3764		     COLS / 10);
3765	set_panel_userptr(p2, (NCURSES_CONST void *) "p2");
3766
3767	p3 = mkpanel(COLOR_YELLOW,
3768		     LINES / 4,
3769		     COLS / 10,
3770		     LINES / 2,
3771		     COLS / 9);
3772	set_panel_userptr(p3, (NCURSES_CONST void *) "p3");
3773
3774	p4 = mkpanel(COLOR_BLUE,
3775		     LINES / 2 - 2,
3776		     COLS / 8,
3777		     LINES / 2 - 2,
3778		     COLS / 3);
3779	set_panel_userptr(p4, (NCURSES_CONST void *) "p4");
3780
3781	p5 = mkpanel(COLOR_MAGENTA,
3782		     LINES / 2 - 2,
3783		     COLS / 8,
3784		     LINES / 2,
3785		     COLS / 2 - 2);
3786	set_panel_userptr(p5, (NCURSES_CONST void *) "p5");
3787
3788	fill_panel(p1);
3789	fill_panel(p2);
3790	fill_panel(p3);
3791	fill_panel(p4);
3792	fill_panel(p5);
3793	hide_panel(p4);
3794	hide_panel(p5);
3795	pflush();
3796	saywhat("press any key to continue");
3797	wait_a_while(nap_msec);
3798
3799	saywhat("h3 s1 s2 s4 s5; press any key to continue");
3800	move_panel(p1, 0, 0);
3801	hide_panel(p3);
3802	show_panel(p1);
3803	show_panel(p2);
3804	show_panel(p4);
3805	show_panel(p5);
3806	pflush();
3807	wait_a_while(nap_msec);
3808
3809	saywhat("s1; press any key to continue");
3810	show_panel(p1);
3811	pflush();
3812	wait_a_while(nap_msec);
3813
3814	saywhat("s2; press any key to continue");
3815	show_panel(p2);
3816	pflush();
3817	wait_a_while(nap_msec);
3818
3819	saywhat("m2; press any key to continue");
3820	move_panel(p2, LINES / 3 + 1, COLS / 8);
3821	pflush();
3822	wait_a_while(nap_msec);
3823
3824	saywhat("s3;");
3825	show_panel(p3);
3826	pflush();
3827	wait_a_while(nap_msec);
3828
3829	saywhat("m3; press any key to continue");
3830	move_panel(p3, LINES / 4 + 1, COLS / 15);
3831	pflush();
3832	wait_a_while(nap_msec);
3833
3834	saywhat("b3; press any key to continue");
3835	bottom_panel(p3);
3836	pflush();
3837	wait_a_while(nap_msec);
3838
3839	saywhat("s4; press any key to continue");
3840	show_panel(p4);
3841	pflush();
3842	wait_a_while(nap_msec);
3843
3844	saywhat("s5; press any key to continue");
3845	show_panel(p5);
3846	pflush();
3847	wait_a_while(nap_msec);
3848
3849	saywhat("t3; press any key to continue");
3850	top_panel(p3);
3851	pflush();
3852	wait_a_while(nap_msec);
3853
3854	saywhat("t1; press any key to continue");
3855	top_panel(p1);
3856	pflush();
3857	wait_a_while(nap_msec);
3858
3859	saywhat("t2; press any key to continue");
3860	top_panel(p2);
3861	pflush();
3862	wait_a_while(nap_msec);
3863
3864	saywhat("t3; press any key to continue");
3865	top_panel(p3);
3866	pflush();
3867	wait_a_while(nap_msec);
3868
3869	saywhat("t4; press any key to continue");
3870	top_panel(p4);
3871	pflush();
3872	wait_a_while(nap_msec);
3873
3874	for (itmp = 0; itmp < 6; itmp++) {
3875	    WINDOW *w4 = panel_window(p4);
3876	    WINDOW *w5 = panel_window(p5);
3877
3878	    saywhat("m4; press any key to continue");
3879	    wmove(w4, LINES / 8, 1);
3880	    waddstr(w4, mod[itmp]);
3881	    move_panel(p4, LINES / 6, itmp * (COLS / 8));
3882	    wmove(w5, LINES / 6, 1);
3883	    waddstr(w5, mod[itmp]);
3884	    pflush();
3885	    wait_a_while(nap_msec);
3886
3887	    saywhat("m5; press any key to continue");
3888	    wmove(w4, LINES / 6, 1);
3889	    waddstr(w4, mod[itmp]);
3890	    move_panel(p5, LINES / 3 - 1, (itmp * 10) + 6);
3891	    wmove(w5, LINES / 8, 1);
3892	    waddstr(w5, mod[itmp]);
3893	    pflush();
3894	    wait_a_while(nap_msec);
3895	}
3896
3897	saywhat("m4; press any key to continue");
3898	move_panel(p4, LINES / 6, itmp * (COLS / 8));
3899	pflush();
3900	wait_a_while(nap_msec);
3901
3902	saywhat("t5; press any key to continue");
3903	top_panel(p5);
3904	pflush();
3905	wait_a_while(nap_msec);
3906
3907	saywhat("t2; press any key to continue");
3908	top_panel(p2);
3909	pflush();
3910	wait_a_while(nap_msec);
3911
3912	saywhat("t1; press any key to continue");
3913	top_panel(p1);
3914	pflush();
3915	wait_a_while(nap_msec);
3916
3917	saywhat("d2; press any key to continue");
3918	rmpanel(p2);
3919	pflush();
3920	wait_a_while(nap_msec);
3921
3922	saywhat("h3; press any key to continue");
3923	hide_panel(p3);
3924	pflush();
3925	wait_a_while(nap_msec);
3926
3927	saywhat("d1; press any key to continue");
3928	rmpanel(p1);
3929	pflush();
3930	wait_a_while(nap_msec);
3931
3932	saywhat("d4; press any key to continue");
3933	rmpanel(p4);
3934	pflush();
3935	wait_a_while(nap_msec);
3936
3937	saywhat("d5; press any key to continue");
3938	rmpanel(p5);
3939	pflush();
3940
3941	rmpanel(p3);
3942	pflush();
3943
3944	wait_a_while(nap_msec);
3945	if (nap_msec == 1)
3946	    break;
3947	nap_msec = 100L;
3948    }
3949
3950    erase();
3951    endwin();
3952}
3953
3954/****************************************************************************
3955 *
3956 * Pad tester
3957 *
3958 ****************************************************************************/
3959
3960#define GRIDSIZE	3
3961
3962static bool pending_pan = FALSE;
3963static bool show_panner_legend = TRUE;
3964
3965static int
3966panner_legend(int line)
3967{
3968    static const char *const legend[] =
3969    {
3970	"Use arrow keys (or U,D,L,R) to pan, q to quit, ! to shell-out.",
3971	"Use +,- (or j,k) to grow/shrink the panner vertically.",
3972	"Use <,> (or h,l) to grow/shrink the panner horizontally.",
3973	"Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
3974    };
3975    int n = (SIZEOF(legend) - (LINES - line));
3976    if (line < LINES && (n >= 0)) {
3977	move(line, 0);
3978	if (show_panner_legend)
3979	    printw("%s", legend[n]);
3980	clrtoeol();
3981	return show_panner_legend;
3982    }
3983    return FALSE;
3984}
3985
3986static void
3987panner_h_cleanup(int from_y, int from_x, int to_x)
3988{
3989    if (!panner_legend(from_y))
3990	do_h_line(from_y, from_x, ' ', to_x);
3991}
3992
3993static void
3994panner_v_cleanup(int from_y, int from_x, int to_y)
3995{
3996    if (!panner_legend(from_y))
3997	do_v_line(from_y, from_x, ' ', to_y);
3998}
3999
4000static void
4001fill_pad(WINDOW *panpad, bool pan_lines)
4002{
4003    int y, x;
4004    unsigned gridcount = 0;
4005
4006    wmove(panpad, 0, 0);
4007    for (y = 0; y < getmaxy(panpad); y++) {
4008	for (x = 0; x < getmaxx(panpad); x++) {
4009	    if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4010		if (y == 0 && x == 0)
4011		    waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4012		else if (y == 0)
4013		    waddch(panpad, pan_lines ? ACS_TTEE : '+');
4014		else if (y == 0 || x == 0)
4015		    waddch(panpad, pan_lines ? ACS_LTEE : '+');
4016		else
4017		    waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4018					     (gridcount++ % 26)));
4019	    } else if (y % GRIDSIZE == 0)
4020		waddch(panpad, pan_lines ? ACS_HLINE : '-');
4021	    else if (x % GRIDSIZE == 0)
4022		waddch(panpad, pan_lines ? ACS_VLINE : '|');
4023	    else
4024		waddch(panpad, ' ');
4025	}
4026    }
4027}
4028
4029static void
4030panner(WINDOW *pad,
4031       int top_x, int top_y, int porty, int portx,
4032       int (*pgetc) (WINDOW *))
4033{
4034#if HAVE_GETTIMEOFDAY
4035    struct timeval before, after;
4036    bool timing = TRUE;
4037#endif
4038    bool pan_lines = FALSE;
4039    bool scrollers = TRUE;
4040    int basex = 0;
4041    int basey = 0;
4042    int pxmax, pymax, lowend, highend, c;
4043
4044    getmaxyx(pad, pymax, pxmax);
4045    scrollok(stdscr, FALSE);	/* we don't want stdscr to scroll! */
4046
4047    c = KEY_REFRESH;
4048    do {
4049#ifdef NCURSES_VERSION
4050	/*
4051	 * During shell-out, the user may have resized the window.  Adjust
4052	 * the port size of the pad to accommodate this.  Ncurses automatically
4053	 * resizes all of the normal windows to fit on the new screen.
4054	 */
4055	if (top_x > COLS)
4056	    top_x = COLS;
4057	if (portx > COLS)
4058	    portx = COLS;
4059	if (top_y > LINES)
4060	    top_y = LINES;
4061	if (porty > LINES)
4062	    porty = LINES;
4063#endif
4064	switch (c) {
4065	case KEY_REFRESH:
4066	    erase();
4067
4068	    /* FALLTHRU */
4069	case '?':
4070	    if (c == '?')
4071		show_panner_legend = !show_panner_legend;
4072	    panner_legend(LINES - 4);
4073	    panner_legend(LINES - 3);
4074	    panner_legend(LINES - 2);
4075	    panner_legend(LINES - 1);
4076	    break;
4077	case 'a':
4078	    pan_lines = !pan_lines;
4079	    fill_pad(pad, pan_lines);
4080	    pending_pan = FALSE;
4081	    break;
4082
4083#if HAVE_GETTIMEOFDAY
4084	case 't':
4085	    timing = !timing;
4086	    if (!timing)
4087		panner_legend(LINES - 1);
4088	    break;
4089#endif
4090	case 's':
4091	    scrollers = !scrollers;
4092	    break;
4093
4094	    /* Move the top-left corner of the pad, keeping the bottom-right
4095	     * corner fixed.
4096	     */
4097	case 'h':		/* increase-columns: move left edge to left */
4098	    if (top_x <= 0)
4099		beep();
4100	    else {
4101		panner_v_cleanup(top_y, top_x, porty);
4102		top_x--;
4103	    }
4104	    break;
4105
4106	case 'j':		/* decrease-lines: move top-edge down */
4107	    if (top_y >= porty)
4108		beep();
4109	    else {
4110		panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4111		top_y++;
4112	    }
4113	    break;
4114
4115	case 'k':		/* increase-lines: move top-edge up */
4116	    if (top_y <= 0)
4117		beep();
4118	    else {
4119		top_y--;
4120		panner_h_cleanup(top_y, top_x, portx);
4121	    }
4122	    break;
4123
4124	case 'l':		/* decrease-columns: move left-edge to right */
4125	    if (top_x >= portx)
4126		beep();
4127	    else {
4128		panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4129		top_x++;
4130	    }
4131	    break;
4132
4133	    /* Move the bottom-right corner of the pad, keeping the top-left
4134	     * corner fixed.
4135	     */
4136	case KEY_IC:		/* increase-columns: move right-edge to right */
4137	    if (portx >= pxmax || portx >= COLS)
4138		beep();
4139	    else {
4140		panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4141		++portx;
4142	    }
4143	    break;
4144
4145	case KEY_IL:		/* increase-lines: move bottom-edge down */
4146	    if (porty >= pymax || porty >= LINES)
4147		beep();
4148	    else {
4149		panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4150		++porty;
4151	    }
4152	    break;
4153
4154	case KEY_DC:		/* decrease-columns: move bottom edge up */
4155	    if (portx <= top_x)
4156		beep();
4157	    else {
4158		portx--;
4159		panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4160	    }
4161	    break;
4162
4163	case KEY_DL:		/* decrease-lines */
4164	    if (porty <= top_y)
4165		beep();
4166	    else {
4167		porty--;
4168		panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4169	    }
4170	    break;
4171
4172	case KEY_LEFT:		/* pan leftwards */
4173	    if (basex > 0)
4174		basex--;
4175	    else
4176		beep();
4177	    break;
4178
4179	case KEY_RIGHT:	/* pan rightwards */
4180	    if (basex + portx - (pymax > porty) < pxmax)
4181		basex++;
4182	    else
4183		beep();
4184	    break;
4185
4186	case KEY_UP:		/* pan upwards */
4187	    if (basey > 0)
4188		basey--;
4189	    else
4190		beep();
4191	    break;
4192
4193	case KEY_DOWN:		/* pan downwards */
4194	    if (basey + porty - (pxmax > portx) < pymax)
4195		basey++;
4196	    else
4197		beep();
4198	    break;
4199
4200	case 'H':
4201	case KEY_HOME:
4202	case KEY_FIND:
4203	    basey = 0;
4204	    break;
4205
4206	case 'E':
4207	case KEY_END:
4208	case KEY_SELECT:
4209	    basey = pymax - porty;
4210	    if (basey < 0)
4211		basey = 0;
4212	    break;
4213
4214	default:
4215	    beep();
4216	    break;
4217	}
4218
4219	mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4220	do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4221	do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4222
4223	if (scrollers && (pxmax > portx - 1)) {
4224	    int length = (portx - top_x - 1);
4225	    float ratio = ((float) length) / ((float) pxmax);
4226
4227	    lowend = (int) (top_x + (basex * ratio));
4228	    highend = (int) (top_x + ((basex + length) * ratio));
4229
4230	    do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4231	    if (highend < portx) {
4232		attron(A_REVERSE);
4233		do_h_line(porty - 1, lowend, ' ', highend + 1);
4234		attroff(A_REVERSE);
4235		do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4236	    }
4237	} else
4238	    do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4239
4240	if (scrollers && (pymax > porty - 1)) {
4241	    int length = (porty - top_y - 1);
4242	    float ratio = ((float) length) / ((float) pymax);
4243
4244	    lowend = (int) (top_y + (basey * ratio));
4245	    highend = (int) (top_y + ((basey + length) * ratio));
4246
4247	    do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4248	    if (highend < porty) {
4249		attron(A_REVERSE);
4250		do_v_line(lowend, portx - 1, ' ', highend + 1);
4251		attroff(A_REVERSE);
4252		do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4253	    }
4254	} else
4255	    do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4256
4257	mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4258	mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4259	mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4260
4261	if (!pending_pan) {
4262#if HAVE_GETTIMEOFDAY
4263	    gettimeofday(&before, 0);
4264#endif
4265	    wnoutrefresh(stdscr);
4266
4267	    pnoutrefresh(pad,
4268			 basey, basex,
4269			 top_y, top_x,
4270			 porty - (pxmax > portx) - 1,
4271			 portx - (pymax > porty) - 1);
4272
4273	    doupdate();
4274#if HAVE_GETTIMEOFDAY
4275	    if (timing) {
4276		double elapsed;
4277		gettimeofday(&after, 0);
4278		elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4279		    - (before.tv_sec + before.tv_usec / 1.0e6);
4280		move(LINES - 1, COLS - 12);
4281		printw("Secs: %2.03f", elapsed);
4282		refresh();
4283	    }
4284#endif
4285	}
4286
4287    } while
4288	((c = pgetc(pad)) != KEY_EXIT);
4289
4290    scrollok(stdscr, TRUE);	/* reset to driver's default */
4291}
4292
4293static int
4294padgetch(WINDOW *win)
4295{
4296    static int count;
4297    static int last;
4298    int c;
4299
4300    if ((pending_pan = (count > 0)) != FALSE) {
4301	count--;
4302	pending_pan = (count != 0);
4303    } else {
4304	for (;;) {
4305	    switch (c = wGetchar(win)) {
4306	    case '!':
4307		ShellOut(FALSE);
4308		/* FALLTHRU */
4309	    case CTRL('r'):
4310		endwin();
4311		refresh();
4312		c = KEY_REFRESH;
4313		break;
4314	    case CTRL('l'):
4315		c = KEY_REFRESH;
4316		break;
4317	    case 'U':
4318		c = KEY_UP;
4319		break;
4320	    case 'D':
4321		c = KEY_DOWN;
4322		break;
4323	    case 'R':
4324		c = KEY_RIGHT;
4325		break;
4326	    case 'L':
4327		c = KEY_LEFT;
4328		break;
4329	    case '+':
4330		c = KEY_IL;
4331		break;
4332	    case '-':
4333		c = KEY_DL;
4334		break;
4335	    case '>':
4336		c = KEY_IC;
4337		break;
4338	    case '<':
4339		c = KEY_DC;
4340		break;
4341	    case ERR:		/* FALLTHRU */
4342	    case 'q':
4343		count = 0;
4344		c = KEY_EXIT;
4345		break;
4346	    default:
4347		if (c >= '0' && c <= '9') {
4348		    count = count * 10 + (c - '0');
4349		    continue;
4350		}
4351		break;
4352	    }
4353	    last = c;
4354	    break;
4355	}
4356	if (count > 0)
4357	    count--;
4358    }
4359    return (last);
4360}
4361
4362#define PAD_HIGH 200
4363#define PAD_WIDE 200
4364
4365static void
4366demo_pad(void)
4367/* Demonstrate pads. */
4368{
4369    WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4370
4371    if (panpad == 0) {
4372	Cannot("cannot create requested pad");
4373	return;
4374    }
4375
4376    fill_pad(panpad, FALSE);
4377
4378    panner_legend(LINES - 4);
4379    panner_legend(LINES - 3);
4380    panner_legend(LINES - 2);
4381    panner_legend(LINES - 1);
4382
4383    keypad(panpad, TRUE);
4384
4385    /* Make the pad (initially) narrow enough that a trace file won't wrap.
4386     * We'll still be able to widen it during a test, since that's required
4387     * for testing boundaries.
4388     */
4389    panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4390
4391    delwin(panpad);
4392    endwin();
4393    erase();
4394}
4395#endif /* USE_LIBPANEL */
4396
4397/****************************************************************************
4398 *
4399 * Tests from John Burnell's PDCurses tester
4400 *
4401 ****************************************************************************/
4402
4403static void
4404Continue(WINDOW *win)
4405{
4406    noecho();
4407    wmove(win, 10, 1);
4408    mvwaddstr(win, 10, 1, " Press any key to continue");
4409    wrefresh(win);
4410    wGetchar(win);
4411}
4412
4413static void
4414flushinp_test(WINDOW *win)
4415/* Input test, adapted from John Burnell's PDCurses tester */
4416{
4417    int w, h, bx, by, sw, sh, i;
4418
4419    WINDOW *subWin;
4420    wclear(win);
4421
4422    getmaxyx(win, h, w);
4423    getbegyx(win, by, bx);
4424    sw = w / 3;
4425    sh = h / 3;
4426    if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
4427	return;
4428
4429#ifdef A_COLOR
4430    if (has_colors()) {
4431	init_pair(2, COLOR_CYAN, COLOR_BLUE);
4432	wbkgd(subWin, COLOR_PAIR(2) | ' ');
4433    }
4434#endif
4435    wattrset(subWin, A_BOLD);
4436    box(subWin, ACS_VLINE, ACS_HLINE);
4437    mvwaddstr(subWin, 2, 1, "This is a subwindow");
4438    wrefresh(win);
4439
4440    /*
4441     * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
4442     * it only happened to "work" on SVr4 because that implementation does not
4443     * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
4444     * test behavior, we're using 'cbreak()', which will allow a single
4445     * character to return without needing a newline. - T.Dickey 1997/10/11.
4446     */
4447    cbreak();
4448    mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
4449
4450    mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
4451    mvwaddstr(win, 3, 1,
4452	      "These should be discarded (not echoed) after the subwindow goes away.");
4453    wrefresh(win);
4454
4455    for (i = 0; i < 5; i++) {
4456	mvwprintw(subWin, 1, 1, "Time = %d", i);
4457	wrefresh(subWin);
4458	napms(1000);
4459	flushinp();
4460    }
4461
4462    delwin(subWin);
4463    werase(win);
4464    flash();
4465    wrefresh(win);
4466    napms(1000);
4467
4468    mvwaddstr(win, 2, 1,
4469	      "If you were still typing when the window timer expired,");
4470    mvwaddstr(win, 3, 1,
4471	      "or else you typed nothing at all while it was running,");
4472    mvwaddstr(win, 4, 1,
4473	      "test was invalid.  You'll see garbage or nothing at all. ");
4474    mvwaddstr(win, 6, 1, "Press a key");
4475    wmove(win, 9, 10);
4476    wrefresh(win);
4477    echo();
4478    wGetchar(win);
4479    flushinp();
4480    mvwaddstr(win, 12, 0,
4481	      "If you see any key other than what you typed, flushinp() is broken.");
4482    Continue(win);
4483
4484    wmove(win, 9, 10);
4485    wdelch(win);
4486    wrefresh(win);
4487    wmove(win, 12, 0);
4488    clrtoeol();
4489    waddstr(win,
4490	    "What you typed should now have been deleted; if not, wdelch() failed.");
4491    Continue(win);
4492
4493    cbreak();
4494}
4495
4496/****************************************************************************
4497 *
4498 * Menu test
4499 *
4500 ****************************************************************************/
4501
4502#if USE_LIBMENU
4503
4504#define MENU_Y	8
4505#define MENU_X	8
4506
4507static int
4508menu_virtualize(int c)
4509{
4510    if (c == '\n' || c == KEY_EXIT)
4511	return (MAX_COMMAND + 1);
4512    else if (c == 'u')
4513	return (REQ_SCR_ULINE);
4514    else if (c == 'd')
4515	return (REQ_SCR_DLINE);
4516    else if (c == 'b' || c == KEY_NPAGE)
4517	return (REQ_SCR_UPAGE);
4518    else if (c == 'f' || c == KEY_PPAGE)
4519	return (REQ_SCR_DPAGE);
4520    else if (c == 'n' || c == KEY_DOWN)
4521	return (REQ_NEXT_ITEM);
4522    else if (c == 'p' || c == KEY_UP)
4523	return (REQ_PREV_ITEM);
4524    else if (c == ' ')
4525	return (REQ_TOGGLE_ITEM);
4526    else {
4527	if (c != KEY_MOUSE)
4528	    beep();
4529	return (c);
4530    }
4531}
4532
4533static const char *animals[] =
4534{
4535    "Lions",
4536    "Tigers",
4537    "Bears",
4538    "(Oh my!)",
4539    "Newts",
4540    "Platypi",
4541    "Lemurs",
4542    "(Oh really?!)",
4543    "Leopards",
4544    "Panthers",
4545    "Pumas",
4546    "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4547    "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
4548    (char *) 0
4549};
4550
4551static void
4552menu_test(void)
4553{
4554    MENU *m;
4555    ITEM *items[SIZEOF(animals)];
4556    ITEM **ip = items;
4557    const char **ap;
4558    int mrows, mcols, c;
4559    WINDOW *menuwin;
4560
4561#ifdef NCURSES_MOUSE_VERSION
4562    mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
4563#endif
4564    mvaddstr(0, 0, "This is the menu test:");
4565    mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
4566    mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
4567    mvaddstr(4, 0,
4568	     "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
4569    mvaddstr(5, 0, "  Press return to exit.");
4570    refresh();
4571
4572    for (ap = animals; *ap; ap++)
4573	*ip++ = new_item(*ap, "");
4574    *ip = (ITEM *) 0;
4575
4576    m = new_menu(items);
4577
4578    set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
4579    scale_menu(m, &mrows, &mcols);
4580
4581    menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
4582    set_menu_win(m, menuwin);
4583    keypad(menuwin, TRUE);
4584    box(menuwin, 0, 0);
4585
4586    set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
4587
4588    post_menu(m);
4589
4590    while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
4591	if (c == E_NOT_POSTED)
4592	    break;
4593	if (c == E_REQUEST_DENIED)
4594	    beep();
4595	continue;
4596    }
4597
4598    (void) mvprintw(LINES - 2, 0,
4599		    "You chose: %s\n", item_name(current_item(m)));
4600    (void) addstr("Press any key to continue...");
4601    wGetchar(stdscr);
4602
4603    unpost_menu(m);
4604    delwin(menuwin);
4605
4606    free_menu(m);
4607    for (ip = items; *ip; ip++)
4608	free_item(*ip);
4609#ifdef NCURSES_MOUSE_VERSION
4610    mousemask(0, (mmask_t *) 0);
4611#endif
4612}
4613
4614#ifdef TRACE
4615#define T_TBL(name) { #name, name }
4616static struct {
4617    const char *name;
4618    unsigned mask;
4619} t_tbl[] = {
4620
4621    T_TBL(TRACE_DISABLE),
4622	T_TBL(TRACE_TIMES),
4623	T_TBL(TRACE_TPUTS),
4624	T_TBL(TRACE_UPDATE),
4625	T_TBL(TRACE_MOVE),
4626	T_TBL(TRACE_CHARPUT),
4627	T_TBL(TRACE_ORDINARY),
4628	T_TBL(TRACE_CALLS),
4629	T_TBL(TRACE_VIRTPUT),
4630	T_TBL(TRACE_IEVENT),
4631	T_TBL(TRACE_BITS),
4632	T_TBL(TRACE_ICALLS),
4633	T_TBL(TRACE_CCALLS),
4634	T_TBL(TRACE_DATABASE),
4635	T_TBL(TRACE_ATTRS),
4636	T_TBL(TRACE_MAXIMUM),
4637    {
4638	(char *) 0, 0
4639    }
4640};
4641
4642static char *
4643tracetrace(unsigned tlevel)
4644{
4645    static char *buf;
4646    int n;
4647
4648    if (buf == 0) {
4649	size_t need = 12;
4650	for (n = 0; t_tbl[n].name != 0; n++)
4651	    need += strlen(t_tbl[n].name) + 2;
4652	buf = (char *) malloc(need);
4653    }
4654    sprintf(buf, "0x%02x = {", tlevel);
4655    if (tlevel == 0) {
4656	sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
4657    } else {
4658	for (n = 1; t_tbl[n].name != 0; n++)
4659	    if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
4660		strcat(buf, t_tbl[n].name);
4661		strcat(buf, ", ");
4662	    }
4663    }
4664    if (buf[strlen(buf) - 2] == ',')
4665	buf[strlen(buf) - 2] = '\0';
4666    return (strcat(buf, "}"));
4667}
4668
4669/* fake a dynamically reconfigurable menu using the 0th entry to deselect
4670 * the others
4671 */
4672static int
4673run_trace_menu(MENU * m)
4674{
4675    ITEM **items;
4676    ITEM *i, **p;
4677
4678    for (;;) {
4679	bool changed = FALSE;
4680	switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
4681	case E_UNKNOWN_COMMAND:
4682	    return FALSE;
4683	default:
4684	    items = menu_items(m);
4685	    i = current_item(m);
4686	    if (i == items[0]) {
4687		if (item_value(i)) {
4688		    for (p = items + 1; *p != 0; p++)
4689			if (item_value(*p)) {
4690			    set_item_value(*p, FALSE);
4691			    changed = TRUE;
4692			}
4693		}
4694	    } else {
4695		for (p = items + 1; *p != 0; p++)
4696		    if (item_value(*p)) {
4697			set_item_value(items[0], FALSE);
4698			changed = TRUE;
4699			break;
4700		    }
4701	    }
4702	    if (!changed)
4703		return TRUE;
4704	}
4705    }
4706}
4707
4708static void
4709trace_set(void)
4710/* interactively set the trace level */
4711{
4712    MENU *m;
4713    ITEM *items[SIZEOF(t_tbl)];
4714    ITEM **ip = items;
4715    int mrows, mcols;
4716    unsigned newtrace;
4717    int n;
4718    WINDOW *menuwin;
4719
4720    mvaddstr(0, 0, "Interactively set trace level:");
4721    mvaddstr(2, 0, "  Press space bar to toggle a selection.");
4722    mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
4723    mvaddstr(4, 0, "  Press return to set the trace level.");
4724    mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
4725
4726    refresh();
4727
4728    for (n = 0; t_tbl[n].name != 0; n++)
4729	*ip++ = new_item(t_tbl[n].name, "");
4730    *ip = (ITEM *) 0;
4731
4732    m = new_menu(items);
4733
4734    set_menu_format(m, 0, 2);
4735    scale_menu(m, &mrows, &mcols);
4736
4737    menu_opts_off(m, O_ONEVALUE);
4738    menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
4739    set_menu_win(m, menuwin);
4740    keypad(menuwin, TRUE);
4741    box(menuwin, 0, 0);
4742
4743    set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
4744
4745    post_menu(m);
4746
4747    for (ip = menu_items(m); *ip; ip++) {
4748	unsigned mask = t_tbl[item_index(*ip)].mask;
4749	if (mask == 0)
4750	    set_item_value(*ip, _nc_tracing == 0);
4751	else if ((mask & _nc_tracing) == mask)
4752	    set_item_value(*ip, TRUE);
4753    }
4754
4755    while (run_trace_menu(m))
4756	continue;
4757
4758    newtrace = 0;
4759    for (ip = menu_items(m); *ip; ip++)
4760	if (item_value(*ip))
4761	    newtrace |= t_tbl[item_index(*ip)].mask;
4762    trace(newtrace);
4763    _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
4764
4765    (void) mvprintw(LINES - 2, 0,
4766		    "Trace level is %s\n", tracetrace(_nc_tracing));
4767    (void) addstr("Press any key to continue...");
4768    wGetchar(stdscr);
4769
4770    unpost_menu(m);
4771    delwin(menuwin);
4772
4773    free_menu(m);
4774    for (ip = items; *ip; ip++)
4775	free_item(*ip);
4776}
4777#endif /* TRACE */
4778#endif /* USE_LIBMENU */
4779
4780/****************************************************************************
4781 *
4782 * Forms test
4783 *
4784 ****************************************************************************/
4785#if USE_LIBFORM
4786static FIELD *
4787make_label(int frow, int fcol, NCURSES_CONST char *label)
4788{
4789    FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
4790
4791    if (f) {
4792	set_field_buffer(f, 0, label);
4793	set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
4794    }
4795    return (f);
4796}
4797
4798static FIELD *
4799make_field(int frow, int fcol, int rows, int cols, bool secure)
4800{
4801    FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
4802
4803    if (f) {
4804	set_field_back(f, A_UNDERLINE);
4805	set_field_userptr(f, (void *) 0);
4806    }
4807    return (f);
4808}
4809
4810static void
4811display_form(FORM * f)
4812{
4813    WINDOW *w;
4814    int rows, cols;
4815
4816    scale_form(f, &rows, &cols);
4817
4818    if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
4819	set_form_win(f, w);
4820	set_form_sub(f, derwin(w, rows, cols, 1, 2));
4821	box(w, 0, 0);
4822	keypad(w, TRUE);
4823    }
4824
4825    if (post_form(f) != E_OK)
4826	wrefresh(w);
4827}
4828
4829static void
4830erase_form(FORM * f)
4831{
4832    WINDOW *w = form_win(f);
4833    WINDOW *s = form_sub(f);
4834
4835    unpost_form(f);
4836    werase(w);
4837    wrefresh(w);
4838    delwin(s);
4839    delwin(w);
4840}
4841
4842static int
4843edit_secure(FIELD * me, int c)
4844{
4845    int rows, cols, frow, fcol, nrow, nbuf;
4846
4847    if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
4848	&& nbuf > 0) {
4849	char temp[80];
4850	long len;
4851
4852	strcpy(temp, field_buffer(me, 1));
4853	len = (long) (char *) field_userptr(me);
4854	if (c <= KEY_MAX) {
4855	    if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
4856		temp[len++] = c;
4857		temp[len] = 0;
4858		set_field_buffer(me, 1, temp);
4859		c = '*';
4860	    } else {
4861		c = 0;
4862	    }
4863	} else {
4864	    switch (c) {
4865	    case REQ_BEG_FIELD:
4866	    case REQ_CLR_EOF:
4867	    case REQ_CLR_EOL:
4868	    case REQ_DEL_LINE:
4869	    case REQ_DEL_WORD:
4870	    case REQ_DOWN_CHAR:
4871	    case REQ_END_FIELD:
4872	    case REQ_INS_CHAR:
4873	    case REQ_INS_LINE:
4874	    case REQ_LEFT_CHAR:
4875	    case REQ_NEW_LINE:
4876	    case REQ_NEXT_WORD:
4877	    case REQ_PREV_WORD:
4878	    case REQ_RIGHT_CHAR:
4879	    case REQ_UP_CHAR:
4880		c = 0;		/* we don't want to do inline editing */
4881		break;
4882	    case REQ_CLR_FIELD:
4883		if (len) {
4884		    temp[0] = 0;
4885		    set_field_buffer(me, 1, temp);
4886		}
4887		break;
4888	    case REQ_DEL_CHAR:
4889	    case REQ_DEL_PREV:
4890		if (len) {
4891		    temp[--len] = 0;
4892		    set_field_buffer(me, 1, temp);
4893		}
4894		break;
4895	    }
4896	}
4897	set_field_userptr(me, (void *) len);
4898    }
4899    return c;
4900}
4901
4902static int
4903form_virtualize(FORM * f, WINDOW *w)
4904{
4905    static const struct {
4906	int code;
4907	int result;
4908    } lookup[] = {
4909	{
4910	    CTRL('A'), REQ_NEXT_CHOICE
4911	},
4912	{
4913	    CTRL('B'), REQ_PREV_WORD
4914	},
4915	{
4916	    CTRL('C'), REQ_CLR_EOL
4917	},
4918	{
4919	    CTRL('D'), REQ_DOWN_FIELD
4920	},
4921	{
4922	    CTRL('E'), REQ_END_FIELD
4923	},
4924	{
4925	    CTRL('F'), REQ_NEXT_PAGE
4926	},
4927	{
4928	    CTRL('G'), REQ_DEL_WORD
4929	},
4930	{
4931	    CTRL('H'), REQ_DEL_PREV
4932	},
4933	{
4934	    CTRL('I'), REQ_INS_CHAR
4935	},
4936	{
4937	    CTRL('K'), REQ_CLR_EOF
4938	},
4939	{
4940	    CTRL('L'), REQ_LEFT_FIELD
4941	},
4942	{
4943	    CTRL('M'), REQ_NEW_LINE
4944	},
4945	{
4946	    CTRL('N'), REQ_NEXT_FIELD
4947	},
4948	{
4949	    CTRL('O'), REQ_INS_LINE
4950	},
4951	{
4952	    CTRL('P'), REQ_PREV_FIELD
4953	},
4954	{
4955	    CTRL('R'), REQ_RIGHT_FIELD
4956	},
4957	{
4958	    CTRL('S'), REQ_BEG_FIELD
4959	},
4960	{
4961	    CTRL('U'), REQ_UP_FIELD
4962	},
4963	{
4964	    CTRL('V'), REQ_DEL_CHAR
4965	},
4966	{
4967	    CTRL('W'), REQ_NEXT_WORD
4968	},
4969	{
4970	    CTRL('X'), REQ_CLR_FIELD
4971	},
4972	{
4973	    CTRL('Y'), REQ_DEL_LINE
4974	},
4975	{
4976	    CTRL('Z'), REQ_PREV_CHOICE
4977	},
4978	{
4979	    ESCAPE, MAX_FORM_COMMAND + 1
4980	},
4981	{
4982	    KEY_BACKSPACE, REQ_DEL_PREV
4983	},
4984	{
4985	    KEY_DOWN, REQ_DOWN_CHAR
4986	},
4987	{
4988	    KEY_END, REQ_LAST_FIELD
4989	},
4990	{
4991	    KEY_HOME, REQ_FIRST_FIELD
4992	},
4993	{
4994	    KEY_LEFT, REQ_LEFT_CHAR
4995	},
4996	{
4997	    KEY_LL, REQ_LAST_FIELD
4998	},
4999	{
5000	    KEY_NEXT, REQ_NEXT_FIELD
5001	},
5002	{
5003	    KEY_NPAGE, REQ_NEXT_PAGE
5004	},
5005	{
5006	    KEY_PPAGE, REQ_PREV_PAGE
5007	},
5008	{
5009	    KEY_PREVIOUS, REQ_PREV_FIELD
5010	},
5011	{
5012	    KEY_RIGHT, REQ_RIGHT_CHAR
5013	},
5014	{
5015	    KEY_UP, REQ_UP_CHAR
5016	},
5017	{
5018	    QUIT, MAX_FORM_COMMAND + 1
5019	}
5020    };
5021
5022    static int mode = REQ_INS_MODE;
5023    int c = wGetchar(w);
5024    unsigned n;
5025    FIELD *me = current_field(f);
5026    bool current = TRUE;
5027
5028    if (c == CTRL(']')) {
5029	if (mode == REQ_INS_MODE) {
5030	    mode = REQ_OVL_MODE;
5031	} else {
5032	    mode = REQ_INS_MODE;
5033	}
5034	c = mode;
5035    } else {
5036	for (n = 0; n < SIZEOF(lookup); n++) {
5037	    if (lookup[n].code == c) {
5038		c = lookup[n].result;
5039		break;
5040	    }
5041	}
5042    }
5043    mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5044
5045    /*
5046     * Force the field that the user is typing into to be in reverse video,
5047     * while the other fields are shown underlined.
5048     */
5049    switch (c) {
5050    case REQ_BEG_FIELD:
5051    case REQ_CLR_EOF:
5052    case REQ_CLR_EOL:
5053    case REQ_CLR_FIELD:
5054    case REQ_DEL_CHAR:
5055    case REQ_DEL_LINE:
5056    case REQ_DEL_PREV:
5057    case REQ_DEL_WORD:
5058    case REQ_END_FIELD:
5059    case REQ_INS_CHAR:
5060    case REQ_INS_LINE:
5061    case REQ_LEFT_CHAR:
5062    case REQ_LEFT_FIELD:
5063    case REQ_NEXT_WORD:
5064    case REQ_RIGHT_CHAR:
5065	current = TRUE;
5066	break;
5067    default:
5068	current = (c < KEY_MAX);
5069	break;
5070    }
5071    if (current) {
5072	c = edit_secure(me, c);
5073	set_field_back(me, A_REVERSE);
5074    } else {
5075	c = edit_secure(me, c);
5076	set_field_back(me, A_UNDERLINE);
5077    }
5078    return c;
5079}
5080
5081static int
5082my_form_driver(FORM * form, int c)
5083{
5084    if (c == (MAX_FORM_COMMAND + 1)
5085	&& form_driver(form, REQ_VALIDATION) == E_OK)
5086	return (TRUE);
5087    else {
5088	beep();
5089	return (FALSE);
5090    }
5091}
5092
5093/*
5094 * Allow a middle initial, optionally with a '.' to end it.
5095 */
5096static bool
5097mi_field_check(FIELD * fld, const void *data GCC_UNUSED)
5098{
5099    char *s = field_buffer(fld, 0);
5100    int state = 0;
5101    int n;
5102
5103    for (n = 0; s[n] != '\0'; ++n) {
5104	switch (state) {
5105	case 0:
5106	    if (s[n] == '.') {
5107		if (n != 1)
5108		    return FALSE;
5109		state = 2;
5110	    } else if (isspace(UChar(s[n]))) {
5111		state = 2;
5112	    }
5113	    break;
5114	case 2:
5115	    if (!isspace(UChar(s[n])))
5116		return FALSE;
5117	    break;
5118	}
5119    }
5120
5121    /* force the form to display a leading capital */
5122    if (islower(UChar(s[0]))) {
5123	s[0] = toupper(UChar(s[0]));
5124	set_field_buffer(fld, 0, s);
5125    }
5126    return TRUE;
5127}
5128
5129static bool
5130mi_char_check(int ch, const void *data GCC_UNUSED)
5131{
5132    return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5133}
5134
5135/*
5136 * Passwords should be at least 6 characters.
5137 */
5138static bool
5139pw_field_check(FIELD * fld, const void *data GCC_UNUSED)
5140{
5141    char *s = field_buffer(fld, 0);
5142    int n;
5143
5144    for (n = 0; s[n] != '\0'; ++n) {
5145	if (isspace(UChar(s[n]))) {
5146	    if (n < 6)
5147		return FALSE;
5148	}
5149    }
5150    return TRUE;
5151}
5152
5153static bool
5154pw_char_check(int ch, const void *data GCC_UNUSED)
5155{
5156    return (isgraph(ch) ? TRUE : FALSE);
5157}
5158
5159static void
5160demo_forms(void)
5161{
5162    WINDOW *w;
5163    FORM *form;
5164    FIELD *f[12], *secure;
5165    FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5166    FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5167    int finished = 0, c;
5168    unsigned n = 0;
5169
5170    move(18, 0);
5171    addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5172    addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5173    addstr("Home -- go to first field      End -- go to last field\n");
5174    addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5175    addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5176    addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5177    addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5178    addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5179    addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5180    addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5181    addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5182
5183    mvaddstr(4, 57, "Forms Entry Test");
5184
5185    refresh();
5186
5187    /* describe the form */
5188    f[n++] = make_label(0, 15, "Sample Form");
5189
5190    f[n++] = make_label(2, 0, "Last Name");
5191    f[n++] = make_field(3, 0, 1, 18, FALSE);
5192    set_field_type(f[n - 1], TYPE_ALPHA, 1);
5193
5194    f[n++] = make_label(2, 20, "First Name");
5195    f[n++] = make_field(3, 20, 1, 12, FALSE);
5196    set_field_type(f[n - 1], TYPE_ALPHA, 1);
5197
5198    f[n++] = make_label(2, 34, "Middle Name");
5199    f[n++] = make_field(3, 34, 1, 12, FALSE);
5200    set_field_type(f[n - 1], fty_middle);
5201
5202    f[n++] = make_label(5, 0, "Comments");
5203    f[n++] = make_field(6, 0, 4, 46, FALSE);
5204
5205    f[n++] = make_label(5, 20, "Password:");
5206    secure =
5207	f[n++] = make_field(5, 30, 1, 9, TRUE);
5208    set_field_type(f[n - 1], fty_passwd);
5209    f[n++] = (FIELD *) 0;
5210
5211    form = new_form(f);
5212
5213    display_form(form);
5214
5215    w = form_win(form);
5216    raw();
5217    nonl();			/* lets us read ^M's */
5218    while (!finished) {
5219	switch (form_driver(form, c = form_virtualize(form, w))) {
5220	case E_OK:
5221	    mvaddstr(5, 57, field_buffer(secure, 1));
5222	    clrtoeol();
5223	    refresh();
5224	    break;
5225	case E_UNKNOWN_COMMAND:
5226	    finished = my_form_driver(form, c);
5227	    break;
5228	default:
5229	    beep();
5230	    break;
5231	}
5232    }
5233
5234    erase_form(form);
5235
5236    free_form(form);
5237    for (c = 0; f[c] != 0; c++)
5238	free_field(f[c]);
5239    free_fieldtype(fty_middle);
5240    free_fieldtype(fty_passwd);
5241    noraw();
5242    nl();
5243}
5244#endif /* USE_LIBFORM */
5245
5246/****************************************************************************
5247 *
5248 * Overlap test
5249 *
5250 ****************************************************************************/
5251
5252static void
5253fillwin(WINDOW *win, char ch)
5254{
5255    int y, x;
5256    int y1, x1;
5257
5258    getmaxyx(win, y1, x1);
5259    for (y = 0; y < y1; y++) {
5260	wmove(win, y, 0);
5261	for (x = 0; x < x1; x++)
5262	    waddch(win, UChar(ch));
5263    }
5264}
5265
5266static void
5267crosswin(WINDOW *win, char ch)
5268{
5269    int y, x;
5270    int y1, x1;
5271
5272    getmaxyx(win, y1, x1);
5273    for (y = 0; y < y1; y++) {
5274	for (x = 0; x < x1; x++)
5275	    if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5276		|| (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5277		wmove(win, y, x);
5278		waddch(win, UChar(ch));
5279	    }
5280    }
5281}
5282
5283static void
5284overlap_test(void)
5285/* test effects of overlapping windows */
5286{
5287    int ch;
5288
5289    WINDOW *win1 = newwin(9, 20, 3, 3);
5290    WINDOW *win2 = newwin(9, 20, 9, 16);
5291
5292    raw();
5293    refresh();
5294    move(0, 0);
5295    printw("This test shows the behavior of wnoutrefresh() with respect to\n");
5296    printw("the shared region of two overlapping windows A and B.  The cross\n");
5297    printw("pattern in each window does not overlap the other.\n");
5298
5299    move(18, 0);
5300    printw("a = refresh A, then B, then doupdate. b = refresh B, then A, then doupdaute\n");
5301    printw("c = fill window A with letter A.      d = fill window B with letter B.\n");
5302    printw("e = cross pattern in window A.        f = cross pattern in window B.\n");
5303    printw("g = clear window A.                   h = clear window B.\n");
5304    printw("i = overwrite A onto B.               j = overwrite B onto A.\n");
5305    printw("^Q/ESC = terminate test.");
5306
5307    while ((ch = Getchar()) != QUIT && ch != ESCAPE)
5308	switch (ch) {
5309	case 'a':		/* refresh window A first, then B */
5310	    wnoutrefresh(win1);
5311	    wnoutrefresh(win2);
5312	    doupdate();
5313	    break;
5314
5315	case 'b':		/* refresh window B first, then A */
5316	    wnoutrefresh(win2);
5317	    wnoutrefresh(win1);
5318	    doupdate();
5319	    break;
5320
5321	case 'c':		/* fill window A so it's visible */
5322	    fillwin(win1, 'A');
5323	    break;
5324
5325	case 'd':		/* fill window B so it's visible */
5326	    fillwin(win2, 'B');
5327	    break;
5328
5329	case 'e':		/* cross test pattern in window A */
5330	    crosswin(win1, 'A');
5331	    break;
5332
5333	case 'f':		/* cross test pattern in window A */
5334	    crosswin(win2, 'B');
5335	    break;
5336
5337	case 'g':		/* clear window A */
5338	    wclear(win1);
5339	    wmove(win1, 0, 0);
5340	    break;
5341
5342	case 'h':		/* clear window B */
5343	    wclear(win2);
5344	    wmove(win2, 0, 0);
5345	    break;
5346
5347	case 'i':		/* overwrite A onto B */
5348	    overwrite(win1, win2);
5349	    break;
5350
5351	case 'j':		/* overwrite B onto A */
5352	    overwrite(win2, win1);
5353	    break;
5354	}
5355
5356    delwin(win2);
5357    delwin(win1);
5358    erase();
5359    endwin();
5360}
5361
5362/****************************************************************************
5363 *
5364 * Main sequence
5365 *
5366 ****************************************************************************/
5367
5368static bool
5369do_single_test(const char c)
5370/* perform a single specified test */
5371{
5372    switch (c) {
5373    case 'a':
5374	getch_test();
5375	break;
5376
5377#if USE_WIDEC_SUPPORT
5378    case 'A':
5379	get_wch_test();
5380	break;
5381#endif
5382
5383    case 'b':
5384	attr_test();
5385	break;
5386
5387#if USE_WIDEC_SUPPORT
5388    case 'B':
5389	wide_attr_test();
5390	break;
5391#endif
5392
5393    case 'c':
5394	if (!has_colors())
5395	    Cannot("does not support color.");
5396	else
5397	    color_test();
5398	break;
5399
5400#if USE_WIDEC_SUPPORT
5401    case 'C':
5402	if (!has_colors())
5403	    Cannot("does not support color.");
5404	else
5405	    wide_color_test();
5406	break;
5407#endif
5408
5409    case 'd':
5410	if (!has_colors())
5411	    Cannot("does not support color.");
5412	else if (!can_change_color())
5413	    Cannot("has hardwired color values.");
5414	else
5415	    color_edit();
5416	break;
5417
5418    case 'e':
5419	slk_test();
5420	break;
5421
5422#if USE_WIDEC_SUPPORT
5423    case 'E':
5424	wide_slk_test();
5425	break;
5426#endif
5427    case 'f':
5428	acs_display();
5429	break;
5430
5431#if USE_WIDEC_SUPPORT
5432    case 'F':
5433	wide_acs_display();
5434	break;
5435#endif
5436
5437#if USE_LIBPANEL
5438    case 'o':
5439	demo_panels();
5440	break;
5441#endif
5442
5443    case 'g':
5444	acs_and_scroll();
5445	break;
5446
5447    case 'i':
5448	flushinp_test(stdscr);
5449	break;
5450
5451    case 'k':
5452	test_sgr_attributes();
5453	break;
5454
5455#if USE_LIBMENU
5456    case 'm':
5457	menu_test();
5458	break;
5459#endif
5460
5461#if USE_LIBPANEL
5462    case 'p':
5463	demo_pad();
5464	break;
5465#endif
5466
5467#if USE_LIBFORM
5468    case 'r':
5469	demo_forms();
5470	break;
5471#endif
5472
5473    case 's':
5474	overlap_test();
5475	break;
5476
5477#if USE_LIBMENU && defined(TRACE)
5478    case 't':
5479	trace_set();
5480	break;
5481#endif
5482
5483    case '?':
5484	break;
5485
5486    default:
5487	return FALSE;
5488    }
5489
5490    return TRUE;
5491}
5492
5493static void
5494usage(void)
5495{
5496    static const char *const tbl[] =
5497    {
5498	"Usage: ncurses [options]"
5499	,""
5500	,"Options:"
5501#ifdef NCURSES_VERSION
5502	,"  -a f,b   set default-colors (assumed white-on-black)"
5503	,"  -d       use default-colors if terminal supports them"
5504#endif
5505	,"  -e fmt   specify format for soft-keys test (e)"
5506	,"  -f       rip-off footer line (can repeat)"
5507	,"  -h       rip-off header line (can repeat)"
5508	,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
5509#if USE_LIBPANEL
5510	,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
5511#endif
5512#ifdef TRACE
5513	,"  -t mask  specify default trace-level (may toggle with ^T)"
5514#endif
5515    };
5516    size_t n;
5517    for (n = 0; n < SIZEOF(tbl); n++)
5518	fprintf(stderr, "%s\n", tbl[n]);
5519    ExitProgram(EXIT_FAILURE);
5520}
5521
5522static void
5523set_terminal_modes(void)
5524{
5525    noraw();
5526    cbreak();
5527    noecho();
5528    scrollok(stdscr, TRUE);
5529    idlok(stdscr, TRUE);
5530    keypad(stdscr, TRUE);
5531}
5532
5533#ifdef SIGUSR1
5534static RETSIGTYPE
5535announce_sig(int sig)
5536{
5537    (void) fprintf(stderr, "Handled signal %d\r\n", sig);
5538}
5539#endif
5540
5541static int
5542rip_footer(WINDOW *win, int cols)
5543{
5544    wbkgd(win, A_REVERSE);
5545    werase(win);
5546    wmove(win, 0, 0);
5547    wprintw(win, "footer: %d columns", cols);
5548    wnoutrefresh(win);
5549    return OK;
5550}
5551
5552static int
5553rip_header(WINDOW *win, int cols)
5554{
5555    wbkgd(win, A_REVERSE);
5556    werase(win);
5557    wmove(win, 0, 0);
5558    wprintw(win, "header: %d columns", cols);
5559    wnoutrefresh(win);
5560    return OK;
5561}
5562
5563static void
5564main_menu(bool top)
5565{
5566    int command;
5567
5568    do {
5569	(void) puts("This is the ncurses main menu");
5570	(void) puts("a = keyboard and mouse input test");
5571#if USE_WIDEC_SUPPORT
5572	(void) puts("A = wide-character keyboard and mouse input test");
5573#endif
5574	(void) puts("b = character attribute test");
5575#if USE_WIDEC_SUPPORT
5576	(void) puts("B = wide-character attribute test");
5577#endif
5578	(void) puts("c = color test pattern");
5579#if USE_WIDEC_SUPPORT
5580	(void) puts("C = color test pattern using wide-character calls");
5581#endif
5582	if (top)
5583	    (void) puts("d = edit RGB color values");
5584	(void) puts("e = exercise soft keys");
5585#if USE_WIDEC_SUPPORT
5586	(void) puts("E = exercise soft keys using wide-characters");
5587#endif
5588	(void) puts("f = display ACS characters");
5589#if USE_WIDEC_SUPPORT
5590	(void) puts("F = display Wide-ACS characters");
5591#endif
5592	(void) puts("g = display windows and scrolling");
5593	(void) puts("i = test of flushinp()");
5594	(void) puts("k = display character attributes");
5595#if USE_LIBMENU
5596	(void) puts("m = menu code test");
5597#endif
5598#if USE_LIBPANEL
5599	(void) puts("o = exercise panels library");
5600	(void) puts("p = exercise pad features");
5601	(void) puts("q = quit");
5602#endif
5603#if USE_LIBFORM
5604	(void) puts("r = exercise forms code");
5605#endif
5606	(void) puts("s = overlapping-refresh test");
5607#if USE_LIBMENU && defined(TRACE)
5608	(void) puts("t = set trace level");
5609#endif
5610	(void) puts("? = repeat this command summary");
5611
5612	(void) fputs("> ", stdout);
5613	(void) fflush(stdout);	/* necessary under SVr4 curses */
5614
5615	/*
5616	 * This used to be an 'fgets()' call.  However (on Linux, at least)
5617	 * mixing stream I/O and 'read()' (used in the library) causes the
5618	 * input stream to be flushed when switching between the two.
5619	 */
5620	command = 0;
5621	for (;;) {
5622	    char ch;
5623	    if (read(fileno(stdin), &ch, 1) <= 0) {
5624		if (command == 0)
5625		    command = 'q';
5626		break;
5627	    } else if (command == 0 && !isspace(UChar(ch))) {
5628		command = ch;
5629	    } else if (ch == '\n' || ch == '\r') {
5630		if ((command == 'd') && !top) {
5631		    (void) fputs("Do not nest test-d\n", stdout);
5632		    command = 0;
5633		}
5634		if (command != 0)
5635		    break;
5636		(void) fputs("> ", stdout);
5637		(void) fflush(stdout);
5638	    }
5639	}
5640
5641	if (do_single_test(command)) {
5642	    /*
5643	     * This may be overkill; it's intended to reset everything back
5644	     * to the initial terminal modes so that tests don't get in
5645	     * each other's way.
5646	     */
5647	    flushinp();
5648	    set_terminal_modes();
5649	    reset_prog_mode();
5650	    clear();
5651	    refresh();
5652	    endwin();
5653	    if (command == '?') {
5654		(void) puts("This is the ncurses capability tester.");
5655		(void)
5656		    puts("You may select a test from the main menu by typing the");
5657		(void)
5658		    puts("key letter of the choice (the letter to left of the =)");
5659		(void)
5660		    puts("at the > prompt.  The commands `x' or `q' will exit.");
5661	    }
5662	    continue;
5663	}
5664    } while
5665	(command != 'q');
5666}
5667
5668/*+-------------------------------------------------------------------------
5669	main(argc,argv)
5670--------------------------------------------------------------------------*/
5671
5672#define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
5673#define okRGB(n)   ((n) >= 0 && (n) <= 1000)
5674
5675int
5676main(int argc, char *argv[])
5677{
5678    int c;
5679    int my_e_param = 1;
5680#ifdef NCURSES_VERSION
5681    int default_fg = COLOR_WHITE;
5682    int default_bg = COLOR_BLACK;
5683    bool assumed_colors = FALSE;
5684    bool default_colors = FALSE;
5685#endif
5686    char *palette_file = 0;
5687
5688    setlocale(LC_ALL, "");
5689
5690    while ((c = getopt(argc, argv, "a:de:fhp:s:t:")) != EOF) {
5691	switch (c) {
5692#ifdef NCURSES_VERSION
5693	case 'a':
5694	    assumed_colors = TRUE;
5695	    sscanf(optarg, "%d,%d", &default_fg, &default_bg);
5696	    break;
5697	case 'd':
5698	    default_colors = TRUE;
5699	    break;
5700#endif
5701	case 'e':
5702	    my_e_param = atoi(optarg);
5703#ifdef NCURSES_VERSION
5704	    if (my_e_param > 3)	/* allow extended layouts */
5705		usage();
5706#else
5707	    if (my_e_param > 1)
5708		usage();
5709#endif
5710	    break;
5711	case 'f':
5712	    ripoffline(-1, rip_footer);
5713	    break;
5714	case 'h':
5715	    ripoffline(1, rip_header);
5716	    break;
5717	case 'p':
5718	    palette_file = optarg;
5719	    break;
5720#if USE_LIBPANEL
5721	case 's':
5722	    nap_msec = atol(optarg);
5723	    break;
5724#endif
5725#ifdef TRACE
5726	case 't':
5727	    save_trace = strtol(optarg, 0, 0);
5728	    break;
5729#endif
5730	default:
5731	    usage();
5732	}
5733    }
5734
5735    /*
5736     * If there's no menus (unlikely for ncurses!), then we'll have to set
5737     * tracing on initially, just in case the user wants to test something that
5738     * doesn't involve wGetchar.
5739     */
5740#ifdef TRACE
5741    /* enable debugging */
5742#if !USE_LIBMENU
5743    trace(save_trace);
5744#else
5745    if (!isatty(fileno(stdin)))
5746	trace(save_trace);
5747#endif /* USE_LIBMENU */
5748#endif /* TRACE */
5749
5750    /* tell it we're going to play with soft keys */
5751    slk_init(my_e_param);
5752
5753#ifdef SIGUSR1
5754    /* set up null signal catcher so we can see what interrupts to getch do */
5755    signal(SIGUSR1, announce_sig);
5756#endif
5757
5758    /* we must initialize the curses data structure only once */
5759    initscr();
5760    bkgdset(BLANK);
5761
5762    /* tests, in general, will want these modes */
5763    if (has_colors()) {
5764	start_color();
5765#ifdef NCURSES_VERSION_PATCH
5766	max_colors = COLORS;	/* was > 16 ? 16 : COLORS */
5767#if HAVE_USE_DEFAULT_COLORS
5768	if (default_colors) {
5769	    use_default_colors();
5770	    min_colors = -1;
5771	}
5772#if NCURSES_VERSION_PATCH >= 20000708
5773	else if (assumed_colors)
5774	    assume_default_colors(default_fg, default_bg);
5775#endif
5776#endif
5777#else /* normal SVr4 curses */
5778	max_colors = COLORS;	/* was > 8 ? 8 : COLORS */
5779#endif
5780	max_pairs = COLOR_PAIRS;	/* was > 256 ? 256 : COLOR_PAIRS */
5781
5782	if (can_change_color()) {
5783	    all_colors = (RGB_DATA *) malloc(max_colors * sizeof(RGB_DATA));
5784	    for (c = 0; c < max_colors; ++c) {
5785		color_content(c,
5786			      &all_colors[c].red,
5787			      &all_colors[c].green,
5788			      &all_colors[c].blue);
5789	    }
5790	    if (palette_file != 0) {
5791		FILE *fp = fopen(palette_file, "r");
5792		if (fp != 0) {
5793		    char buffer[BUFSIZ];
5794		    int red, green, blue;
5795		    int scale = 1000;
5796		    while (fgets(buffer, sizeof(buffer), fp) != 0) {
5797			if (sscanf(buffer, "scale:%d", &c) == 1) {
5798			    scale = c;
5799			} else if (sscanf(buffer, "%d:%d %d %d",
5800					  &c,
5801					  &red,
5802					  &green,
5803					  &blue) == 4
5804				   && okCOLOR(c)
5805				   && okRGB(red)
5806				   && okRGB(green)
5807				   && okRGB(blue)) {
5808			    all_colors[c].red = (red * 1000) / scale;
5809			    all_colors[c].green = (green * 1000) / scale;
5810			    all_colors[c].blue = (blue * 1000) / scale;
5811			}
5812		    }
5813		    fclose(fp);
5814		}
5815	    }
5816	}
5817    }
5818    set_terminal_modes();
5819    def_prog_mode();
5820
5821    /*
5822     * Return to terminal mode, so we're guaranteed of being able to
5823     * select terminal commands even if the capabilities are wrong.
5824     */
5825    endwin();
5826
5827#if HAVE_CURSES_VERSION
5828    (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
5829#elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
5830    (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
5831		  NCURSES_VERSION_MAJOR,
5832		  NCURSES_VERSION_MINOR,
5833		  NCURSES_VERSION_PATCH);
5834#else
5835    (void) puts("Welcome to ncurses.  Press ? for help.");
5836#endif
5837
5838    main_menu(TRUE);
5839
5840    ExitProgram(EXIT_SUCCESS);
5841}
5842
5843/* ncurses.c ends here */
5844