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