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