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