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