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