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