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