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