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