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